This commit is contained in:
zhouwentao 2025-12-21 16:52:31 +08:00
parent b046ea95d0
commit 3b457cf1c2
6 changed files with 46 additions and 22 deletions

View File

@ -4,8 +4,9 @@
### `src/utils/message.ts`
- **Purpose**: Provides a global interface for showing toast messages.
- **Methods**: `success(msg, duration)`, `error(msg, duration)`, `warning(msg, duration)`, `info(msg, duration)`.
- **Methods**: `success(msg, duration, position)`, `error(msg, duration, position)`, `warning(msg, duration, position)`, `info(msg, duration, position)`.
- **Implementation**: Programmatically mounts `WMessage.vue`.
- **New Feature**: Supports `position` argument: `'top-center' | 'top-left' | 'top-right' | 'bottom-center' | 'bottom-left' | 'bottom-right'`.
### `src/service/request/index.ts`
- **Purpose**: Encapsulates Axios for API requests.

View File

@ -34,3 +34,10 @@
- **Goal**: Ensure score data is fetched when ScoreForm mounts if store is empty.
- **Scope**:
- `src/components/ScoreForm.vue` (Update onMounted)
### [Task 3] Enhance WMessage Component
- **Time**: 2025-12-18
- **Goal**: Add position configuration support (top/bottom/left/right/center).
- **Scope**:
- `src/utils/message.ts` (Update logic to support multiple containers)
- `src/components/ui/WMessage.vue` (No changes needed, styles handled by container)

View File

@ -22,3 +22,6 @@
- [x] Create `src/stores/score.ts` <!-- id: 18 -->
- [x] Integrate Get Score in `TheNavigation.vue` <!-- id: 19 -->
- [x] Integrate Save Score in `ScoreForm.vue` <!-- id: 20 -->
- [x] [Task 3] Enhance WMessage Component <!-- id: 21 -->
- [x] Add position support to `src/utils/message.ts` (top/bottom/left/right/center) <!-- id: 22 -->

View File

@ -53,7 +53,7 @@ function close() {
visible.value = false
// Give time for transition to finish before removing
setTimeout(() => {
// props.onClose(props.id)
props.onClose(props.id)
}, 300)
}

View File

@ -118,8 +118,8 @@ class Request {
return this.instance.request(config)
}
get<T = any>(url: string, config?: CustomRequestConfig): Promise<T> {
return this.instance.get(url, config)
get<T = any>(url: string, params?: any, config?: CustomRequestConfig): Promise<T> {
return this.instance.get(url, { ...config, params })
}
post<T = any>(url: string, data?: any, config?: CustomRequestConfig): Promise<T> {

View File

@ -5,22 +5,42 @@ let seed = 1
const instances: any[] = []
type MessageType = 'success' | 'error' | 'warning' | 'info'
export type MessagePosition = 'top-center' | 'top-left' | 'top-right' | 'bottom-center' | 'bottom-left' | 'bottom-right'
interface MessageOptions {
message: string
type?: MessageType
duration?: number
position?: MessagePosition
}
const positionClasses: Record<MessagePosition, string> = {
'top-center': 'top-4 left-1/2 transform -translate-x-1/2 flex-col items-center',
'top-left': 'top-4 left-4 flex-col items-start',
'top-right': 'top-4 right-4 flex-col items-end',
'bottom-center': 'bottom-4 left-1/2 transform -translate-x-1/2 flex-col-reverse items-center',
'bottom-left': 'bottom-4 left-4 flex-col-reverse items-start',
'bottom-right': 'bottom-4 right-4 flex-col-reverse items-end',
}
const Message = (options: MessageOptions) => {
const { position = 'top-center' } = options
const id = `message_${seed++}`
const container = document.createElement('div')
// Create a container for the message if it doesn't exist
let messageContainer = document.querySelector('.w-message-container')
// We need separate containers for each position
const containerClass = `w-message-container-${position}`
let messageContainer = document.querySelector(`.${containerClass}`)
if (!messageContainer) {
messageContainer = document.createElement('div')
messageContainer.className = 'w-message-container fixed top-4 left-1/2 transform -translate-x-1/2 z-50 flex flex-col items-center pointer-events-none'
// Common classes
let classes = `w-message-container ${containerClass} fixed z-50 flex pointer-events-none transition-all duration-300`
// Position specific classes
classes += ` ${positionClasses[position]}`
messageContainer.className = classes
document.body.appendChild(messageContainer)
}
@ -28,37 +48,30 @@ const Message = (options: MessageOptions) => {
...options,
id,
onClose: () => {
close(id, container)
close(id, container, position)
},
}
const vnode = createVNode(WMessage, props)
// Render the component into the container
// We append the container to the messageContainer
messageContainer.appendChild(container)
render(vnode, container)
instances.push({ id, vnode, container })
instances.push({ id, vnode, container, position })
}
function close(id: string, container: HTMLElement) {
function close(id: string, container: HTMLElement, position: string) {
const idx = instances.findIndex(vm => vm.id === id)
if (idx === -1) return
const { vnode } = instances[idx]
// Manually call close on component if needed, but here we just unmount
// The component handles its own visibility transition
// We wait for the transition to finish? The component calls onClose after transition.
// So we just remove it from DOM.
render(null, container)
container.remove()
instances.splice(idx, 1)
// Clean up container if empty
const messageContainer = document.querySelector('.w-message-container')
const containerClass = `w-message-container-${position}`
const messageContainer = document.querySelector(`.${containerClass}`)
if (messageContainer && messageContainer.childNodes.length === 0) {
messageContainer.remove()
}
@ -66,10 +79,10 @@ function close(id: string, container: HTMLElement) {
// Helpers
export const message = {
success: (msg: string, duration?: number) => Message({ message: msg, type: 'success', duration }),
error: (msg: string, duration?: number) => Message({ message: msg, type: 'error', duration }),
warning: (msg: string, duration?: number) => Message({ message: msg, type: 'warning', duration }),
info: (msg: string, duration?: number) => Message({ message: msg, type: 'info', duration }),
success: (msg: string, duration?: number, position?: MessagePosition) => Message({ message: msg, type: 'success', duration, position }),
error: (msg: string, duration?: number, position?: MessagePosition) => Message({ message: msg, type: 'error', duration, position }),
warning: (msg: string, duration?: number, position?: MessagePosition) => Message({ message: msg, type: 'warning', duration, position }),
info: (msg: string, duration?: number, position?: MessagePosition) => Message({ message: msg, type: 'info', duration, position }),
}
export default message