import type { AppContext, VNode } from 'vue' import { createVNode, getCurrentInstance, nextTick, render } from 'vue' import Modal from './index' function omit, K extends keyof T>( object: T, keys: K[], ): Omit { 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 cancelBtnProps?: Record 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) => { 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, } }