| | |
| | | :loading-tip="getProps.loadingTip" |
| | | :minHeight="getProps.minHeight" |
| | | :height="getWrapperHeight" |
| | | :visible="visibleRef" |
| | | :open="openRef" |
| | | :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined" |
| | | v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')" |
| | | v-bind="omit(getProps.wrapperProps, 'open', 'height', 'modalFooterHeight')" |
| | | @ext-height="handleExtHeight" |
| | | @height-change="handleHeightChange" |
| | | > |
| | |
| | | </template> |
| | | </Modal> |
| | | </template> |
| | | <script lang="ts"> |
| | | <script lang="ts" setup> |
| | | import type { ModalProps, ModalMethods } from './typing'; |
| | | |
| | | import { |
| | | defineComponent, |
| | | computed, |
| | | ref, |
| | | watch, |
| | |
| | | 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 { 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'; |
| | | import { useDesign } from '@/hooks/web/useDesign'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'BasicModal', |
| | | components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader }, |
| | | inheritAttrs: false, |
| | | props: basicProps, |
| | | emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'], |
| | | setup(props, { emit, attrs }) { |
| | | const visibleRef = ref(false); |
| | | const propsRef = ref<Partial<ModalProps> | null>(null); |
| | | const modalWrapperRef = ref<any>(null); |
| | | const { prefixCls } = useDesign('basic-modal'); |
| | | defineOptions({ name: 'BasicModal', inheritAttrs: false }); |
| | | |
| | | // 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 props = defineProps(basicProps); |
| | | |
| | | const instance = getCurrentInstance(); |
| | | if (instance) { |
| | | emit('register', modalMethods, instance.uid); |
| | | } |
| | | const emit = defineEmits([ |
| | | 'open-change', |
| | | 'height-change', |
| | | 'cancel', |
| | | 'ok', |
| | | 'register', |
| | | 'update:open', |
| | | 'fullscreen', |
| | | ]); |
| | | |
| | | // Custom title component: get title |
| | | const getMergeProps = computed((): Recordable => { |
| | | return { |
| | | ...props, |
| | | ...(unref(propsRef) as any), |
| | | }; |
| | | }); |
| | | const attrs = useAttrs(); |
| | | const openRef = ref(false); |
| | | const propsRef = ref<Partial<ModalProps> | null>(null); |
| | | const modalWrapperRef = ref<any>(null); |
| | | const { prefixCls } = useDesign('basic-modal'); |
| | | |
| | | 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']); |
| | | // modal Bottom and top height |
| | | const extHeightRef = ref(0); |
| | | const modalMethods: ModalMethods = { |
| | | setModalProps, |
| | | emitOpen: undefined, |
| | | redoModalHeight: () => { |
| | | nextTick(() => { |
| | | if (unref(modalWrapperRef)) { |
| | | (unref(modalWrapperRef) as any).setModalHeight(); |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | return { |
| | | handleCancel, |
| | | getBindValue, |
| | | getProps, |
| | | handleFullScreen, |
| | | fullScreenRef, |
| | | getMergeProps, |
| | | handleOk, |
| | | visibleRef, |
| | | omit, |
| | | modalWrapperRef, |
| | | handleExtHeight, |
| | | handleHeightChange, |
| | | handleTitleDbClick, |
| | | getWrapperHeight, |
| | | }; |
| | | }, |
| | | }; |
| | | |
| | | 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: handleFullScreenInner, |
| | | 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), |
| | | open: unref(openRef), |
| | | okButtonProps: undefined, |
| | | cancelButtonProps: undefined, |
| | | title: undefined, |
| | | }; |
| | | return { |
| | | ...opt, |
| | | wrapClassName: unref(getWrapClassName), |
| | | }; |
| | | }); |
| | | |
| | | const getBindValue = computed((): Recordable => { |
| | | const attr = { |
| | | ...attrs, |
| | | ...unref(getMergeProps), |
| | | open: unref(openRef), |
| | | }; |
| | | if (attr?.['wrapClassName'] === unref(getWrapClassName)) { |
| | | attr['wrapClassName'] = `${attr?.['wrapClassName'] || ''} ` + prefixCls; |
| | | } else { |
| | | attr['wrapClassName'] = `${unref(getWrapClassName) || ''}` + prefixCls; |
| | | } |
| | | 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(() => { |
| | | openRef.value = !!props.open; |
| | | fullScreenRef.value = !!props.defaultFullscreen; |
| | | }); |
| | | |
| | | watch( |
| | | () => unref(openRef), |
| | | (v) => { |
| | | emit('open-change', v); |
| | | emit('update:open', v); |
| | | if (instance && modalMethods.emitOpen) { |
| | | modalMethods.emitOpen(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(); |
| | | openRef.value = !isClose; |
| | | return; |
| | | } |
| | | |
| | | openRef.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, 'open')) { |
| | | openRef.value = !!props.open; |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | // 事件传递 |
| | | function handleFullScreen(e) { |
| | | handleFullScreenInner(e); |
| | | emit('fullscreen'); |
| | | } |
| | | </script> |