Browse Source

fix(modal): ensure that the full screen height is calculated correctly

Vben 3 years ago
parent
commit
1c1755cf5b

+ 9 - 3
CHANGELOG.zh_CN.md

@@ -2,9 +2,15 @@
 
 ### ✨ Features
 
-- `Cropper` 头像裁剪新增圆形裁剪功能
-- 新增头像上传组件
-- `useDrawer`新增`closeDrawer`函数
+- **CropperImage** `Cropper` 头像裁剪新增圆形裁剪功能
+- **CropperAvatar** 新增头像上传组件
+- **Drawer** `useDrawer`新增`closeDrawer`函数
+
+### 🐛 Bug Fixes
+
+- **Modal** 修复全屏高度计算错误
+- **PageWrapper** 修复高度计算问题
+- 修复后台模式下,Iframe 路由错误
 
 ## 2.4.2(2021-06-10)
 

+ 1 - 1
src/components/Drawer/src/BasicDrawer.vue

@@ -163,7 +163,7 @@
 
       function setDrawerProps(props: Partial<DrawerProps>): void {
         // Keep the last setDrawerProps
-        propsRef.value = deepMerge((unref(propsRef) as any) || {}, props);
+        propsRef.value = deepMerge(unref(propsRef), props);
 
         if (Reflect.has(props, 'visible')) {
           visibleRef.value = !!props.visible;

+ 0 - 3
src/components/Drawer/src/useDrawer.ts

@@ -5,7 +5,6 @@ import type {
   DrawerProps,
   UseDrawerInnerReturnType,
 } from './typing';
-
 import {
   ref,
   getCurrentInstance,
@@ -16,11 +15,9 @@ import {
   toRaw,
   computed,
 } from 'vue';
-
 import { isProdMode } from '/@/utils/env';
 import { isFunction } from '/@/utils/is';
 import { tryOnUnmounted } from '@vueuse/core';
-
 import { isEqual } from 'lodash-es';
 import { error } from '/@/utils/log';
 

+ 4 - 3
src/components/Modal/index.ts

@@ -1,7 +1,8 @@
+import { withInstall } from '/@/utils';
 import './src/index.less';
-import BasicModal from './src/BasicModal.vue';
+import basicModal from './src/BasicModal.vue';
 
-export { BasicModal };
+export const BasicModal = withInstall(basicModal);
 export { useModalContext } from './src/hooks/useModalContext';
 export { useModal, useModalInner } from './src/hooks/useModal';
-export * from './src/types';
+export * from './src/typing';

+ 3 - 6
src/components/Modal/src/BasicModal.vue

@@ -49,7 +49,7 @@
   </Modal>
 </template>
 <script lang="ts">
-  import type { ModalProps, ModalMethods } from './types';
+  import type { ModalProps, ModalMethods } from './typing';
 
   import {
     defineComponent,
@@ -62,20 +62,17 @@
     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';
+
   export default defineComponent({
     name: 'BasicModal',
     components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
@@ -189,7 +186,7 @@
        */
       function setModalProps(props: Partial<ModalProps>): void {
         // Keep the last setModalProps
-        propsRef.value = deepMerge(unref(propsRef) || {}, props);
+        propsRef.value = deepMerge(unref(propsRef), props);
         if (!Reflect.has(props, 'visible')) return;
         visibleRef.value = !!props.visible;
       }

+ 0 - 1
src/components/Modal/src/components/Modal.tsx

@@ -20,7 +20,6 @@ export default defineComponent({
 
     return () => {
       const propsData = { ...unref(attrs), ...props } as Recordable;
-
       return <Modal {...propsData}>{extendSlots(slots)}</Modal>;
     };
   },

+ 3 - 4
src/components/Modal/src/components/ModalClose.vue

@@ -2,7 +2,6 @@
   <div :class="getClass">
     <template v-if="canFullscreen">
       <FullscreenExitOutlined role="full" @click="handleFullScreen" v-if="fullScreen" />
-
       <FullscreenOutlined role="close" @click="handleFullScreen" v-else />
     </template>
     <CloseOutlined @click="handleCancel" />
@@ -12,14 +11,13 @@
   import { defineComponent, computed } from 'vue';
   import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';
   import { useDesign } from '/@/hooks/web/useDesign';
-  import { propTypes } from '/@/utils/propTypes';
 
   export default defineComponent({
     name: 'ModalClose',
     components: { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined },
     props: {
-      canFullscreen: propTypes.bool.def(true),
-      fullScreen: propTypes.bool,
+      canFullscreen: { type: Boolean, default: true },
+      fullScreen: { type: Boolean },
     },
     emits: ['cancel', 'fullscreen'],
     setup(props, { emit }) {
@@ -38,6 +36,7 @@
       function handleCancel(e: Event) {
         emit('cancel', e);
       }
+
       function handleFullScreen(e: Event) {
         e?.stopPropagation();
         e?.preventDefault();

+ 1 - 0
src/components/Modal/src/components/ModalFooter.vue

@@ -33,6 +33,7 @@
       function handleCancel(e: Event) {
         emit('cancel', e);
       }
+
       return { handleOk, handleCancel };
     },
   });

+ 1 - 2
src/components/Modal/src/components/ModalHeader.vue

@@ -8,7 +8,6 @@
   import { defineComponent } from 'vue';
   import { BasicTitle } from '/@/components/Basic';
 
-  import { propTypes } from '/@/utils/propTypes';
   export default defineComponent({
     name: 'BasicModalHeader',
     components: { BasicTitle },
@@ -16,7 +15,7 @@
       helpMessage: {
         type: [String, Array] as PropType<string | string[]>,
       },
-      title: propTypes.string,
+      title: { type: String },
     },
   });
 </script>

+ 28 - 21
src/components/Modal/src/components/ModalWrapper.vue

@@ -6,9 +6,7 @@
   </ScrollContainer>
 </template>
 <script lang="ts">
-  import type { ModalWrapperProps } from '../types';
   import type { CSSProperties } from 'vue';
-
   import {
     defineComponent,
     computed,
@@ -20,31 +18,31 @@
     nextTick,
     onUnmounted,
   } from 'vue';
-
   import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
   import { ScrollContainer } from '/@/components/Container';
-
-  import { propTypes } from '/@/utils/propTypes';
   import { createModalContext } from '../hooks/useModalContext';
+  import { useMutationObserver } from '@vueuse/core';
+
+  const props = {
+    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 },
+    visible: { type: Boolean },
+    fullScreen: { type: Boolean },
+    loadingTip: { type: String },
+  };
 
   export default defineComponent({
     name: 'ModalWrapper',
     components: { ScrollContainer },
     inheritAttrs: false,
-    props: {
-      loading: propTypes.bool,
-      useWrapper: propTypes.bool.def(true),
-      modalHeaderHeight: propTypes.number.def(57),
-      modalFooterHeight: propTypes.number.def(74),
-      minHeight: propTypes.number.def(200),
-      height: propTypes.number,
-      footerOffset: propTypes.number.def(0),
-      visible: propTypes.bool,
-      fullScreen: propTypes.bool,
-      loadingTip: propTypes.string,
-    },
+    props,
     emits: ['height-change', 'ext-height'],
-    setup(props: ModalWrapperProps, { emit }) {
+    setup(props, { emit }) {
       const wrapperRef = ref<ComponentRef>(null);
       const spinRef = ref<ElRef>(null);
       const realHeightRef = ref(0);
@@ -56,6 +54,17 @@
 
       useWindowSizeFn(setModalHeight.bind(null, false));
 
+      useMutationObserver(
+        spinRef,
+        () => {
+          setModalHeight();
+        },
+        {
+          attributes: true,
+          subtree: true,
+        }
+      );
+
       createModalContext({
         redoModalHeight: setModalHeight,
       });
@@ -63,8 +72,7 @@
       const spinStyle = computed((): CSSProperties => {
         return {
           minHeight: `${props.minHeight}px`,
-          // padding 28
-          maxHeight: `${unref(realHeightRef)}px`,
+          [props.fullScreen ? 'height' : 'maxHeight']: `${unref(realHeightRef)}px`,
         };
       });
 
@@ -87,7 +95,6 @@
       onMounted(() => {
         const { modalHeaderHeight, modalFooterHeight } = props;
         emit('ext-height', modalHeaderHeight + modalFooterHeight);
-        // listenElResize();
       });
 
       onUnmounted(() => {

+ 23 - 25
src/components/Modal/src/hooks/useModal.ts

@@ -4,8 +4,7 @@ import type {
   ModalProps,
   ReturnMethods,
   UseModalInnerReturnType,
-} from '../types';
-
+} from '../typing';
 import {
   ref,
   onUnmounted,
@@ -20,10 +19,10 @@ import { isProdMode } from '/@/utils/env';
 import { isFunction } from '/@/utils/is';
 import { isEqual } from 'lodash-es';
 import { tryOnUnmounted } from '@vueuse/core';
-
 import { error } from '/@/utils/log';
 import { computed } from 'vue';
-const dataTransferRef = reactive<any>({});
+
+const dataTransfer = reactive<any>({});
 
 const visibleData = reactive<{ [key: number]: boolean }>({});
 
@@ -31,29 +30,31 @@ const visibleData = reactive<{ [key: number]: boolean }>({});
  * @description: Applicable to independent modal and call outside
  */
 export function useModal(): UseModalReturnType {
-  const modalRef = ref<Nullable<ModalMethods>>(null);
-  const loadedRef = ref<Nullable<boolean>>(false);
-  const uidRef = ref<string>('');
+  const modal = ref<Nullable<ModalMethods>>(null);
+  const loaded = ref<Nullable<boolean>>(false);
+  const uid = ref<string>('');
 
   function register(modalMethod: ModalMethods, uuid: string) {
-    uidRef.value = uuid;
-
+    if (!getCurrentInstance()) {
+      throw new Error('useModal() can only be used inside setup() or functional components!');
+    }
+    uid.value = uuid;
     isProdMode() &&
       onUnmounted(() => {
-        modalRef.value = null;
-        loadedRef.value = false;
-        dataTransferRef[unref(uidRef)] = null;
+        modal.value = null;
+        loaded.value = false;
+        dataTransfer[unref(uid)] = null;
       });
-    if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) return;
+    if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
 
-    modalRef.value = modalMethod;
+    modal.value = modalMethod;
     modalMethod.emitVisible = (visible: boolean, uid: number) => {
       visibleData[uid] = visible;
     };
   }
 
   const getInstance = () => {
-    const instance = unref(modalRef);
+    const instance = unref(modal);
     if (!instance) {
       error('useModal instance is undefined!');
     }
@@ -66,7 +67,7 @@ export function useModal(): UseModalReturnType {
     },
 
     getVisible: computed((): boolean => {
-      return visibleData[~~unref(uidRef)];
+      return visibleData[~~unref(uid)];
     }),
 
     redoModalHeight: () => {
@@ -79,15 +80,15 @@ export function useModal(): UseModalReturnType {
       });
 
       if (!data) return;
-
+      const id = unref(uid);
       if (openOnSet) {
-        dataTransferRef[unref(uidRef)] = null;
-        dataTransferRef[unref(uidRef)] = toRaw(data);
+        dataTransfer[id] = null;
+        dataTransfer[id] = toRaw(data);
         return;
       }
-      const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), toRaw(data));
+      const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data));
       if (!equal) {
-        dataTransferRef[unref(uidRef)] = toRaw(data);
+        dataTransfer[id] = toRaw(data);
       }
     },
 
@@ -103,9 +104,6 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
   const currentInstance = getCurrentInstance();
   const uidRef = ref<string>('');
 
-  // currentInstall.type.emits = [...currentInstall.type.emits, 'register'];
-  // Object.assign(currentInstall.type.emits, ['register']);
-
   const getInstance = () => {
     const instance = unref(modalInstanceRef);
     if (!instance) {
@@ -125,7 +123,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
   };
 
   watchEffect(() => {
-    const data = dataTransferRef[unref(uidRef)];
+    const data = dataTransfer[unref(uidRef)];
     if (!data) return;
     if (!callbackFn || !isFunction(callbackFn)) return;
     nextTick(() => {

+ 0 - 1
src/components/Modal/src/hooks/useModalFullScreen.ts

@@ -12,7 +12,6 @@ export function useFullScreen(context: UseFullScreenContext) {
 
   const getWrapClassName = computed(() => {
     const clsName = unref(context.wrapClassName) || '';
-
     return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName);
   });
 

+ 29 - 30
src/components/Modal/src/props.ts

@@ -1,45 +1,44 @@
 import type { PropType, CSSProperties } from 'vue';
+import type { ModalWrapperProps } from './typing';
 import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
-
 import { useI18n } from '/@/hooks/web/useI18n';
-import { propTypes, VueNode } from '/@/utils/propTypes';
-import type { ModalWrapperProps } from './types';
+
 const { t } = useI18n();
 
 export const modalProps = {
-  visible: propTypes.bool,
-  scrollTop: propTypes.bool.def(true),
-  height: propTypes.number,
-  minHeight: propTypes.number,
+  visible: { type: Boolean },
+  scrollTop: { type: Boolean, default: true },
+  height: { type: Number },
+  minHeight: { type: Number },
   // open drag
-  draggable: propTypes.bool.def(true),
-  centered: propTypes.bool,
-  cancelText: propTypes.string.def(t('common.cancelText')),
-  okText: propTypes.string.def(t('common.okText')),
+  draggable: { type: Boolean, default: true },
+  centered: { type: Boolean },
+  cancelText: { type: String, default: t('common.cancelText') },
+  okText: { type: String, default: t('common.okText') },
 
   closeFunc: Function as PropType<() => Promise<boolean>>,
 };
 
 export const basicProps = Object.assign({}, modalProps, {
-  defaultFullscreen: propTypes.bool,
+  defaultFullscreen: { type: Boolean },
   // Can it be full screen
-  canFullscreen: propTypes.bool.def(true),
+  canFullscreen: { type: Boolean, default: true },
   // After enabling the wrapper, the bottom can be increased in height
-  wrapperFooterOffset: propTypes.number.def(0),
+  wrapperFooterOffset: { type: Number, default: 0 },
   // Warm reminder message
   helpMessage: [String, Array] as PropType<string | string[]>,
   // Whether to setting wrapper
-  useWrapper: propTypes.bool.def(true),
-  loading: propTypes.bool,
-  loadingTip: propTypes.string,
+  useWrapper: { type: Boolean, default: true },
+  loading: { type: Boolean },
+  loadingTip: { type: String },
   /**
    * @description: Show close button
    */
-  showCancelBtn: propTypes.bool.def(true),
+  showCancelBtn: { type: Boolean, default: true },
   /**
    * @description: Show confirmation button
    */
-  showOkBtn: propTypes.bool.def(true),
+  showOkBtn: { type: Boolean, default: true },
 
   wrapperProps: Object as PropType<Partial<ModalWrapperProps>>,
 
@@ -47,38 +46,38 @@ export const basicProps = Object.assign({}, modalProps, {
 
   bodyStyle: Object as PropType<CSSProperties>,
 
-  closable: propTypes.bool.def(true),
+  closable: { type: Boolean, default: true },
 
   closeIcon: Object as PropType<VueNode>,
 
-  confirmLoading: propTypes.bool,
+  confirmLoading: { type: Boolean },
 
-  destroyOnClose: propTypes.bool,
+  destroyOnClose: { type: Boolean },
 
   footer: Object as PropType<VueNode>,
 
   getContainer: Function as PropType<() => any>,
 
-  mask: propTypes.bool.def(true),
+  mask: { type: Boolean, default: true },
 
-  maskClosable: propTypes.bool.def(true),
-  keyboard: propTypes.bool.def(true),
+  maskClosable: { type: Boolean, default: true },
+  keyboard: { type: Boolean, default: true },
 
   maskStyle: Object as PropType<CSSProperties>,
 
-  okType: propTypes.string.def('primary'),
+  okType: { type: String, default: 'primary' },
 
   okButtonProps: Object as PropType<ButtonProps>,
 
   cancelButtonProps: Object as PropType<ButtonProps>,
 
-  title: propTypes.string,
+  title: { type: String },
 
-  visible: propTypes.bool,
+  visible: { type: Boolean },
 
   width: [String, Number] as PropType<string | number>,
 
-  wrapClassName: propTypes.string,
+  wrapClassName: { type: String },
 
-  zIndex: propTypes.number,
+  zIndex: { type: Number },
 });

+ 0 - 0
src/components/Modal/src/types.ts → src/components/Modal/src/typing.ts


+ 4 - 2
src/components/Qrcode/index.ts

@@ -1,3 +1,5 @@
-export { default as QrCode } from './src/Qrcode.vue';
+import { withInstall } from '/@/utils';
+import qrCode from './src/Qrcode.vue';
 
-export * from './src/types';
+export const QrCode = withInstall(qrCode);
+export * from './src/typing';

+ 1 - 1
src/components/Qrcode/src/Qrcode.vue

@@ -8,7 +8,7 @@
   import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
   import { toDataURL } from 'qrcode';
   import { downloadByUrl } from '/@/utils/file/download';
-  import { QrcodeDoneEventParams } from './types';
+  import { QrcodeDoneEventParams } from './typing';
 
   export default defineComponent({
     name: 'QrCode',

+ 1 - 1
src/components/Qrcode/src/drawCanvas.ts

@@ -1,6 +1,6 @@
 import { toCanvas } from 'qrcode';
 import type { QRCodeRenderersOptions } from 'qrcode';
-import { RenderQrCodeParams, ContentType } from './types';
+import { RenderQrCodeParams, ContentType } from './typing';
 export const renderQrCode = ({ canvas, content, width = 0, options = {} }: RenderQrCodeParams) => {
   // 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
   options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);

+ 1 - 2
src/components/Qrcode/src/drawLogo.ts

@@ -1,12 +1,11 @@
 import { isString } from '/@/utils/is';
-import { RenderQrCodeParams, LogoType } from './types';
+import { RenderQrCodeParams, LogoType } from './typing';
 export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => {
   if (!logo) {
     return new Promise((resolve) => {
       resolve((canvas as HTMLCanvasElement).toDataURL());
     });
   }
-
   const canvasWidth = (canvas as HTMLCanvasElement).width;
   const {
     logoSize = 0.15,

+ 1 - 2
src/components/Qrcode/src/qrcodePlus.ts

@@ -1,5 +1,4 @@
 // 参考 qr-code-with-logo 进行ts版本修改
 import { toCanvas } from './toCanvas';
-export * from './types';
-
+export * from './typing';
 export { toCanvas };

+ 1 - 1
src/components/Qrcode/src/toCanvas.ts

@@ -1,6 +1,6 @@
 import { renderQrCode } from './drawCanvas';
 import { drawLogo } from './drawLogo';
-import { RenderQrCodeParams } from './types';
+import { RenderQrCodeParams } from './typing';
 export const toCanvas = (options: RenderQrCodeParams) => {
   return renderQrCode(options)
     .then(() => {

+ 0 - 0
src/components/Qrcode/src/types.ts → src/components/Qrcode/src/typing.ts


+ 1 - 1
src/layouts/default/header/components/lock/LockModal.vue

@@ -95,7 +95,7 @@
     &__entry {
       position: relative;
       //height: 240px;
-      padding: 130px 30px 60px 30px;
+      padding: 130px 30px 30px 30px;
       border-radius: 10px;
     }
 

+ 1 - 4
src/views/demo/comp/drawer/index.vue

@@ -8,10 +8,7 @@
     <Alert message="自适应高度/显示footer" show-icon />
     <a-button type="primary" class="my-4" @click="openDrawer3(true)"> 打开Drawer </a-button>
 
-    <Alert
-      message="内外数据交互,外部通过 transferModalData 发送,内部通过 receiveDrawerDataRef 接收。该数据具有响应式"
-      show-icon
-    />
+    <Alert message="内外数据交互" show-icon />
     <a-button type="primary" class="my-4" @click="send"> 打开Drawer并传递数据 </a-button>
     <Alert message="详情页模式" show-icon />
     <a-button type="primary" class="my-4" @click="openDrawer5(true)"> 打开详情Drawer </a-button>

+ 1 - 4
src/views/demo/comp/modal/index.vue

@@ -14,10 +14,7 @@
     <Alert message="自适应高度" show-icon />
     <a-button type="primary" class="my-4" @click="openModal3"> 打开弹窗 </a-button>
 
-    <Alert
-      message="内外数据交互,外部通过 transferModalData 发送,内部通过 receiveDrawerDataRef 接收。该数据具有响应式"
-      show-icon
-    />
+    <Alert message="内外数据交互" show-icon />
     <a-button type="primary" class="my-4" @click="send"> 打开弹窗并传递数据 </a-button>
 
     <Modal1 @register="register1" :minHeight="100" />

+ 2 - 0
types/global.d.ts

@@ -1,6 +1,7 @@
 import type {
   ComponentRenderProxy,
   VNode,
+  VNodeChild,
   ComponentPublicInstance,
   FunctionalComponent,
   PropType as VuePropType,
@@ -23,6 +24,7 @@ declare global {
 
   // vue
   declare type PropType<T> = VuePropType<T>;
+  declare type VueNode = VNodeChild | JSX.Element;
 
   export type Writable<T> = {
     -readonly [P in keyof T]: T[P];