123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- import { defineComponent, markRaw, ref, computed } from 'vue'
- import { ElDialog, ElButton } from 'element-plus'
- import { getUsingConfig } from '@cip/utils/util'
- import { useCipConfig } from '@cip/components/hooks/use-cip-config'
- import './dialog.less'
- export default defineComponent({
- name: 'CipDialog',
- props: {
- title: String,
- modelValue: { default: false }, // 是否显示dialog
- onConfirm: { type: Function }, // Function(resolve,reject)
- beforeConfirm: { type: Function, default: () => {} }, // 触发表单验证前的值设置
- width: { // 设置dialog宽度
- default: '870px'
- },
- top: { default: '15vh' },
- closeOnClickModal: { // 点击遮罩层是否关闭dialog
- type: Boolean,
- default: false
- },
- showOnly: {
- default: false
- },
- buttonSize: { // 设置footer按钮大小
- default: 'small'
- },
- confirmText: { // 触发on-confirm方法的按钮文字
- default: '确认'
- },
- cancelText: { // 触发关闭dialog方法按钮的文字
- default: '取消'
- },
- showCancel: {
- type: Boolean,
- default: true
- },
- destroyOnClose: { // 默认关闭时销毁dialog
- type: Boolean,
- default: true
- },
- closeOnPressEscape: {},
- maxDepth: { // 查找form的深度
- type: Number,
- default: 5
- },
- fullscreen: Boolean
- },
- emits: ['update:modelValue', 'cancel', 'close'],
- setup (props, { slots, emit }) {
- const cipConfig = useCipConfig()
- // esc弹窗
- const closeOnPressEscape = computed(() => {
- return getUsingConfig(props.closeOnPressEscape, cipConfig.dialog?.closeOnPressEscape)
- })
- const defaultSlot = ref([])
- const isComponent = vnode => !!vnode.component
- const isFormComponent = (vnode) => {
- if (!isComponent(vnode)) return false
- const componentName = (vnode.type?.name ?? '').toLocaleLowerCase()
- return componentName.indexOf('form') > -1
- }
- const getComponent = vnode => vnode?.component
- const getMethodByComponent = (component = {}, method) => component.ctx?.[method] ?? component.exposed?.[method]
- const clearValidate = (vnodeList = []) => {
- // eslint-disable-next-line no-unused-expressions
- const handler = (component) => {
- const clearValidate = getMethodByComponent(component, 'clearValidate')
- // eslint-disable-next-line no-unused-expressions
- clearValidate?.()
- }
- findFormAndHandler(vnodeList, handler).then(() => {})
- }
- const validForms = async (vnodeList = []) => {
- const validList = []
- const handler = async (component) => {
- try {
- const validate = getMethodByComponent(component, 'validate')
- await validate?.()
- validList.push(true)
- } catch (e) {
- validList.push(e)
- }
- }
- try {
- await findFormAndHandler(vnodeList, handler)
- } catch (e) {
- console.log('findFormAndHandler', e)
- }
- return validList
- }
- const findFormAndHandler = async (vnodeList = [], handler = () => {}, depth = 1) => {
- for (let i = 0; i < vnodeList.length; i++) {
- const vnode = vnodeList[i]
- if (isFormComponent(vnode)) {
- const component = getComponent(vnode)
- await handler(component)
- } else {
- if (depth > props.maxDepth) return false
- // 使用defineAsyncComponent 是 subTree 及为下个组件
- const childVnodeList = isComponent(vnode)
- ? (vnode.component.subTree?.children ?? [vnode.component.subTree])
- : vnode.children
- // 如果是文字节点则跳过不处理
- if (typeof childVnodeList === 'string') {
- continue
- }
- if (childVnodeList?.length > 0) {
- await findFormAndHandler(childVnodeList, handler, ++depth) // 此处++必须在前
- }
- }
- }
- }
- const waiting = ref(false)
- // 默认使用props.onConfirm当入参为函数时使用函数
- const confirm = async (cb) => {
- // 防止cb为e导致的错误
- if (typeof cb !== 'function') cb = props.onConfirm
- try {
- waiting.value = true
- const validList = await validForms(defaultSlot.value)
- if (!validList.some(valid => valid !== true)) {
- const res = await new Promise((resolve, reject) => {
- if (typeof cb === 'function') {
- cb(resolve, reject)
- } else {
- reject(new TypeError('onConfirm is not a function'))
- }
- })
- updateVisible(false)
- return res ?? true
- } else {
- // 存在未通过验证的表单
- throw new Error('未通过表单验证')
- }
- // eslint-disable-next-line no-useless-catch
- } catch (e) {
- // 跑出错误
- throw e
- } finally {
- waiting.value = false
- }
- }
- const cancel = () => {
- updateVisible(false)
- emit('cancel')
- }
- const openHandler = () => {
- clearValidate(defaultSlot.value)
- }
- const closeHandler = () => {
- emit('close')
- }
- const updateVisible = (val) => {
- emit('update:modelValue', val)
- }
- const dialogSlots = {
- title: () => <>
- <div className="el-dialog__mainTitle">{props.title || slots.mainTitle?.() }</div>
- <div class="el-dialog__subTitle">{slots.subTitle?.()}</div>
- </>,
- default: () => {
- const slot = slots.default?.()
- if (slot) {
- defaultSlot.value = markRaw(slot)
- }
- return slot
- },
- footer: () => {
- if (!props.showOnly) {
- const defaultFooter = <div>
- {
- !waiting.value && props.showCancel &&
- <ElButton
- onClick={() => cancel()}
- size={props.buttonSize}>
- {props.cancelText}
- </ElButton>
- }
- <ElButton
- onClick={() => confirm()}
- size={props.buttonSize}
- type={'primary'}
- loading={waiting.value} >
- {props.confirmText}
- </ElButton>
- </div>
- const footerSlot = slots.footer?.({ confirm, loading: waiting.value, cancel })
- return slots.footer ? footerSlot : defaultFooter
- } else {
- return slots.footer?.()
- }
- }
- }
- return () => (
- <ElDialog
- customClass={'cip-dialog__wrapper'}
- modelValue={props.modelValue}
- onUpdate:modelValue={updateVisible}
- title={props.title}
- width={props.width}
- closeOnClickModal={props.closeOnClickModal}
- top={props.top}
- destroyOnClose={props.destroyOnClose}
- appendToBody={true}
- close-on-press-escape={closeOnPressEscape.value}
- onClose={() => closeHandler()}
- onOpen={() => openHandler()}
- fullscreen={props.fullscreen}
- v-slots={dialogSlots}/>
- )
- }
- })
|