| | |
| | | <template> |
| | | <ScrollContainer ref="wrapperRef"> |
| | | <ScrollContainer ref="wrapperRef" :scrollHeight="realHeight"> |
| | | <div ref="spinRef" :style="spinStyle" v-loading="loading" :loading-tip="loadingTip"> |
| | | <slot></slot> |
| | | </div> |
| | | </ScrollContainer> |
| | | </template> |
| | | <script lang="ts"> |
| | | <script lang="ts" setup> |
| | | import type { CSSProperties } from 'vue'; |
| | | import { |
| | | defineComponent, |
| | | computed, |
| | | ref, |
| | | watchEffect, |
| | | unref, |
| | | watch, |
| | | onMounted, |
| | | nextTick, |
| | | onUnmounted, |
| | | } 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 { ScrollContainer } from '@/components/Container'; |
| | | import { createModalContext } from '../hooks/useModalContext'; |
| | | import { useMutationObserver } from '@vueuse/core'; |
| | | |
| | | const props = { |
| | | defineOptions({ name: 'ModalWrapper', inheritAttrs: false }); |
| | | |
| | | const props = defineProps({ |
| | | loading: { type: Boolean }, |
| | | useWrapper: { type: Boolean, default: true }, |
| | | modalHeaderHeight: { type: Number, default: 57 }, |
| | |
| | | minHeight: { type: Number, default: 200 }, |
| | | height: { type: Number }, |
| | | footerOffset: { type: Number, default: 0 }, |
| | | visible: { type: Boolean }, |
| | | open: { type: Boolean }, |
| | | fullScreen: { type: Boolean }, |
| | | loadingTip: { type: String }, |
| | | }; |
| | | |
| | | export default defineComponent({ |
| | | name: 'ModalWrapper', |
| | | components: { ScrollContainer }, |
| | | inheritAttrs: false, |
| | | props, |
| | | emits: ['height-change', 'ext-height'], |
| | | setup(props, { emit }) { |
| | | const wrapperRef = ref(null); |
| | | const spinRef = ref(null); |
| | | const realHeightRef = ref(0); |
| | | const minRealHeightRef = ref(0); |
| | | |
| | | let realHeight = 0; |
| | | |
| | | let stopElResizeFn: AnyFunction = () => {}; |
| | | |
| | | useWindowSizeFn(setModalHeight.bind(null, false)); |
| | | |
| | | 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() { |
| | | // 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度 |
| | | // 加上这个,就必须在使用的时候传递父级的visible |
| | | if (!props.visible) 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 = spinEl.scrollHeight; |
| | | // } |
| | | |
| | | if (props.fullScreen) { |
| | | realHeightRef.value = |
| | | window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28; |
| | | } else { |
| | | realHeightRef.value = props.height |
| | | ? props.height |
| | | : realHeight > maxHeight |
| | | ? maxHeight |
| | | : realHeight; |
| | | } |
| | | emit('height-change', unref(realHeightRef)); |
| | | } catch (error) { |
| | | console.log(error); |
| | | } |
| | | } |
| | | |
| | | return { wrapperRef, spinRef, spinStyle, scrollTop, setModalHeight }; |
| | | }, |
| | | }); |
| | | |
| | | 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> |