You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.7 KiB
207 lines
5.7 KiB
<template> |
|
<Modal v-bind="getBindValue" @cancel="handleCancel"> |
|
<template #closeIcon v-if="!$slots.closeIcon"> |
|
<ModalClose |
|
:canFullscreen="getProps.canFullscreen" |
|
:fullScreen="fullScreenRef" |
|
@cancel="handleCancel" |
|
@fullscreen="handleFullScreen" |
|
/> |
|
</template> |
|
|
|
<template #title v-if="!$slots.title"> |
|
<ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" @dblclick="handleTitleDbClick" /> |
|
</template> |
|
|
|
<template #footer v-if="!$slots.footer"> |
|
<ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel"> |
|
<template #[item]="data" v-for="item in Object.keys($slots)"> |
|
<slot :name="item" v-bind="data || {}"></slot> |
|
</template> |
|
</ModalFooter> |
|
</template> |
|
|
|
<ModalWrapper |
|
:useWrapper="getProps.useWrapper" |
|
:footerOffset="wrapperFooterOffset" |
|
:fullScreen="fullScreenRef" |
|
ref="modalWrapperRef" |
|
:loading="getProps.loading" |
|
:loading-tip="getProps.loadingTip" |
|
:minHeight="getProps.minHeight" |
|
:height="getWrapperHeight" |
|
:visible="visibleRef" |
|
:modalFooterHeight="footer !== undefined && !footer ? 0 : undefined" |
|
v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')" |
|
@ext-height="handleExtHeight" |
|
@height-change="handleHeightChange" |
|
> |
|
<slot></slot> |
|
</ModalWrapper> |
|
|
|
<template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))"> |
|
<slot :name="item" v-bind="data || {}"></slot> |
|
</template> |
|
</Modal> |
|
</template> |
|
<script lang="ts" setup> |
|
import type { ModalProps, ModalMethods } from './typing' |
|
|
|
import { computed, ref, watch, unref, watchEffect, toRef, getCurrentInstance, nextTick, useAttrs } from 'vue' |
|
import Modal from './components/Modal' |
|
import ModalWrapper from './components/ModalWrapper.vue' |
|
import ModalClose from './components/ModalClose.vue' |
|
import ModalFooter from './components/ModalFooter.vue' |
|
import ModalHeader from './components/ModalHeader.vue' |
|
import { isFunction } from '@/utils/is' |
|
import { deepMerge } from '@/utils' |
|
import { basicProps } from './props' |
|
import { useFullScreen } from './hooks/useModalFullScreen' |
|
import { omit } from 'lodash-es' |
|
import { useDesign } from '@/hooks/web/useDesign' |
|
|
|
defineOptions({ name: 'BasicModal', inheritAttrs: false }) |
|
|
|
const props = defineProps(basicProps) |
|
const attrs = useAttrs() |
|
const emit = defineEmits(['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible']) |
|
const visibleRef = ref(false) |
|
const propsRef = ref<Partial<ModalProps> | null>(null) |
|
const modalWrapperRef = ref<any>(null) |
|
const { prefixCls } = useDesign('basic-modal') |
|
|
|
// modal Bottom and top height |
|
const extHeightRef = ref(0) |
|
const modalMethods: ModalMethods = { |
|
setModalProps, |
|
emitVisible: undefined, |
|
redoModalHeight: () => { |
|
nextTick(() => { |
|
if (unref(modalWrapperRef)) { |
|
;(unref(modalWrapperRef) as any).setModalHeight() |
|
} |
|
}) |
|
} |
|
} |
|
|
|
const instance = getCurrentInstance() |
|
if (instance) { |
|
emit('register', modalMethods, instance.uid) |
|
} |
|
|
|
// Custom title component: get title |
|
const getMergeProps = computed((): Recordable => { |
|
return { |
|
...props, |
|
...(unref(propsRef) as any) |
|
} |
|
}) |
|
|
|
const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({ |
|
modalWrapperRef, |
|
extHeightRef, |
|
wrapClassName: toRef(getMergeProps.value, 'wrapClassName') |
|
}) |
|
|
|
// modal component does not need title and origin buttons |
|
const getProps = computed((): Recordable => { |
|
const opt = { |
|
...unref(getMergeProps), |
|
visible: unref(visibleRef), |
|
okButtonProps: undefined, |
|
cancelButtonProps: undefined, |
|
title: undefined |
|
} |
|
return { |
|
...opt, |
|
wrapClassName: unref(getWrapClassName) |
|
} |
|
}) |
|
|
|
const getBindValue = computed((): Recordable => { |
|
const attr = { |
|
...attrs, |
|
...unref(getMergeProps), |
|
visible: unref(visibleRef) |
|
} |
|
attr['wrapClassName'] = `${attr?.['wrapClassName'] || ''} ${unref(getWrapClassName)}` |
|
if (unref(fullScreenRef)) { |
|
return omit(attr, ['height', 'title']) |
|
} |
|
return omit(attr, 'title') |
|
}) |
|
|
|
const getWrapperHeight = computed(() => { |
|
if (unref(fullScreenRef)) return undefined |
|
return unref(getProps).height |
|
}) |
|
|
|
watchEffect(() => { |
|
visibleRef.value = !!props.visible |
|
fullScreenRef.value = !!props.defaultFullscreen |
|
}) |
|
|
|
watch( |
|
() => unref(visibleRef), |
|
(v) => { |
|
emit('visible-change', v) |
|
emit('update:visible', v) |
|
instance && modalMethods.emitVisible?.(v, instance.uid) |
|
nextTick(() => { |
|
if (props.scrollTop && v && unref(modalWrapperRef)) { |
|
;(unref(modalWrapperRef) as any).scrollTop() |
|
} |
|
}) |
|
}, |
|
{ |
|
immediate: false |
|
} |
|
) |
|
|
|
// 取消事件 |
|
async function handleCancel(e: Event) { |
|
e?.stopPropagation() |
|
// 过滤自定义关闭按钮的空白区域 |
|
if ((e.target as HTMLElement)?.classList?.contains(prefixCls + '-close--custom')) return |
|
if (props.closeFunc && isFunction(props.closeFunc)) { |
|
const isClose: boolean = await props.closeFunc() |
|
visibleRef.value = !isClose |
|
return |
|
} |
|
|
|
visibleRef.value = false |
|
emit('cancel', e) |
|
} |
|
|
|
/** |
|
* @description: 设置modal参数 |
|
*/ |
|
function setModalProps(props: Partial<ModalProps>): void { |
|
// Keep the last setModalProps |
|
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props) |
|
if (Reflect.has(props, 'visible')) { |
|
visibleRef.value = !!props.visible |
|
} |
|
if (Reflect.has(props, 'defaultFullscreen')) { |
|
fullScreenRef.value = !!props.defaultFullscreen |
|
} |
|
} |
|
|
|
function handleOk(e: Event) { |
|
emit('ok', e) |
|
} |
|
|
|
function handleHeightChange(height: string) { |
|
emit('height-change', height) |
|
} |
|
|
|
function handleExtHeight(height: number) { |
|
extHeightRef.value = height |
|
} |
|
|
|
function handleTitleDbClick(e) { |
|
if (!props.canFullscreen) return |
|
e.stopPropagation() |
|
handleFullScreen(e) |
|
} |
|
</script>
|
|
|