import { defineComponent, h, onMounted, render, ref, type VNode } from 'vue'; import Modal from './Modal.vue'; import Button from './Button.vue'; import Icon from '@/components/Icon.vue'; export type ModalConfirmType = 'info' | 'success' | 'warning' | 'error' | 'confirm'; export interface ModalConfirmOptions { title?: string; content?: string | VNode; okText?: string; cancelText?: string; width?: string | number; maskClosable?: boolean; showCancel?: boolean; onOk?: () => void | Promise; onCancel?: () => void; } const typeConfig: Record = { info: { icon: 'icon-info-fill', color: '#1677ff' }, success: { icon: 'icon-round-check-fill', color: '#00c49f' }, warning: { icon: 'icon-warn-fill', color: '#ff8b2c' }, error: { icon: 'icon-round-close-fill', color: '#ff4d4f' }, confirm: { icon: 'icon-question-fill', color: '#ff8b2c' }, }; const ModalHolder = defineComponent({ name: 'ModalConfirmHolder', props: { type: { type: String, required: true }, options: { type: Object, required: true }, onDestroy: { type: Function, required: true }, }, setup(props) { const show = ref(false); const okLoading = ref(false); const opt = props.options as ModalConfirmOptions; const cfg = typeConfig[props.type as ModalConfirmType]; onMounted(() => { show.value = true; }); function destroy() { (props.onDestroy as () => void)(); } function handleClose() { show.value = false; } function handleUpdateShow(value: boolean) { show.value = value; if (!value) { setTimeout(destroy, 200); } } function handleOk() { const result = opt.onOk?.(); if (result && typeof (result as Promise).then === 'function') { okLoading.value = true; Promise.resolve(result) .then(handleClose) .catch(() => {}) .finally(() => { okLoading.value = false; }); } else { handleClose(); } } function handleCancel() { opt.onCancel?.(); handleClose(); } return () => h( Modal, { show: show.value, 'onUpdate:show': handleUpdateShow, title: opt.title, width: opt.width, maskClosable: opt.maskClosable, }, { default: () => h('div', { class: 'flex items-start gap-3' }, [ h(Icon, { name: cfg.icon, size: '1.5rem', color: cfg.color, class: 'flex-shrink-0 mt-0.5' }), opt.content ? h( 'div', { class: 'flex-1 text-sm whitespace-pre-wrap break-words' }, typeof opt.content === 'string' ? opt.content : opt.content, ) : null, ]), footer: () => h('div', { class: 'flex flex-row justify-end items-center gap-2' }, [ opt.showCancel ? h(Button, { onClick: handleCancel }, { default: () => opt.cancelText ?? '取消' }) : null, h( Button, { type: 'primary', loading: okLoading.value, onClick: handleOk }, { default: () => opt.okText ?? '确定' }, ), ]), }, ); }, }); function openConfirm(type: ModalConfirmType, options: ModalConfirmOptions = {}) { const container = document.createElement('div'); document.body.appendChild(container); const destroy = () => { render(null, container); container.remove(); }; const vm = h(ModalHolder, { type, options, onDestroy: destroy }); render(vm, container); } export const ModalApi = { info: (options?: ModalConfirmOptions) => openConfirm('info', { showCancel: false, ...options }), success: (options?: ModalConfirmOptions) => openConfirm('success', { showCancel: false, ...options }), warning: (options?: ModalConfirmOptions) => openConfirm('warning', { showCancel: false, ...options }), error: (options?: ModalConfirmOptions) => openConfirm('error', { showCancel: false, ...options }), confirm: (options?: ModalConfirmOptions) => openConfirm('confirm', { showCancel: true, ...options }), };