import type { PropType } from 'vue' import { defineComponent, onMounted, ref, Teleport } from 'vue' import { getPrefixCls } from '../_utils/getPrefixCls' import Button from '../button/index' import Icon from '../icon/index' type DrawerSize = 'small' | 'medium' | 'large' type SizeType = number | string | DrawerSize export default defineComponent({ name: 'Drawer', props: { visible: { type: Boolean, default: false, }, placement: { type: String as PropType<'top' | 'bottom' | 'left' | 'right'>, default: 'right', }, size: { type: [Number, String] as PropType, default: 'medium', }, title: { type: String, default: '', }, showClose: { type: Boolean, default: false, }, confirmBtn: { type: null, default: () => ({ label: '确认', type: 'solid', status: 'primary' }), }, cancelBtn: { type: null, default: () => ({ label: '取消', type: 'solid', status: 'default' }), }, beforeConfirm: { type: Function as PropType< (done: (ok: boolean) => void) => void | boolean | Promise >, }, maskClose: { type: Boolean, default: true, }, appendTo: { type: [String, Object] as PropType, default: 'body', }, }, emits: ['update:visible', 'confirm', 'cancel'], setup(props, { slots, emit }) { const prefixCls = getPrefixCls('drawer') const confirmLoading = ref(false) const close = () => { emit('update:visible', false) confirmLoading.value = false } const handleMaskClick = () => { if (props.maskClose) { close() } } const resolveSize = (value: SizeType, defaultPx = 300) => { if (typeof value === 'number') return `${value}px` if (typeof value === 'string') { const presetMap: Record = { small: '300px', medium: '500px', large: '700px', } if (presetMap[value as DrawerSize]) return presetMap[value as DrawerSize] return value } return `${defaultPx}px` } const handleConfirm = async () => { let allow = true if (props.beforeConfirm) { confirmLoading.value = true try { allow = await new Promise((resolve) => { const result = props.beforeConfirm!(ok => resolve(ok)) if (typeof result === 'boolean') { resolve(result) } else if (result instanceof Promise) { result.then(ok => resolve(ok)).catch(() => resolve(false)) } // 如果是用 done 回调,就等调用方触发 resolve }) } catch { allow = false } } if (allow) { close() emit('confirm') } confirmLoading.value = false } const handleCancel = () => { close() emit('cancel') } // 挂载目标位置 const targetEl = ref(document.body) onMounted(() => { if (props.appendTo instanceof HTMLElement) { targetEl.value = props.appendTo } else if (typeof props.appendTo === 'string') { const el = document.querySelector(props.appendTo) if (el) targetEl.value = el as HTMLElement } }) // 判断是否是 body const isBody = props.appendTo === 'body' return () => (
{slots.header ? slots.header() : ( <> {props.title} {props.showClose && ( emit('update:visible', false)} /> )} )}
{slots.default ? slots.default() : null}
{slots.footer ? slots.footer() : ( <> )}
) }, })