<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">
|
import type { ModalProps, ModalMethods } from './typing';
|
|
import {
|
defineComponent,
|
computed,
|
ref,
|
watch,
|
unref,
|
watchEffect,
|
toRef,
|
getCurrentInstance,
|
nextTick,
|
} 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';
|
|
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');
|
|
// 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);
|
}
|
|
return {
|
handleCancel,
|
getBindValue,
|
getProps,
|
handleFullScreen,
|
fullScreenRef,
|
getMergeProps,
|
handleOk,
|
visibleRef,
|
omit,
|
modalWrapperRef,
|
handleExtHeight,
|
handleHeightChange,
|
handleTitleDbClick,
|
getWrapperHeight,
|
};
|
},
|
});
|
</script>
|