<template>
|
<ScrollContainer ref="wrapperRef" :scrollHeight="realHeight">
|
<div ref="spinRef" :style="spinStyle" v-loading="loading" :loading-tip="loadingTip">
|
<slot></slot>
|
</div>
|
</ScrollContainer>
|
</template>
|
<script lang="ts" setup>
|
import type { CSSProperties } from 'vue';
|
import { computed, ref, watchEffect, unref, watch, onMounted, nextTick, onUnmounted } from 'vue';
|
import { useWindowSizeFn } from '@vben/hooks';
|
import { type AnyFunction } from '@vben/types';
|
import { ScrollContainer } from '@/components/Container';
|
import { createModalContext } from '../hooks/useModalContext';
|
import { useMutationObserver } from '@vueuse/core';
|
|
defineOptions({ name: 'ModalWrapper', inheritAttrs: false });
|
|
const props = defineProps({
|
loading: { type: Boolean },
|
useWrapper: { type: Boolean, default: true },
|
modalHeaderHeight: { type: Number, default: 57 },
|
modalFooterHeight: { type: Number, default: 74 },
|
minHeight: { type: Number, default: 200 },
|
height: { type: Number },
|
footerOffset: { type: Number, default: 0 },
|
open: { type: Boolean },
|
fullScreen: { type: Boolean },
|
loadingTip: { type: String },
|
});
|
|
const emit = defineEmits(['height-change', 'ext-height']);
|
|
const wrapperRef = ref(null);
|
const spinRef = ref(null);
|
const realHeightRef = ref(0);
|
const minRealHeightRef = ref(0);
|
const realHeight = ref(0);
|
|
let stopElResizeFn: AnyFunction = () => {};
|
|
useWindowSizeFn(setModalHeight.bind(null));
|
|
useMutationObserver(
|
spinRef,
|
() => {
|
setModalHeight();
|
},
|
{
|
attributes: true,
|
subtree: true,
|
},
|
);
|
|
createModalContext({
|
redoModalHeight: setModalHeight,
|
});
|
|
const spinStyle = computed((): CSSProperties => {
|
return {
|
minHeight: `${props.minHeight}px`,
|
[props.fullScreen ? 'height' : 'maxHeight']: `${unref(realHeightRef)}px`,
|
};
|
});
|
|
watchEffect(() => {
|
props.useWrapper && setModalHeight();
|
});
|
|
watch(
|
() => props.fullScreen,
|
(v) => {
|
setModalHeight();
|
if (!v) {
|
realHeightRef.value = minRealHeightRef.value;
|
} else {
|
minRealHeightRef.value = realHeightRef.value;
|
}
|
},
|
);
|
|
onMounted(() => {
|
const { modalHeaderHeight, modalFooterHeight } = props;
|
emit('ext-height', modalHeaderHeight + modalFooterHeight);
|
});
|
|
onUnmounted(() => {
|
stopElResizeFn && stopElResizeFn();
|
});
|
|
async function scrollTop() {
|
nextTick(() => {
|
const wrapperRefDom = unref(wrapperRef);
|
if (!wrapperRefDom) return;
|
(wrapperRefDom as any)?.scrollTo?.(0);
|
});
|
}
|
|
async function setModalHeight() {
|
// 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
|
// 加上这个,就必须在使用的时候传递父级的open
|
if (!props.open) return;
|
const wrapperRefDom = unref(wrapperRef);
|
if (!wrapperRefDom) return;
|
|
const bodyDom = (wrapperRefDom as any).$el.parentElement;
|
if (!bodyDom) return;
|
bodyDom.style.padding = '0';
|
await nextTick();
|
|
try {
|
const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;
|
if (!modalDom) return;
|
|
const modalRect = getComputedStyle(modalDom as Element).top;
|
const modalTop = Number.parseInt(modalRect);
|
let maxHeight =
|
window.innerHeight -
|
modalTop * 2 +
|
(props.footerOffset! || 0) -
|
props.modalFooterHeight -
|
props.modalHeaderHeight;
|
|
// 距离顶部过进会出现滚动条
|
if (modalTop < 40) {
|
maxHeight -= 26;
|
}
|
await nextTick();
|
const spinEl: any = unref(spinRef);
|
|
if (!spinEl) return;
|
await nextTick();
|
// if (!realHeight) {
|
realHeight.value = spinEl.scrollHeight;
|
// }
|
|
if (props.fullScreen) {
|
realHeightRef.value =
|
window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28;
|
} else {
|
realHeightRef.value = props.height
|
? props.height
|
: realHeight.value > maxHeight
|
? maxHeight
|
: realHeight.value;
|
}
|
emit('height-change', unref(realHeightRef));
|
} catch (error) {
|
console.log(error);
|
}
|
}
|
|
defineExpose({ scrollTop, setModalHeight });
|
</script>
|