Files
workspace/code/base-project/base-vue/packages/ao-design/src/modal/useModal.ts
T
2025-09-21 21:52:08 +08:00

159 lines
4.2 KiB
TypeScript

import type { AppContext, VNode } from 'vue'
import { createVNode, getCurrentInstance, nextTick, render } from 'vue'
import Modal from './index'
function omit<T extends Record<string, any>, K extends keyof T>(
object: T,
keys: K[],
): Omit<T, K> {
const result = { ...object }
keys.forEach(key => delete result[key])
return result
}
export interface ModalOptions {
layout?: string | VNode | (() => VNode)
title?: string | VNode | (() => VNode)
content?: string | VNode | (() => VNode)
footer?: string | VNode | (() => VNode)
position?: 'top' | 'center' | 'bottom'
confirmBtnProps?: Record<string, any>
cancelBtnProps?: Record<string, any>
width?: string
height?: string
fullscreen?: boolean
showClose?: boolean
showFullscreen?: boolean
draggable?: boolean
draggableSelector?: string
confirmBtn?: any
cancelBtn?: any
mask?: boolean
maskClosable?: boolean
onConfirm?: () => void
onCancel?: () => void
onClose?: () => void
}
function createModal(options: ModalOptions & { type?: string }, appContext?: AppContext) {
let container: HTMLDivElement | null = document.createElement('div')
document.body.appendChild(container)
let vm: VNode
const handleConfirm = () => {
if (vm.component)
vm.component.props.visible = false
options.onConfirm?.()
}
const handleCancel = () => {
if (vm.component)
vm.component.props.visible = false
options.onCancel?.()
}
const handleClose = async () => {
await nextTick()
if (container) {
render(null, container)
document.body.removeChild(container)
}
container = null
options.onClose?.()
}
const defaultConfig = {
visible: true,
onConfirm: handleConfirm,
onCancel: handleCancel,
onClose: handleClose,
type: options.type ?? 'default',
}
vm = createVNode(
Modal,
{
...defaultConfig,
...omit(options, ['layout', 'content', 'title', 'footer', 'onConfirm', 'onCancel', 'onClose']),
},
options.layout
? {
layout:
typeof options.layout === 'function'
? options.layout
: () => options.layout,
}
: {
default: options.content
? typeof options.content === 'function'
? options.content
: () => options.content
: undefined,
title: options.title
? typeof options.title === 'function'
? options.title
: () => options.title
: undefined,
footer: options.footer
? typeof options.footer === 'function'
? options.footer
: () => options.footer
: undefined,
},
)
// 自动获取 appContext,除非手动传入
const instanceAppContext = appContext ?? getCurrentInstance()?.appContext ?? undefined
if (instanceAppContext) {
vm.appContext = instanceAppContext
}
render(vm, container)
return {
close: () => {
if (vm.component)
vm.component.props.visible = false
},
update: (newOptions: Partial<ModalOptions>) => {
if (vm.component) {
Object.entries(newOptions).forEach(([key, val]) => {
vm.component!.props[key] = val
})
}
},
}
}
type ModalType = 'default' | 'info' | 'success' | 'warning' | 'danger' | 'question'
export function useModal() {
const open = (options: ModalOptions & { type?: ModalType }, appContext?: AppContext) =>
createModal({ ...options, type: options.type ?? 'default' }, appContext)
const info = (options: ModalOptions, appContext?: AppContext) =>
createModal({ ...options, type: 'info' }, appContext)
const success = (options: ModalOptions, appContext?: AppContext) =>
createModal({ ...options, type: 'success' }, appContext)
const warning = (options: ModalOptions, appContext?: AppContext) =>
createModal({ ...options, type: 'warning' }, appContext)
const danger = (options: ModalOptions, appContext?: AppContext) =>
createModal({ ...options, type: 'danger' }, appContext)
const question = (options: ModalOptions, appContext?: AppContext) =>
createModal({ ...options, type: 'question' }, appContext)
return {
open,
info,
success,
warning,
danger,
question,
}
}