Commit fb0c7763eddde38d3746cb424ebe9662ac576c86

Authored by vben
1 parent 2f1fbf8e

fix(modal): fix modal not showing footer

CHANGELOG.zh_CN.md
1 1 ## Wip
2 2  
  3 +### 🎫 Chores
  4 +
  5 +- 添加部分注释
  6 +- pwa 图标补充
  7 +- types 类型调整
  8 +
3 9 ### 🐛 Bug Fixes
4 10  
5 11 - 修复本地代理 post 接口到 https 地址超时错误
  12 +- 修复 modal 在不显示 footer 的时候全屏高度计算问题
6 13  
7 14 ## 2.0.0-rc.6 (2020-10-28)
8 15  
... ...
src/components/Authority/src/index.vue
1 1 <script lang="ts">
2   - import { defineComponent, PropType, computed, unref } from 'vue';
  2 + import type { PropType } from 'vue';
  3 + import { defineComponent, computed, unref } from 'vue';
3 4  
4 5 import { PermissionModeEnum } from '/@/enums/appEnum';
5 6 import { RoleEnum } from '/@/enums/roleEnum';
... ...
src/components/Basic/src/BasicArrow.vue
... ... @@ -7,11 +7,10 @@
7 7 import type { PropType } from 'vue';
8 8  
9 9 import { defineComponent, computed } from 'vue';
10   -
11 10 import { RightOutlined } from '@ant-design/icons-vue';
12 11  
13 12 export default defineComponent({
14   - name: 'BaseArrow',
  13 + name: 'BasicArrow',
15 14 components: { RightOutlined },
16 15 props: {
17 16 // Expand contract, expand by default
... ... @@ -24,7 +23,6 @@
24 23 const getClass = computed(() => {
25 24 const preCls = 'base-arrow';
26 25 const cls = [preCls];
27   -
28 26 props.expand && cls.push(`${preCls}__active`);
29 27 return cls;
30 28 });
... ...
src/components/Basic/src/BasicHelp.vue
1 1 <script lang="ts">
2 2 import type { PropType } from 'vue';
  3 + import { defineComponent, computed, unref, h } from 'vue';
3 4  
4 5 import { Tooltip } from 'ant-design-vue';
5 6 import { InfoCircleOutlined } from '@ant-design/icons-vue';
6   - import { defineComponent, computed, unref, h } from 'vue';
7 7  
8 8 import { getPopupContainer } from '/@/utils';
9   -
10 9 import { isString, isArray } from '/@/utils/is';
11 10 import { getSlot } from '/@/utils/helper/tsxHelper';
12 11 export default defineComponent({
13   - name: 'BaseHelp',
  12 + name: 'BasicHelp',
14 13 components: { Tooltip },
15 14 props: {
16 15 // max-width
... ... @@ -56,12 +55,14 @@
56 55 maxWidth: props.maxWidth,
57 56 };
58 57 });
  58 +
59 59 const getWrapStyleRef = computed(() => {
60 60 return {
61 61 color: props.color,
62 62 fontSize: props.fontSize,
63 63 };
64 64 });
  65 +
65 66 const getMainStyleRef = computed(() => {
66 67 return props.absolute ? props.position : {};
67 68 });
... ... @@ -81,6 +82,7 @@
81 82 }
82 83 return null;
83 84 };
  85 +
84 86 return () => {
85 87 return h(
86 88 Tooltip,
... ...
src/components/Basic/src/BasicTitle.vue
1 1 <template>
2 2 <span class="base-title" :class="{ 'show-span': showSpan && $slots.default }">
3 3 <slot />
4   - <BaseHelp class="base-title__help" v-if="helpMessage" :text="helpMessage" />
  4 + <BasicHelp class="base-title__help" v-if="helpMessage" :text="helpMessage" />
5 5 </span>
6 6 </template>
7 7 <script lang="ts">
... ... @@ -9,8 +9,11 @@
9 9  
10 10 import { defineComponent } from 'vue';
11 11  
  12 + import BasicHelp from './BasicHelp.vue';
  13 +
12 14 export default defineComponent({
13   - name: 'BaseTitle',
  15 + name: 'BasicTitle',
  16 + components: { BasicHelp },
14 17 props: {
15 18 helpMessage: {
16 19 type: [String, Array] as PropType<string | string[]>,
... ...
src/components/Breadcrumb/BreadcrumbItem.vue
... ... @@ -31,10 +31,12 @@
31 31 },
32 32 setup(props) {
33 33 const linkRef = ref<Nullable<HTMLElement>>(null);
  34 +
34 35 const parent = inject('breadcrumb') as {
35 36 separator: string;
36 37 separatorClass: string;
37 38 };
  39 +
38 40 const { push, replace } = useRouter();
39 41  
40 42 onMounted(() => {
... ...
src/components/Modal/src/BasicModal.tsx
1 1 import type { ModalProps, ModalMethods } from './types';
2 2  
  3 +import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
  4 +
3 5 import Modal from './Modal';
4   -import { Button } from 'ant-design-vue';
  6 +import Button from '/@/components/Button/index.vue';
5 7 import ModalWrapper from './ModalWrapper';
6 8 import { BasicTitle } from '/@/components/Basic';
7   -import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
8   -
9 9 import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';
10 10  
11   -import { basicProps } from './props';
12   -
13 11 import { getSlot, extendSlots } from '/@/utils/helper/tsxHelper';
14 12 import { isFunction } from '/@/utils/is';
15 13 import { deepMerge } from '/@/utils';
16 14 import { buildUUID } from '/@/utils/uuid';
17 15  
  16 +import { basicProps } from './props';
18 17 // import { triggerWindowResize } from '@/utils/event/triggerWindowResizeEvent';
19 18 export default defineComponent({
20 19 name: 'BasicModal',
... ... @@ -22,18 +21,14 @@ export default defineComponent({
22 21 emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register'],
23 22 setup(props, { slots, emit, attrs }) {
24 23 const visibleRef = ref(false);
25   -
26 24 const propsRef = ref<Partial<ModalProps> | null>(null);
27   -
28 25 const modalWrapperRef = ref<any>(null);
29   -
30 26 // modal Bottom and top height
31 27 const extHeightRef = ref(0);
32   -
33 28 // Unexpanded height of the popup
34 29 const formerHeightRef = ref(0);
35   -
36 30 const fullScreenRef = ref(false);
  31 +
37 32 // Custom title component: get title
38 33 const getMergeProps = computed(() => {
39 34 return {
... ... @@ -41,6 +36,7 @@ export default defineComponent({
41 36 ...(unref(propsRef) as any),
42 37 };
43 38 });
  39 +
44 40 // modal component does not need title
45 41 const getProps = computed((): any => {
46 42 const opt = {
... ... @@ -56,9 +52,11 @@ export default defineComponent({
56 52 wrapClassName: className,
57 53 };
58 54 });
  55 +
59 56 watchEffect(() => {
60 57 visibleRef.value = !!props.visible;
61 58 });
  59 +
62 60 watch(
63 61 () => unref(visibleRef),
64 62 (v) => {
... ... @@ -68,6 +66,7 @@ export default defineComponent({
68 66 immediate: false,
69 67 }
70 68 );
  69 +
71 70 /**
72 71 * @description: 渲染标题
73 72 */
... ... @@ -83,13 +82,17 @@ export default defineComponent({
83 82  
84 83 function renderContent() {
85 84 const { useWrapper, loading, wrapperProps } = unref(getProps);
86   - return useWrapper ? (
  85 + if (!useWrapper) return getSlot(slots);
  86 +
  87 + const showFooter = props.footer !== undefined && !props.footer ? 0 : undefined;
  88 + return (
87 89 <ModalWrapper
88 90 footerOffset={props.wrapperFooterOffset}
89 91 fullScreen={unref(fullScreenRef)}
90 92 ref={modalWrapperRef}
91 93 loading={loading}
92 94 visible={unref(visibleRef)}
  95 + modalFooterHeight={showFooter}
93 96 {...wrapperProps}
94 97 onGetExtHeight={(height: number) => {
95 98 extHeightRef.value = height;
... ... @@ -100,13 +103,12 @@ export default defineComponent({
100 103 >
101 104 {() => getSlot(slots)}
102 105 </ModalWrapper>
103   - ) : (
104   - getSlot(slots)
105 106 );
106 107 }
  108 +
107 109 // 取消事件
108 110 async function handleCancel(e: Event) {
109   - e.stopPropagation();
  111 + e && e.stopPropagation();
110 112 if (props.closeFunc && isFunction(props.closeFunc)) {
111 113 const isClose: boolean = await props.closeFunc();
112 114 visibleRef.value = !isClose;
... ... @@ -115,6 +117,7 @@ export default defineComponent({
115 117 visibleRef.value = false;
116 118 emit('cancel');
117 119 }
  120 +
118 121 // 底部按钮自定义实现,
119 122 function renderFooter() {
120 123 const {
... ... @@ -131,7 +134,6 @@ export default defineComponent({
131 134 return (
132 135 <>
133 136 {getSlot(slots, 'insertFooter')}
134   -
135 137 {showCancelBtn && (
136 138 <Button {...cancelButtonProps} onClick={handleCancel}>
137 139 {() => cancelText}
... ... @@ -150,11 +152,11 @@ export default defineComponent({
150 152 {() => okText}
151 153 </Button>
152 154 )}
153   -
154 155 {getSlot(slots, 'appendFooter')}
155 156 </>
156 157 );
157 158 }
  159 +
158 160 /**
159 161 * @description: 关闭按钮
160 162 */
... ... @@ -176,27 +178,26 @@ export default defineComponent({
176 178 }
177 179  
178 180 function handleFullScreen(e: Event) {
179   - e.stopPropagation();
  181 + e && e.stopPropagation();
180 182 fullScreenRef.value = !unref(fullScreenRef);
181 183  
182 184 const modalWrapper = unref(modalWrapperRef);
183   - if (modalWrapper) {
184   - const modalWrapSpinEl = (modalWrapper.$el as HTMLElement).querySelector(
185   - '.ant-spin-nested-loading'
186   - );
187   - if (modalWrapSpinEl) {
188   - if (!unref(formerHeightRef) && unref(fullScreenRef)) {
189   - formerHeightRef.value = (modalWrapSpinEl as HTMLElement).offsetHeight;
190   - console.log(formerHeightRef);
191   - }
192   - if (unref(fullScreenRef)) {
193   - (modalWrapSpinEl as HTMLElement).style.height = `${
194   - window.innerHeight - unref(extHeightRef)
195   - }px`;
196   - } else {
197   - (modalWrapSpinEl as HTMLElement).style.height = `${unref(formerHeightRef)}px`;
198   - }
199   - }
  185 + if (!modalWrapper) return;
  186 +
  187 + const wrapperEl = modalWrapper.$el as HTMLElement;
  188 + if (!wrapperEl) return;
  189 +
  190 + const modalWrapSpinEl = wrapperEl.querySelector('.ant-spin-nested-loading') as HTMLElement;
  191 + if (!modalWrapSpinEl) return;
  192 +
  193 + if (!unref(formerHeightRef) && unref(fullScreenRef)) {
  194 + formerHeightRef.value = modalWrapSpinEl.offsetHeight;
  195 + }
  196 +
  197 + if (unref(fullScreenRef)) {
  198 + modalWrapSpinEl.style.height = `${window.innerHeight - unref(extHeightRef)}px`;
  199 + } else {
  200 + modalWrapSpinEl.style.height = `${unref(formerHeightRef)}px`;
200 201 }
201 202 }
202 203  
... ... @@ -206,21 +207,22 @@ export default defineComponent({
206 207 function setModalProps(props: Partial<ModalProps>): void {
207 208 // Keep the last setModalProps
208 209 propsRef.value = deepMerge(unref(propsRef) || {}, props);
209   - if (Reflect.has(props, 'visible')) {
210   - visibleRef.value = !!props.visible;
211   - }
  210 + if (!Reflect.has(props, 'visible')) return;
  211 + visibleRef.value = !!props.visible;
212 212 }
213 213  
214 214 const modalMethods: ModalMethods = {
215 215 setModalProps,
216 216 };
  217 +
217 218 const uuid = buildUUID();
218 219 emit('register', modalMethods, uuid);
  220 +
219 221 return () => (
220 222 <Modal
221 223 onCancel={handleCancel}
222   - {...{ ...attrs, ...props, ...unref(getProps) }}
223 224 getContainer={() => document.querySelector('.default-layout__main')}
  225 + {...{ ...attrs, ...props, ...unref(getProps) }}
224 226 >
225 227 {{
226 228 ...extendSlots(slots, ['default']),
... ...
src/components/Modal/src/Modal.tsx
... ... @@ -23,6 +23,7 @@ export default defineComponent({
23 23 dialogHeaderEl.style.cursor = 'move';
24 24  
25 25 dialogHeaderEl.onmousedown = (e: any) => {
  26 + if (!e) return;
26 27 // 鼠标按下,计算当前元素距离可视区的距离
27 28 const disX = e.clientX;
28 29 const disY = e.clientY;
... ... @@ -84,8 +85,8 @@ export default defineComponent({
84 85 const handleDrag = () => {
85 86 const dragWraps = document.querySelectorAll('.ant-modal-wrap');
86 87 for (const wrap of dragWraps as any) {
  88 + if (!wrap) continue;
87 89 const display = getStyle(wrap, 'display');
88   -
89 90 const draggable = wrap.getAttribute('data-drag');
90 91 if (display !== 'none') {
91 92 // 拖拽位置
... ... @@ -98,7 +99,6 @@ export default defineComponent({
98 99 if (!props.visible) {
99 100 return;
100 101 }
101   - // context.$nextTick();
102 102 useTimeout(() => {
103 103 handleDrag();
104 104 }, 30);
... ...
src/components/Modal/src/ModalWrapper.tsx
... ... @@ -13,10 +13,9 @@ import {
13 13 onUnmounted,
14 14 } from 'vue';
15 15 import { Spin } from 'ant-design-vue';
16   -import { ScrollContainer } from '/@/components/Container/index';
17 16  
18 17 import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
19   -import { useTimeout } from '/@/hooks/core/useTimeout';
  18 +// import { useTimeout } from '/@/hooks/core/useTimeout';
20 19  
21 20 import { getSlot } from '/@/utils/helper/tsxHelper';
22 21 import { useElResize } from '/@/hooks/event/useElResize';
... ... @@ -61,26 +60,46 @@ export default defineComponent({
61 60 const wrapStyle = computed(() => {
62 61 return {
63 62 minHeight: `${props.minHeight}px`,
64   - overflow: 'hidden',
  63 + height: `${unref(realHeightRef)}px`,
  64 + overflow: 'auto',
65 65 };
66 66 });
67 67  
68 68 // 重试次数
69   - let tryCount = 0;
  69 + // let tryCount = 0;
  70 + let stopElResizeFn: Fn = () => {};
  71 +
  72 + watchEffect(() => {
  73 + setModalHeight();
  74 + });
  75 +
  76 + watch(
  77 + () => props.fullScreen,
  78 + (v) => {
  79 + !v && setModalHeight();
  80 + }
  81 + );
  82 +
  83 + onMounted(() => {
  84 + const { modalHeaderHeight, modalFooterHeight } = props;
  85 + emit('getExtHeight', modalHeaderHeight + modalFooterHeight);
  86 + listenElResize();
  87 + });
  88 +
  89 + onUnmounted(() => {
  90 + stopElResizeFn && stopElResizeFn();
  91 + });
  92 +
  93 + useWindowSizeFn(setModalHeight);
  94 +
70 95 async function setModalHeight() {
71 96 // 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
72 97 // 加上这个,就必须在使用的时候传递父级的visible
73   - if (!props.visible) {
74   - return;
75   - }
  98 + if (!props.visible) return;
76 99 const wrapperRefDom = unref(wrapperRef);
77   - if (!wrapperRefDom) {
78   - return;
79   - }
  100 + if (!wrapperRefDom) return;
80 101 const bodyDom = wrapperRefDom.parentElement;
81   - if (!bodyDom) {
82   - return;
83   - }
  102 + if (!bodyDom) return;
84 103 bodyDom.style.padding = '0';
85 104 await nextTick();
86 105  
... ... @@ -104,23 +123,23 @@ export default defineComponent({
104 123 }
105 124 await nextTick();
106 125 const spinEl = unref(spinRef);
107   - if (!spinEl) {
108   - useTimeout(() => {
109   - // retry
110   - if (tryCount < 3) {
111   - setModalHeight();
112   - }
113   - tryCount++;
114   - }, 10);
115   - return;
116   - }
117   - tryCount = 0;
  126 + // if (!spinEl) {
  127 + // useTimeout(() => {
  128 + // // retry
  129 + // if (tryCount < 3) {
  130 + // setModalHeight();
  131 + // }
  132 + // tryCount++;
  133 + // }, 10);
  134 + // return;
  135 + // }
  136 + // tryCount = 0;
118 137  
119   - const realHeight = (spinEl.$el.querySelector('.ant-spin-container') as HTMLElement)
120   - .scrollHeight;
  138 + const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement;
  139 + if (!spinContainerEl) return;
  140 +
  141 + const realHeight = spinContainerEl.scrollHeight;
121 142  
122   - // 16为 p-2和m-2 加起来为4,基础4, 4*4=16
123   - // 32 padding
124 143 if (props.fullScreen) {
125 144 realHeightRef.value =
126 145 window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 26;
... ... @@ -138,6 +157,7 @@ export default defineComponent({
138 157 console.log(error);
139 158 }
140 159 }
  160 +
141 161 function listenElResize() {
142 162 const wrapper = unref(wrapperRef);
143 163 if (!wrapper) return;
... ... @@ -146,41 +166,16 @@ export default defineComponent({
146 166 const [start, stop] = useElResize(container, () => {
147 167 setModalHeight();
148 168 });
  169 + stopElResizeFn = stop;
149 170 start();
150   - onUnmounted(() => {
151   - stop();
152   - });
153 171 }
154   - nextTick(() => {});
155   - watchEffect(() => {
156   - setModalHeight();
157   - });
158   - watch(
159   - () => props.fullScreen,
160   - (v) => {
161   - !v && setModalHeight();
162   - }
163   - );
164   -
165   - onMounted(() => {
166   - const { modalHeaderHeight, modalFooterHeight } = props;
167   - emit('getExtHeight', modalHeaderHeight + modalFooterHeight);
168   - listenElResize();
169   - });
170   -
171   - useWindowSizeFn(setModalHeight);
172 172  
173 173 return () => {
174   - const height = unref(realHeightRef);
175 174 return (
176 175 <div ref={wrapperRef} style={unref(wrapStyle)}>
177   - <ScrollContainer>
178   - {() => (
179   - <Spin ref={spinRef} spinning={props.loading} style={{ height: `${height}px` }}>
180   - {() => getSlot(slots)}
181   - </Spin>
182   - )}
183   - </ScrollContainer>
  176 + <Spin ref={spinRef} spinning={props.loading}>
  177 + {() => getSlot(slots)}
  178 + </Spin>
184 179 </div>
185 180 );
186 181 };
... ...
src/components/Modal/src/index.less
... ... @@ -26,7 +26,7 @@
26 26 line-height: 16px;
27 27  
28 28 .base-title {
29   - cursor: move;
  29 + cursor: move !important;
30 30 }
31 31 }
32 32  
... ... @@ -56,7 +56,6 @@
56 56 }
57 57  
58 58 .ant-modal-body {
59   - // background: #f1f2f6;
60 59 padding: 0;
61 60 }
62 61  
... ... @@ -69,7 +68,6 @@
69 68 }
70 69  
71 70 &-header {
72   - // padding: 12.5px 24px;
73 71 padding: 16px;
74 72 }
75 73  
... ... @@ -79,7 +77,6 @@
79 77  
80 78 &-footer {
81 79 padding: 10px 26px 26px 16px;
82   - // border-top: none;
83 80  
84 81 button + button {
85 82 margin-left: 10px;
... ...
src/components/Modal/src/useModal.ts
... ... @@ -21,15 +21,15 @@ export function useModal(): UseModalReturnType {
21 21 const uidRef = ref<string>('');
22 22 function register(modalMethod: ModalMethods, uuid: string) {
23 23 uidRef.value = uuid;
  24 +
24 25 isProdMode() &&
25 26 onUnmounted(() => {
26 27 modalRef.value = null;
27 28 loadedRef.value = false;
28 29 dataTransferRef[unref(uidRef)] = null;
29 30 });
30   - if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) {
31   - return;
32   - }
  31 + if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) return;
  32 +
33 33 modalRef.value = modalMethod;
34 34 }
35 35 const getInstance = () => {
... ... @@ -44,11 +44,13 @@ export function useModal(): UseModalReturnType {
44 44 setModalProps: (props: Partial<ModalProps>): void => {
45 45 getInstance().setModalProps(props);
46 46 },
  47 +
47 48 openModal: (visible = true): void => {
48 49 getInstance().setModalProps({
49 50 visible: visible,
50 51 });
51 52 },
  53 +
52 54 transferModalData(val: any) {
53 55 dataTransferRef[unref(uidRef)] = val;
54 56 },
... ... @@ -64,6 +66,7 @@ export const useModalInner = (): UseModalInnerReturnType =&gt; {
64 66 if (!currentInstall) {
65 67 throw new Error('instance is undefined!');
66 68 }
  69 +
67 70 const getInstance = () => {
68 71 const instance = unref(modalInstanceRef);
69 72 if (!instance) {
... ... @@ -71,26 +74,32 @@ export const useModalInner = (): UseModalInnerReturnType =&gt; {
71 74 }
72 75 return instance;
73 76 };
  77 +
74 78 const register = (modalInstance: ModalMethods, uuid: string) => {
75 79 uidRef.value = uuid;
76 80 modalInstanceRef.value = modalInstance;
77 81 currentInstall.emit('register', modalInstance);
78 82 };
  83 +
79 84 return [
80 85 register,
81 86 {
82 87 receiveModalDataRef: computed(() => {
83 88 return dataTransferRef[unref(uidRef)];
84 89 }),
  90 +
85 91 changeLoading: (loading = true) => {
86 92 getInstance().setModalProps({ loading });
87 93 },
  94 +
88 95 changeOkLoading: (loading = true) => {
89 96 getInstance().setModalProps({ confirmLoading: loading });
90 97 },
  98 +
91 99 closeModal: () => {
92 100 getInstance().setModalProps({ visible: false });
93 101 },
  102 +
94 103 setModalProps: (props: Partial<ModalProps>) => {
95 104 getInstance().setModalProps(props);
96 105 },
... ...
src/components/registerGlobComp.ts
1 1 import Icon from './Icon/index';
2   -import { BasicHelp, BasicTitle } from './Basic';
3 2 import Button from './Button/index.vue';
4 3 import { Button as AntButton } from 'ant-design-vue';
5 4 import { getApp } from '/@/useApp';
6 5  
7   -const compList = [Icon, BasicHelp, BasicTitle, Button, AntButton.Group];
  6 +const compList = [Icon, Button, AntButton.Group];
8 7  
9 8 // Fix hmr multiple registered components
10 9 let registered = false;
... ...