Commit ebf7c8aa53b7ed11c72734646d558a559e818473

Authored by vben
1 parent 73cee06d

perf(modal-drawer): replace the scrollbar assembly

Showing 40 changed files with 1088 additions and 862 deletions
CHANGELOG.zh_CN.md
@@ -12,6 +12,10 @@ @@ -12,6 +12,10 @@
12 - form: 新增远程下拉`ApiSelect`及示例 12 - form: 新增远程下拉`ApiSelect`及示例
13 - form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框 13 - form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框
14 14
  15 +### ⚡ Performance Improvements
  16 +
  17 +- 优化`modal`与`drawer`滚动条组件
  18 +
15 ### 🐛 Bug Fixes 19 ### 🐛 Bug Fixes
16 20
17 - 修复混合模式下滚动条丢失问题 21 - 修复混合模式下滚动条丢失问题
@@ -21,6 +25,7 @@ @@ -21,6 +25,7 @@
21 - 修复路由类型错误 25 - 修复路由类型错误
22 - 修复菜单分割时权限失效问题 26 - 修复菜单分割时权限失效问题
23 - 关闭多标签页时 iframe 提前加载 27 - 关闭多标签页时 iframe 提前加载
  28 +- 修复`modal`与`drawer`已知问题
24 29
25 ## 2.0.0-rc.14 (2020-12-15) 30 ## 2.0.0-rc.14 (2020-12-15)
26 31
src/components/Container/src/LazyContainer.vue
1 <template> 1 <template>
2 <transition-group 2 <transition-group
3 - class="lazy-container" 3 + :class="prefixCls"
4 v-bind="$attrs" 4 v-bind="$attrs"
5 ref="elRef" 5 ref="elRef"
6 :name="transitionName" 6 :name="transitionName"
@@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
25 import { useTimeoutFn } from '/@/hooks/core/useTimeout'; 25 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
26 import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver'; 26 import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
27 import { propTypes } from '/@/utils/propTypes'; 27 import { propTypes } from '/@/utils/propTypes';
  28 + import { useDesign } from '/@/hooks/web/useDesign';
28 29
29 interface State { 30 interface State {
30 isInit: boolean; 31 isInit: boolean;
@@ -70,6 +71,8 @@ @@ -70,6 +71,8 @@
70 intersectionObserverInstance: null, 71 intersectionObserverInstance: null,
71 }); 72 });
72 73
  74 + const { prefixCls } = useDesign('lazy-container');
  75 +
73 onMounted(() => { 76 onMounted(() => {
74 immediateInit(); 77 immediateInit();
75 initIntersectionObserver(); 78 initIntersectionObserver();
@@ -129,13 +132,17 @@ @@ -129,13 +132,17 @@
129 } 132 }
130 return { 133 return {
131 elRef, 134 elRef,
  135 + prefixCls,
132 ...toRefs(state), 136 ...toRefs(state),
133 }; 137 };
134 }, 138 },
135 }); 139 });
136 </script> 140 </script>
137 <style lang="less"> 141 <style lang="less">
138 - .lazy-container { 142 + @import (reference) '../../../design/index.less';
  143 + @prefix-cls: ~'@{namespace}-lazy-container';
  144 +
  145 + .@{prefix-cls} {
139 width: 100%; 146 width: 100%;
140 height: 100%; 147 height: 100%;
141 } 148 }
src/components/Container/src/collapse/CollapseContainer.vue
1 <template> 1 <template>
2 - <div class="collapse-container p-2">  
3 - <CollapseHeader v-bind="$props" :show="show" @expand="handleExpand"> 2 + <div :class="['p-2', prefixCls]">
  3 + <CollapseHeader v-bind="$props" :prefixCls="prefixCls" :show="show" @expand="handleExpand">
4 <template #title> 4 <template #title>
5 <slot name="title" /> 5 <slot name="title" />
6 </template> 6 </template>
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 8
9 <CollapseTransition :enable="canExpan"> 9 <CollapseTransition :enable="canExpan">
10 <Skeleton v-if="loading" /> 10 <Skeleton v-if="loading" />
11 - <div class="collapse-container__body" v-else v-show="show"> 11 + <div :class="`${prefixCls}__body`" v-else v-show="show">
12 <LazyContainer :timeout="lazyTime" v-if="lazy"> 12 <LazyContainer :timeout="lazyTime" v-if="lazy">
13 <slot /> 13 <slot />
14 <template #skeleton> 14 <template #skeleton>
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 // hook 35 // hook
36 import { useTimeoutFn } from '/@/hooks/core/useTimeout'; 36 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
37 import { propTypes } from '/@/utils/propTypes'; 37 import { propTypes } from '/@/utils/propTypes';
  38 + import { useDesign } from '/@/hooks/web/useDesign';
38 39
39 export default defineComponent({ 40 export default defineComponent({
40 name: 'CollapseContainer', 41 name: 'CollapseContainer',
@@ -64,6 +65,9 @@ @@ -64,6 +65,9 @@
64 }, 65 },
65 setup(props) { 66 setup(props) {
66 const show = ref(true); 67 const show = ref(true);
  68 +
  69 + const { prefixCls } = useDesign('collapse-container');
  70 +
67 /** 71 /**
68 * @description: Handling development events 72 * @description: Handling development events
69 */ 73 */
@@ -77,20 +81,20 @@ @@ -77,20 +81,20 @@
77 return { 81 return {
78 show, 82 show,
79 handleExpand, 83 handleExpand,
  84 + prefixCls,
80 }; 85 };
81 }, 86 },
82 }); 87 });
83 </script> 88 </script>
84 <style lang="less"> 89 <style lang="less">
85 - .collapse-container { 90 + @import (reference) '../../../../design/index.less';
  91 + @prefix-cls: ~'@{namespace}-collapse-container';
  92 +
  93 + .@{prefix-cls} {
86 background: #fff; 94 background: #fff;
87 border-radius: 2px; 95 border-radius: 2px;
88 transition: all 0.3s ease-in-out; 96 transition: all 0.3s ease-in-out;
89 97
90 - &.no-shadow {  
91 - box-shadow: none;  
92 - }  
93 -  
94 &__header { 98 &__header {
95 display: flex; 99 display: flex;
96 height: 32px; 100 height: 32px;
src/components/Container/src/collapse/CollapseHeader.vue
1 <template> 1 <template>
2 - <div class="collapse-container__header"> 2 + <div :class="`${prefixCls}__header`">
3 <BasicTitle :helpMessage="$attrs.helpMessage"> 3 <BasicTitle :helpMessage="$attrs.helpMessage">
4 <template v-if="$attrs.title"> 4 <template v-if="$attrs.title">
5 {{ $attrs.title }} 5 {{ $attrs.title }}
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 </template> 9 </template>
10 </BasicTitle> 10 </BasicTitle>
11 11
12 - <div class="collapse-container__action"> 12 + <div :class="`${prefixCls}__action`">
13 <slot name="action" /> 13 <slot name="action" />
14 <BasicArrow v-if="$attrs.canExpan" top :expand="$attrs.show" @click="$emit('expand')" /> 14 <BasicArrow v-if="$attrs.canExpan" top :expand="$attrs.show" @click="$emit('expand')" />
15 </div> 15 </div>
@@ -21,5 +21,8 @@ @@ -21,5 +21,8 @@
21 export default defineComponent({ 21 export default defineComponent({
22 inheritAttrs: false, 22 inheritAttrs: false,
23 components: { BasicArrow, BasicTitle }, 23 components: { BasicArrow, BasicTitle },
  24 + props: {
  25 + prefixCls: String,
  26 + },
24 }); 27 });
25 </script> 28 </script>
src/components/Description/src/index.tsx
@@ -24,7 +24,7 @@ export default defineComponent({ @@ -24,7 +24,7 @@ export default defineComponent({
24 const getMergeProps = computed(() => { 24 const getMergeProps = computed(() => {
25 return { 25 return {
26 ...props, 26 ...props,
27 - ...(unref(propsRef) as any), 27 + ...(unref(propsRef) as Recordable),
28 } as DescOptions; 28 } as DescOptions;
29 }); 29 });
30 30
src/components/Description/src/props.ts
@@ -13,7 +13,7 @@ export default { @@ -13,7 +13,7 @@ export default {
13 bordered: propTypes.bool.def(true), 13 bordered: propTypes.bool.def(true),
14 14
15 column: { 15 column: {
16 - type: [Number, Object] as PropType<number | any>, 16 + type: [Number, Object] as PropType<number | Recordable>,
17 default: () => { 17 default: () => {
18 return { xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 }; 18 return { xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 };
19 }, 19 },
src/components/Description/src/types.ts
@@ -15,7 +15,10 @@ export interface DescItem { @@ -15,7 +15,10 @@ export interface DescItem {
15 span?: number; 15 span?: number;
16 show?: (...arg: any) => boolean; 16 show?: (...arg: any) => boolean;
17 // render 17 // render
18 - render?: (val: string, data: any) => VNode | undefined | JSX.Element | Element | string | number; 18 + render?: (
  19 + val: string,
  20 + data: Recordable
  21 + ) => VNode | undefined | JSX.Element | Element | string | number;
19 } 22 }
20 23
21 export interface DescOptions extends DescriptionsProps { 24 export interface DescOptions extends DescriptionsProps {
@@ -30,7 +33,7 @@ export interface DescOptions extends DescriptionsProps { @@ -30,7 +33,7 @@ export interface DescOptions extends DescriptionsProps {
30 * 数据 33 * 数据
31 * @type object 34 * @type object
32 */ 35 */
33 - data: any; 36 + data: Recordable;
34 /** 37 /**
35 * Built-in CollapseContainer component configuration 38 * Built-in CollapseContainer component configuration
36 * @type CollapseContainerOptions 39 * @type CollapseContainerOptions
src/components/Description/src/useDescription.ts
@@ -19,7 +19,7 @@ export function useDescription(props?: Partial&lt;DescOptions&gt;): UseDescReturnType @@ -19,7 +19,7 @@ export function useDescription(props?: Partial&lt;DescOptions&gt;): UseDescReturnType
19 19
20 const methods: DescInstance = { 20 const methods: DescInstance = {
21 setDescProps: (descProps: Partial<DescOptions>): void => { 21 setDescProps: (descProps: Partial<DescOptions>): void => {
22 - unref(descRef)!.setDescProps(descProps); 22 + unref(descRef)?.setDescProps(descProps);
23 }, 23 },
24 }; 24 };
25 25
src/components/Drawer/index.ts
1 import { withInstall } from '../util'; 1 import { withInstall } from '../util';
2 2
3 -import BasicDrawer from './src/BasicDrawer'; 3 +import BasicDrawer from './src/BasicDrawer.vue';
4 4
5 export { BasicDrawer }; 5 export { BasicDrawer };
6 export * from './src/types'; 6 export * from './src/types';
src/components/Drawer/src/BasicDrawer.tsx deleted 100644 → 0
1 -import './index.less';  
2 -  
3 -import type { DrawerInstance, DrawerProps } from './types';  
4 -import type { CSSProperties } from 'vue';  
5 -  
6 -import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, toRaw } from 'vue';  
7 -import { Drawer, Row, Col, Button } from 'ant-design-vue';  
8 -  
9 -import { BasicTitle } from '/@/components/Basic';  
10 -import { Loading } from '/@/components/Loading';  
11 -import { LeftOutlined } from '@ant-design/icons-vue';  
12 -  
13 -import { useI18n } from '/@/hooks/web/useI18n';  
14 -  
15 -import { getSlot } from '/@/utils/helper/tsxHelper';  
16 -import { isFunction, isNumber } from '/@/utils/is';  
17 -import { deepMerge } from '/@/utils';  
18 -import { tryTsxEmit } from '/@/utils/helper/vueHelper';  
19 -  
20 -import { basicProps } from './props';  
21 -  
22 -const prefixCls = 'basic-drawer';  
23 -export default defineComponent({  
24 - inheritAttrs: false,  
25 - props: basicProps,  
26 - emits: ['visible-change', 'ok', 'close', 'register'],  
27 - setup(props, { slots, emit, attrs }) {  
28 - const scrollRef = ref<ElRef>(null);  
29 - const visibleRef = ref(false);  
30 - const propsRef = ref<Partial<Nullable<DrawerProps>>>(null);  
31 -  
32 - const { t } = useI18n();  
33 -  
34 - const getMergeProps = computed(  
35 - (): DrawerProps => {  
36 - return deepMerge(toRaw(props), unref(propsRef));  
37 - }  
38 - );  
39 -  
40 - const getProps = computed(  
41 - (): DrawerProps => {  
42 - const opt = {  
43 - placement: 'right',  
44 - ...attrs,  
45 - ...unref(getMergeProps),  
46 - visible: unref(visibleRef),  
47 - };  
48 - opt.title = undefined;  
49 - const { isDetail, width, wrapClassName, getContainer } = opt;  
50 - if (isDetail) {  
51 - if (!width) {  
52 - opt.width = '100%';  
53 - }  
54 - const detailCls = `${prefixCls}__detail`;  
55 -  
56 - opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;  
57 -  
58 - if (!getContainer) {  
59 - // TODO type error?  
60 - opt.getContainer = '.layout-content' as any;  
61 - }  
62 - }  
63 - return opt as DrawerProps;  
64 - }  
65 - );  
66 -  
67 - const getBindValues = computed(  
68 - (): DrawerProps => {  
69 - return {  
70 - ...attrs,  
71 - ...unref(getProps),  
72 - };  
73 - }  
74 - );  
75 -  
76 - // Custom implementation of the bottom button,  
77 - const getFooterHeight = computed(() => {  
78 - const { footerHeight, showFooter } = unref(getProps);  
79 -  
80 - if (showFooter && footerHeight) {  
81 - return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`;  
82 - }  
83 - return `0px`;  
84 - });  
85 -  
86 - const getScrollContentStyle = computed(  
87 - (): CSSProperties => {  
88 - const footerHeight = unref(getFooterHeight);  
89 - return {  
90 - position: 'relative',  
91 - height: `calc(100% - ${footerHeight})`,  
92 - overflow: 'auto',  
93 - padding: '16px',  
94 - paddingBottom: '30px',  
95 - };  
96 - }  
97 - );  
98 -  
99 - const getLoading = computed(() => {  
100 - return !!unref(getProps)?.loading;  
101 - });  
102 -  
103 - watchEffect(() => {  
104 - visibleRef.value = props.visible;  
105 - });  
106 -  
107 - watch(  
108 - () => visibleRef.value,  
109 - (visible) => {  
110 - nextTick(() => {  
111 - emit('visible-change', visible);  
112 - });  
113 - },  
114 - {  
115 - immediate: false,  
116 - }  
117 - );  
118 -  
119 - // Cancel event  
120 - async function onClose(e: ChangeEvent) {  
121 - const { closeFunc } = unref(getProps);  
122 - emit('close', e);  
123 - if (closeFunc && isFunction(closeFunc)) {  
124 - const res = await closeFunc();  
125 - visibleRef.value = !res;  
126 - return;  
127 - }  
128 - visibleRef.value = false;  
129 - }  
130 -  
131 - function setDrawerProps(props: Partial<DrawerProps>): void {  
132 - // Keep the last setDrawerProps  
133 - propsRef.value = deepMerge(unref(propsRef) || {}, props);  
134 -  
135 - if (Reflect.has(props, 'visible')) {  
136 - visibleRef.value = !!props.visible;  
137 - }  
138 - }  
139 -  
140 - function renderFooter() {  
141 - if (slots?.footer) {  
142 - return getSlot(slots, 'footer');  
143 - }  
144 - const {  
145 - showCancelBtn,  
146 - cancelButtonProps,  
147 - cancelText,  
148 - showOkBtn,  
149 - okType,  
150 - okText,  
151 - okButtonProps,  
152 - confirmLoading,  
153 - showFooter,  
154 - } = unref(getProps);  
155 - if (!showFooter) {  
156 - return null;  
157 - }  
158 -  
159 - return (  
160 - <div class={`${prefixCls}__footer`}>  
161 - {getSlot(slots, 'insertFooter')}  
162 - {showCancelBtn && (  
163 - <Button {...cancelButtonProps} onClick={onClose} class="mr-2">  
164 - {() => cancelText}  
165 - </Button>  
166 - )}  
167 - {getSlot(slots, 'centerFooter')}  
168 - {showOkBtn && (  
169 - <Button  
170 - type={okType}  
171 - onClick={() => {  
172 - emit('ok');  
173 - }}  
174 - {...okButtonProps}  
175 - loading={confirmLoading}  
176 - >  
177 - {() => okText}  
178 - </Button>  
179 - )}  
180 - {getSlot(slots, 'appendFooter')}  
181 - </div>  
182 - );  
183 - }  
184 -  
185 - function renderHeader() {  
186 - if (slots?.title) {  
187 - return getSlot(slots, 'title');  
188 - }  
189 - const { title } = unref(getMergeProps);  
190 -  
191 - if (!props.isDetail) {  
192 - return <BasicTitle>{() => title || getSlot(slots, 'title')}</BasicTitle>;  
193 - }  
194 - return (  
195 - <Row type="flex" align="middle" class={`${prefixCls}__detail-header`}>  
196 - {() => (  
197 - <>  
198 - {props.showDetailBack && (  
199 - <Button size="small" type="link" onClick={onClose}>  
200 - {() => <LeftOutlined />}  
201 - </Button>  
202 - )}  
203 - {title && (  
204 - <Col style="flex:1" class={[`${prefixCls}__detail-title`, 'ellipsis', 'px-2']}>  
205 - {() => title}  
206 - </Col>  
207 - )}  
208 - {getSlot(slots, 'titleToolbar')}  
209 - </>  
210 - )}  
211 - </Row>  
212 - );  
213 - }  
214 -  
215 - const drawerInstance: DrawerInstance = {  
216 - setDrawerProps: setDrawerProps,  
217 - };  
218 -  
219 - tryTsxEmit((instance) => {  
220 - emit('register', drawerInstance, instance.uid);  
221 - });  
222 -  
223 - return () => {  
224 - return (  
225 - <Drawer class={prefixCls} onClose={onClose} {...unref(getBindValues)}>  
226 - {{  
227 - title: () => renderHeader(),  
228 - default: () => (  
229 - <>  
230 - <div ref={scrollRef} style={unref(getScrollContentStyle)}>  
231 - <Loading  
232 - absolute  
233 - tip={t('component.drawer.loadingText')}  
234 - loading={unref(getLoading)}  
235 - />  
236 - {getSlot(slots)}  
237 - </div>  
238 - {renderFooter()}  
239 - </>  
240 - ),  
241 - }}  
242 - </Drawer>  
243 - );  
244 - };  
245 - },  
246 -});  
src/components/Drawer/src/BasicDrawer.vue 0 → 100644
  1 +<template>
  2 + <Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues">
  3 + <template #title v-if="!$slots.title">
  4 + <DrawerHeader
  5 + :title="getMergeProps.title"
  6 + :isDetail="isDetail"
  7 + :showDetailBack="showDetailBack"
  8 + @close="onClose"
  9 + >
  10 + <template #titleToolbar>
  11 + <slot name="titleToolbar" />
  12 + </template>
  13 + </DrawerHeader>
  14 + </template>
  15 +
  16 + <ScrollContainer
  17 + :style="getScrollContentStyle"
  18 + v-loading="getLoading"
  19 + :loading-tip="loadingText || t('component.drawer.loadingText')"
  20 + >
  21 + <slot />
  22 + </ScrollContainer>
  23 + <DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
  24 + <template #[item]="data" v-for="item in Object.keys($slots)">
  25 + <slot :name="item" v-bind="data" />
  26 + </template>
  27 + </DrawerFooter>
  28 + </Drawer>
  29 +</template>
  30 +<script lang="ts">
  31 + import type { DrawerInstance, DrawerProps } from './types';
  32 + import type { CSSProperties } from 'vue';
  33 +
  34 + import {
  35 + defineComponent,
  36 + ref,
  37 + computed,
  38 + watchEffect,
  39 + watch,
  40 + unref,
  41 + nextTick,
  42 + toRaw,
  43 + getCurrentInstance,
  44 + } from 'vue';
  45 + import { Drawer } from 'ant-design-vue';
  46 +
  47 + import { useI18n } from '/@/hooks/web/useI18n';
  48 +
  49 + import { isFunction, isNumber } from '/@/utils/is';
  50 + import { deepMerge } from '/@/utils';
  51 + import DrawerFooter from './components/DrawerFooter.vue';
  52 + import DrawerHeader from './components/DrawerHeader.vue';
  53 + import { ScrollContainer } from '/@/components/Container';
  54 +
  55 + import { basicProps } from './props';
  56 + import { useDesign } from '/@/hooks/web/useDesign';
  57 + import { useAttrs } from '/@/hooks/core/useAttrs';
  58 +
  59 + export default defineComponent({
  60 + inheritAttrs: false,
  61 + components: { Drawer, ScrollContainer, DrawerFooter, DrawerHeader },
  62 + props: basicProps,
  63 + emits: ['visible-change', 'ok', 'close', 'register'],
  64 + setup(props, { emit }) {
  65 + const visibleRef = ref(false);
  66 + const attrs = useAttrs();
  67 + const propsRef = ref<Partial<Nullable<DrawerProps>>>(null);
  68 +
  69 + const { t } = useI18n();
  70 + const { prefixVar, prefixCls } = useDesign('basic-drawer');
  71 +
  72 + const drawerInstance: DrawerInstance = {
  73 + setDrawerProps: setDrawerProps,
  74 + emitVisible: undefined,
  75 + };
  76 +
  77 + const instance = getCurrentInstance();
  78 +
  79 + instance && emit('register', drawerInstance, instance.uid);
  80 +
  81 + const getMergeProps = computed(
  82 + (): DrawerProps => {
  83 + return deepMerge(toRaw(props), unref(propsRef));
  84 + }
  85 + );
  86 +
  87 + const getProps = computed(
  88 + (): DrawerProps => {
  89 + const opt = {
  90 + placement: 'right',
  91 + ...unref(attrs),
  92 + ...unref(getMergeProps),
  93 + visible: unref(visibleRef),
  94 + };
  95 + opt.title = undefined;
  96 + const { isDetail, width, wrapClassName, getContainer } = opt;
  97 + if (isDetail) {
  98 + if (!width) {
  99 + opt.width = '100%';
  100 + }
  101 + const detailCls = `${prefixCls}__detail`;
  102 + opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
  103 +
  104 + if (!getContainer) {
  105 + // TODO type error?
  106 + opt.getContainer = `.${prefixVar}-layout-content` as any;
  107 + }
  108 + }
  109 + return opt as DrawerProps;
  110 + }
  111 + );
  112 +
  113 + const getBindValues = computed(
  114 + (): DrawerProps => {
  115 + return {
  116 + ...attrs,
  117 + ...unref(getProps),
  118 + };
  119 + }
  120 + );
  121 +
  122 + // Custom implementation of the bottom button,
  123 + const getFooterHeight = computed(() => {
  124 + const { footerHeight, showFooter } = unref(getProps);
  125 + if (showFooter && footerHeight) {
  126 + return isNumber(footerHeight)
  127 + ? `${footerHeight}px`
  128 + : `${footerHeight.replace('px', '')}px`;
  129 + }
  130 + return `0px`;
  131 + });
  132 +
  133 + const getScrollContentStyle = computed(
  134 + (): CSSProperties => {
  135 + const footerHeight = unref(getFooterHeight);
  136 + return {
  137 + position: 'relative',
  138 + height: `calc(100% - ${footerHeight})`,
  139 + };
  140 + }
  141 + );
  142 +
  143 + const getLoading = computed(() => {
  144 + return !!unref(getProps)?.loading;
  145 + });
  146 +
  147 + watchEffect(() => {
  148 + visibleRef.value = props.visible;
  149 + });
  150 +
  151 + watch(
  152 + () => visibleRef.value,
  153 + (visible) => {
  154 + nextTick(() => {
  155 + emit('visible-change', visible);
  156 + instance && drawerInstance.emitVisible?.(visible, instance.uid);
  157 + });
  158 + }
  159 + );
  160 +
  161 + // Cancel event
  162 + async function onClose(e: Recordable) {
  163 + const { closeFunc } = unref(getProps);
  164 + emit('close', e);
  165 + if (closeFunc && isFunction(closeFunc)) {
  166 + const res = await closeFunc();
  167 + visibleRef.value = !res;
  168 + return;
  169 + }
  170 + visibleRef.value = false;
  171 + }
  172 +
  173 + function setDrawerProps(props: Partial<DrawerProps>): void {
  174 + // Keep the last setDrawerProps
  175 + propsRef.value = deepMerge(unref(propsRef) || {}, props);
  176 +
  177 + if (Reflect.has(props, 'visible')) {
  178 + visibleRef.value = !!props.visible;
  179 + }
  180 + }
  181 +
  182 + function handleOk() {
  183 + emit('ok');
  184 + }
  185 +
  186 + return {
  187 + onClose,
  188 + t,
  189 + prefixCls,
  190 + getMergeProps,
  191 + getScrollContentStyle,
  192 + getProps,
  193 + getLoading,
  194 + getBindValues,
  195 + getFooterHeight,
  196 + handleOk,
  197 + };
  198 + },
  199 + });
  200 +</script>
  201 +<style lang="less">
  202 + @import (reference) '../../../design/index.less';
  203 + @header-height: 60px;
  204 + @detail-header-height: 40px;
  205 + @prefix-cls: ~'@{namespace}-basic-drawer';
  206 + @prefix-cls-detail: ~'@{namespace}-basic-drawer__detail';
  207 +
  208 + .@{prefix-cls} {
  209 + .ant-drawer-wrapper-body {
  210 + overflow: hidden;
  211 + }
  212 +
  213 + .ant-drawer-close {
  214 + &:hover {
  215 + color: @error-color;
  216 + }
  217 + }
  218 +
  219 + .ant-drawer-body {
  220 + height: calc(100% - @header-height);
  221 + padding: 0;
  222 + background-color: @background-color-dark;
  223 +
  224 + .scrollbar__wrap {
  225 + padding: 16px !important;
  226 + margin-bottom: 0 !important;
  227 + }
  228 + }
  229 + }
  230 +
  231 + .@{prefix-cls-detail} {
  232 + position: absolute;
  233 +
  234 + .ant-drawer-header {
  235 + width: 100%;
  236 + height: @detail-header-height;
  237 + padding: 0;
  238 + border-top: 1px solid @border-color-base;
  239 + box-sizing: border-box;
  240 + }
  241 +
  242 + .ant-drawer-title {
  243 + height: 100%;
  244 + }
  245 +
  246 + .ant-drawer-close {
  247 + height: @detail-header-height;
  248 + line-height: @detail-header-height;
  249 + }
  250 +
  251 + .scrollbar__wrap {
  252 + padding: 0 !important;
  253 + }
  254 +
  255 + .ant-drawer-body {
  256 + height: calc(100% - @detail-header-height);
  257 + }
  258 + }
  259 +</style>
src/components/Drawer/src/components/DrawerFooter.vue 0 → 100644
  1 +<template>
  2 + <div :class="prefixCls" :style="getStyle" v-if="showFooter || $slots.footer">
  3 + <template v-if="!$slots.footer">
  4 + <slot name="insertFooter" />
  5 + <a-button v-bind="cancelButtonProps" @click="handleClose" class="mr-2" v-if="showCancelBtn">
  6 + {{ cancelText }}
  7 + </a-button>
  8 + <slot name="centerFooter" />
  9 + <a-button
  10 + :type="okType"
  11 + @click="handleOk"
  12 + v-bind="okButtonProps"
  13 + class="mr-2"
  14 + :loading="confirmLoading"
  15 + v-if="showOkBtn"
  16 + >
  17 + {{ okText }}
  18 + </a-button>
  19 + <slot name="appendFooter" />
  20 + </template>
  21 +
  22 + <template v-else>
  23 + <slot name="footer" />
  24 + </template>
  25 + </div>
  26 +</template>
  27 +<script lang="ts">
  28 + import type { CSSProperties } from 'vue';
  29 + import { defineComponent, computed } from 'vue';
  30 + import { useDesign } from '/@/hooks/web/useDesign';
  31 +
  32 + import { footerProps } from '../props';
  33 + export default defineComponent({
  34 + name: 'BasicDrawerFooter',
  35 + props: {
  36 + ...footerProps,
  37 + height: {
  38 + type: String,
  39 + default: '60px',
  40 + },
  41 + },
  42 + emits: ['ok', 'close'],
  43 + setup(props, { emit }) {
  44 + const { prefixCls } = useDesign('basic-drawer-footer');
  45 +
  46 + const getStyle = computed(
  47 + (): CSSProperties => {
  48 + const heightStr = `${props.height}`;
  49 + return {
  50 + height: heightStr,
  51 + lineHeight: heightStr,
  52 + };
  53 + }
  54 + );
  55 +
  56 + function handleOk() {
  57 + emit('ok');
  58 + }
  59 +
  60 + function handleClose() {
  61 + emit('close');
  62 + }
  63 + return { handleOk, prefixCls, handleClose, getStyle };
  64 + },
  65 + });
  66 +</script>
  67 +
  68 +<style lang="less">
  69 + @import (reference) '../../../../design/index.less';
  70 + @prefix-cls: ~'@{namespace}-basic-drawer-footer';
  71 + @footer-height: 60px;
  72 + .@{prefix-cls} {
  73 + position: absolute;
  74 + bottom: 0;
  75 + width: 100%;
  76 + padding: 0 12px 0 20px;
  77 + text-align: right;
  78 + background: #fff;
  79 + border-top: 1px solid @border-color-base;
  80 +
  81 + > * {
  82 + margin-right: 8px;
  83 + }
  84 + }
  85 +</style>
src/components/Drawer/src/components/DrawerHeader.vue 0 → 100644
  1 +<template>
  2 + <BasicTitle v-if="!isDetail" :class="prefixCls">
  3 + <slot name="title" />
  4 + {{ !$slots.title ? title : '' }}
  5 + </BasicTitle>
  6 +
  7 + <div :class="[prefixCls, `${prefixCls}--detail`]" v-else>
  8 + <span :class="`${prefixCls}__twrap`">
  9 + <span @click="handleClose" v-if="showDetailBack">
  10 + <ArrowLeftOutlined :class="`${prefixCls}__back`" />
  11 + </span>
  12 + <span v-if="title">{{ title }}</span>
  13 + </span>
  14 +
  15 + <span :class="`${prefixCls}__toolbar`">
  16 + <slot name="titleToolbar" />
  17 + </span>
  18 + </div>
  19 +</template>
  20 +<script lang="ts">
  21 + import { defineComponent } from 'vue';
  22 + import { BasicTitle } from '/@/components/Basic';
  23 + import { ArrowLeftOutlined } from '@ant-design/icons-vue';
  24 +
  25 + import { useDesign } from '/@/hooks/web/useDesign';
  26 +
  27 + import { propTypes } from '/@/utils/propTypes';
  28 + export default defineComponent({
  29 + name: 'BasicDrawerHeader',
  30 + components: { BasicTitle, ArrowLeftOutlined },
  31 + props: {
  32 + isDetail: propTypes.bool,
  33 + showDetailBack: propTypes.bool,
  34 + title: propTypes.string,
  35 + },
  36 + setup(_, { emit }) {
  37 + const { prefixCls } = useDesign('basic-drawer-header');
  38 +
  39 + function handleClose() {
  40 + emit('close');
  41 + }
  42 + return { prefixCls, handleClose };
  43 + },
  44 + });
  45 +</script>
  46 +
  47 +<style lang="less">
  48 + @import (reference) '../../../../design/index.less';
  49 + @prefix-cls: ~'@{namespace}-basic-drawer-header';
  50 + @footer-height: 60px;
  51 + .@{prefix-cls} {
  52 + display: flex;
  53 + height: 100%;
  54 + align-items: center;
  55 +
  56 + &__back {
  57 + padding: 0 12px;
  58 + cursor: pointer;
  59 +
  60 + &:hover {
  61 + color: @primary-color;
  62 + }
  63 + }
  64 +
  65 + &__twrap {
  66 + flex: 1;
  67 + }
  68 +
  69 + &__toolbar {
  70 + padding-right: 50px;
  71 + }
  72 + }
  73 +</style>
src/components/Drawer/src/index.less deleted 100644 → 0
1 -@import (reference) '../../../design/index.less';  
2 -@header-height: 40px;  
3 -@footer-height: 60px;  
4 -  
5 -.basic-drawer {  
6 - .ant-drawer-wrapper-body {  
7 - overflow: hidden;  
8 - }  
9 -  
10 - .ant-drawer-close {  
11 - &:hover {  
12 - color: @error-color;  
13 - }  
14 - }  
15 -  
16 - .ant-drawer-body {  
17 - height: calc(100% - @header-height);  
18 - padding: 0;  
19 - background-color: @background-color-dark;  
20 -  
21 - .scrollbar__wrap {  
22 - padding: 16px;  
23 - }  
24 - }  
25 -  
26 - &__detail {  
27 - position: absolute;  
28 -  
29 - &-header {  
30 - height: 100%;  
31 - }  
32 -  
33 - .ant-drawer-header {  
34 - width: 100%;  
35 - height: @header-height;  
36 - padding: 0;  
37 - border-top: 1px solid @border-color-base;  
38 - box-sizing: border-box;  
39 - }  
40 -  
41 - .ant-drawer-title {  
42 - height: 100%;  
43 - }  
44 -  
45 - .ant-drawer-close {  
46 - height: @header-height;  
47 - line-height: @header-height;  
48 - }  
49 -  
50 - .scrollbar__wrap {  
51 - padding: 0;  
52 - }  
53 - }  
54 -  
55 - &__footer {  
56 - position: absolute;  
57 - bottom: 0;  
58 - width: 100%;  
59 - height: @footer-height;  
60 - padding: 0 26px;  
61 - line-height: @footer-height;  
62 - text-align: right;  
63 - background: #fff;  
64 - border-top: 1px solid @border-color-base;  
65 - }  
66 -}  
src/components/Drawer/src/props.ts
@@ -10,13 +10,13 @@ export const footerProps = { @@ -10,13 +10,13 @@ export const footerProps = {
10 * @description: Show close button 10 * @description: Show close button
11 */ 11 */
12 showCancelBtn: propTypes.bool.def(true), 12 showCancelBtn: propTypes.bool.def(true),
13 - cancelButtonProps: Object as PropType<any>, 13 + cancelButtonProps: Object as PropType<Recordable>,
14 cancelText: propTypes.string.def(t('component.drawer.cancelText')), 14 cancelText: propTypes.string.def(t('component.drawer.cancelText')),
15 /** 15 /**
16 * @description: Show confirmation button 16 * @description: Show confirmation button
17 */ 17 */
18 showOkBtn: propTypes.bool.def(true), 18 showOkBtn: propTypes.bool.def(true),
19 - okButtonProps: propTypes.any, 19 + okButtonProps: Object as PropType<Recordable>,
20 okText: propTypes.string.def(t('component.drawer.okText')), 20 okText: propTypes.string.def(t('component.drawer.okText')),
21 okType: propTypes.string.def('primary'), 21 okType: propTypes.string.def('primary'),
22 showFooter: propTypes.bool, 22 showFooter: propTypes.bool,
@@ -28,6 +28,7 @@ export const footerProps = { @@ -28,6 +28,7 @@ export const footerProps = {
28 export const basicProps = { 28 export const basicProps = {
29 isDetail: propTypes.bool, 29 isDetail: propTypes.bool,
30 title: propTypes.string.def(''), 30 title: propTypes.string.def(''),
  31 + loadingText: propTypes.string,
31 showDetailBack: propTypes.bool.def(true), 32 showDetailBack: propTypes.bool.def(true),
32 visible: propTypes.bool, 33 visible: propTypes.bool,
33 loading: propTypes.bool, 34 loading: propTypes.bool,
src/components/Drawer/src/types.ts
1 import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes'; 1 import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes';
2 -import type { CSSProperties, VNodeChild } from 'vue'; 2 +import type { CSSProperties, VNodeChild, ComputedRef } from 'vue';
3 import type { ScrollContainerOptions } from '/@/components/Container/index'; 3 import type { ScrollContainerOptions } from '/@/components/Container/index';
4 4
5 export interface DrawerInstance { 5 export interface DrawerInstance {
6 setDrawerProps: (props: Partial<DrawerProps> | boolean) => void; 6 setDrawerProps: (props: Partial<DrawerProps> | boolean) => void;
  7 + emitVisible?: (visible: boolean, uid: number) => void;
7 } 8 }
8 9
9 export interface ReturnMethods extends DrawerInstance { 10 export interface ReturnMethods extends DrawerInstance {
10 openDrawer: <T = any>(visible?: boolean, data?: T, openOnSet?: boolean) => void; 11 openDrawer: <T = any>(visible?: boolean, data?: T, openOnSet?: boolean) => void;
  12 + getVisible?: ComputedRef<boolean>;
11 } 13 }
12 14
13 export type RegisterFn = (drawerInstance: DrawerInstance, uuid?: string) => void; 15 export type RegisterFn = (drawerInstance: DrawerInstance, uuid?: string) => void;
@@ -16,6 +18,7 @@ export interface ReturnInnerMethods extends DrawerInstance { @@ -16,6 +18,7 @@ export interface ReturnInnerMethods extends DrawerInstance {
16 closeDrawer: () => void; 18 closeDrawer: () => void;
17 changeLoading: (loading: boolean) => void; 19 changeLoading: (loading: boolean) => void;
18 changeOkLoading: (loading: boolean) => void; 20 changeOkLoading: (loading: boolean) => void;
  21 + getVisible?: ComputedRef<boolean>;
19 } 22 }
20 23
21 export type UseDrawerReturnType = [RegisterFn, ReturnMethods]; 24 export type UseDrawerReturnType = [RegisterFn, ReturnMethods];
src/components/Drawer/src/useDrawer.ts
@@ -6,22 +6,32 @@ import type { @@ -6,22 +6,32 @@ import type {
6 UseDrawerInnerReturnType, 6 UseDrawerInnerReturnType,
7 } from './types'; 7 } from './types';
8 8
9 -import { ref, getCurrentInstance, unref, reactive, watchEffect, nextTick, toRaw } from 'vue'; 9 +import {
  10 + ref,
  11 + getCurrentInstance,
  12 + unref,
  13 + reactive,
  14 + watchEffect,
  15 + nextTick,
  16 + toRaw,
  17 + computed,
  18 +} from 'vue';
10 19
11 import { isProdMode } from '/@/utils/env'; 20 import { isProdMode } from '/@/utils/env';
12 import { isFunction } from '/@/utils/is'; 21 import { isFunction } from '/@/utils/is';
13 -import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; 22 +import { tryOnUnmounted, isInSetup } from '/@/utils/helper/vueHelper';
14 import { isEqual } from 'lodash-es'; 23 import { isEqual } from 'lodash-es';
  24 +import { error } from '/@/utils/log';
15 25
16 const dataTransferRef = reactive<any>({}); 26 const dataTransferRef = reactive<any>({});
17 27
  28 +const visibleData = reactive<{ [key: number]: boolean }>({});
  29 +
18 /** 30 /**
19 * @description: Applicable to separate drawer and call outside 31 * @description: Applicable to separate drawer and call outside
20 */ 32 */
21 export function useDrawer(): UseDrawerReturnType { 33 export function useDrawer(): UseDrawerReturnType {
22 - if (!getCurrentInstance()) {  
23 - throw new Error('Please put useDrawer function in the setup function!');  
24 - } 34 + isInSetup();
25 35
26 const drawerRef = ref<DrawerInstance | null>(null); 36 const drawerRef = ref<DrawerInstance | null>(null);
27 const loadedRef = ref<Nullable<boolean>>(false); 37 const loadedRef = ref<Nullable<boolean>>(false);
@@ -41,23 +51,31 @@ export function useDrawer(): UseDrawerReturnType { @@ -41,23 +51,31 @@ export function useDrawer(): UseDrawerReturnType {
41 uidRef.value = uuid; 51 uidRef.value = uuid;
42 drawerRef.value = drawerInstance; 52 drawerRef.value = drawerInstance;
43 loadedRef.value = true; 53 loadedRef.value = true;
  54 +
  55 + drawerInstance.emitVisible = (visible: boolean, uid: number) => {
  56 + visibleData[uid] = visible;
  57 + };
44 } 58 }
45 59
46 const getInstance = () => { 60 const getInstance = () => {
47 const instance = unref(drawerRef); 61 const instance = unref(drawerRef);
48 if (!instance) { 62 if (!instance) {
49 - throw new Error('instance is undefined!'); 63 + error('useDrawer instance is undefined!');
50 } 64 }
51 return instance; 65 return instance;
52 }; 66 };
53 67
54 const methods: ReturnMethods = { 68 const methods: ReturnMethods = {
55 setDrawerProps: (props: Partial<DrawerProps>): void => { 69 setDrawerProps: (props: Partial<DrawerProps>): void => {
56 - getInstance().setDrawerProps(props); 70 + getInstance()?.setDrawerProps(props);
57 }, 71 },
58 72
  73 + getVisible: computed((): boolean => {
  74 + return visibleData[~~unref(uidRef)];
  75 + }),
  76 +
59 openDrawer: <T = any>(visible = true, data?: T, openOnSet = true): void => { 77 openDrawer: <T = any>(visible = true, data?: T, openOnSet = true): void => {
60 - getInstance().setDrawerProps({ 78 + getInstance()?.setDrawerProps({
61 visible: visible, 79 visible: visible,
62 }); 80 });
63 if (!data) return; 81 if (!data) return;
@@ -79,17 +97,18 @@ export function useDrawer(): UseDrawerReturnType { @@ -79,17 +97,18 @@ export function useDrawer(): UseDrawerReturnType {
79 97
80 export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => { 98 export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
81 const drawerInstanceRef = ref<Nullable<DrawerInstance>>(null); 99 const drawerInstanceRef = ref<Nullable<DrawerInstance>>(null);
82 - const currentInstall = getCurrentInstance(); 100 + const currentInstance = getCurrentInstance();
83 const uidRef = ref<string>(''); 101 const uidRef = ref<string>('');
84 102
85 - if (!currentInstall) {  
86 - throw new Error('useDrawerInner instance is undefined!'); 103 + if (!currentInstance) {
  104 + error('useDrawerInner instance is undefined!');
87 } 105 }
88 106
89 const getInstance = () => { 107 const getInstance = () => {
90 const instance = unref(drawerInstanceRef); 108 const instance = unref(drawerInstanceRef);
91 if (!instance) { 109 if (!instance) {
92 - throw new Error('useDrawerInner instance is undefined!'); 110 + error('useDrawerInner instance is undefined!');
  111 + return;
93 } 112 }
94 return instance; 113 return instance;
95 }; 114 };
@@ -102,7 +121,7 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType =&gt; { @@ -102,7 +121,7 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType =&gt; {
102 121
103 uidRef.value = uuid; 122 uidRef.value = uuid;
104 drawerInstanceRef.value = modalInstance; 123 drawerInstanceRef.value = modalInstance;
105 - currentInstall.emit('register', modalInstance, uuid); 124 + currentInstance?.emit('register', modalInstance, uuid);
106 }; 125 };
107 126
108 watchEffect(() => { 127 watchEffect(() => {
@@ -118,19 +137,22 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType =&gt; { @@ -118,19 +137,22 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType =&gt; {
118 register, 137 register,
119 { 138 {
120 changeLoading: (loading = true) => { 139 changeLoading: (loading = true) => {
121 - getInstance().setDrawerProps({ loading }); 140 + getInstance()?.setDrawerProps({ loading });
122 }, 141 },
123 142
124 changeOkLoading: (loading = true) => { 143 changeOkLoading: (loading = true) => {
125 - getInstance().setDrawerProps({ confirmLoading: loading }); 144 + getInstance()?.setDrawerProps({ confirmLoading: loading });
126 }, 145 },
  146 + getVisible: computed((): boolean => {
  147 + return visibleData[~~unref(uidRef)];
  148 + }),
127 149
128 closeDrawer: () => { 150 closeDrawer: () => {
129 - getInstance().setDrawerProps({ visible: false }); 151 + getInstance()?.setDrawerProps({ visible: false });
130 }, 152 },
131 153
132 setDrawerProps: (props: Partial<DrawerProps>) => { 154 setDrawerProps: (props: Partial<DrawerProps>) => {
133 - getInstance().setDrawerProps(props); 155 + getInstance()?.setDrawerProps(props);
134 }, 156 },
135 }, 157 },
136 ]; 158 ];
src/components/Form/src/components/FormAction.vue
1 <template> 1 <template>
2 - <a-col  
3 - v-bind="actionColOpt"  
4 - class="mb-2"  
5 - :style="{ textAlign: 'right' }"  
6 - v-if="showActionButtonGroup"  
7 - > 2 + <a-col v-bind="actionColOpt" :style="{ textAlign: 'right' }" v-if="showActionButtonGroup">
8 <FormItem> 3 <FormItem>
9 <slot name="resetBefore" /> 4 <slot name="resetBefore" />
10 <Button 5 <Button
src/components/Modal/index.ts
1 import './src/index.less'; 1 import './src/index.less';
2 import { withInstall } from '../util'; 2 import { withInstall } from '../util';
3 -import BasicModal from './src/BasicModal'; 3 +import BasicModal from './src/BasicModal.vue';
4 4
5 withInstall(BasicModal); 5 withInstall(BasicModal);
6 6
7 export { BasicModal }; 7 export { BasicModal };
8 -export { useModalContext } from './src/useModalContext';  
9 -export { useModal, useModalInner } from './src/useModal'; 8 +export { useModalContext } from './src/hooks/useModalContext';
  9 +export { useModal, useModalInner } from './src/hooks/useModal';
10 export * from './src/types'; 10 export * from './src/types';
src/components/Modal/src/BasicModal.tsx deleted 100644 → 0
1 -import type { ModalProps, ModalMethods } from './types';  
2 -  
3 -import { defineComponent, computed, ref, watch, unref, watchEffect, toRef } from 'vue';  
4 -  
5 -import Modal from './Modal';  
6 -import { Button } from '/@/components/Button';  
7 -import ModalWrapper from './ModalWrapper';  
8 -import { BasicTitle } from '/@/components/Basic';  
9 -import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';  
10 -  
11 -import { getSlot, extendSlots } from '/@/utils/helper/tsxHelper';  
12 -import { isFunction } from '/@/utils/is';  
13 -import { deepMerge } from '/@/utils';  
14 -import { tryTsxEmit } from '/@/utils/helper/vueHelper';  
15 -  
16 -import { basicProps } from './props';  
17 -import { useFullScreen } from './useFullScreen';  
18 -export default defineComponent({  
19 - name: 'BasicModal',  
20 - props: basicProps,  
21 - emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register'],  
22 - setup(props, { slots, emit, attrs }) {  
23 - const visibleRef = ref(false);  
24 - const propsRef = ref<Partial<ModalProps> | null>(null);  
25 - const modalWrapperRef = ref<ComponentRef>(null);  
26 - // modal Bottom and top height  
27 - const extHeightRef = ref(0);  
28 - // Unexpanded height of the popup  
29 -  
30 - // Custom title component: get title  
31 - const getMergeProps = computed(  
32 - (): ModalProps => {  
33 - return {  
34 - ...props,  
35 - ...(unref(propsRef) as any),  
36 - };  
37 - }  
38 - );  
39 -  
40 - const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({  
41 - modalWrapperRef,  
42 - extHeightRef,  
43 - wrapClassName: toRef(getMergeProps.value, 'wrapClassName'),  
44 - });  
45 -  
46 - // modal component does not need title  
47 - const getProps = computed(  
48 - (): ModalProps => {  
49 - const opt = {  
50 - ...unref(getMergeProps),  
51 - visible: unref(visibleRef),  
52 - title: undefined,  
53 - };  
54 -  
55 - return {  
56 - ...opt,  
57 - wrapClassName: unref(getWrapClassName),  
58 - };  
59 - }  
60 - );  
61 -  
62 - const getModalBindValue = computed((): any => {  
63 - return { ...attrs, ...unref(getProps) };  
64 - });  
65 -  
66 - watchEffect(() => {  
67 - visibleRef.value = !!props.visible;  
68 - });  
69 -  
70 - watch(  
71 - () => unref(visibleRef),  
72 - (v) => {  
73 - emit('visible-change', v);  
74 - },  
75 - {  
76 - immediate: false,  
77 - }  
78 - );  
79 -  
80 - /**  
81 - * @description: 渲染标题  
82 - */  
83 - function renderTitle() {  
84 - const { helpMessage } = unref(getProps);  
85 - const { title } = unref(getMergeProps);  
86 - return (  
87 - <BasicTitle helpMessage={helpMessage}>  
88 - {() => (slots.title ? getSlot(slots, 'title') : title)}  
89 - </BasicTitle>  
90 - );  
91 - }  
92 -  
93 - // 取消事件  
94 - async function handleCancel(e: Event) {  
95 - e?.stopPropagation();  
96 -  
97 - if (props.closeFunc && isFunction(props.closeFunc)) {  
98 - const isClose: boolean = await props.closeFunc();  
99 - visibleRef.value = !isClose;  
100 - return;  
101 - }  
102 -  
103 - visibleRef.value = false;  
104 - emit('cancel');  
105 - }  
106 -  
107 - /**  
108 - * @description: 设置modal参数  
109 - */  
110 - function setModalProps(props: Partial<ModalProps>): void {  
111 - // Keep the last setModalProps  
112 - propsRef.value = deepMerge(unref(propsRef) || {}, props);  
113 - if (!Reflect.has(props, 'visible')) return;  
114 - visibleRef.value = !!props.visible;  
115 - }  
116 -  
117 - function renderContent() {  
118 - type OmitWrapperType = Omit<  
119 - ModalProps,  
120 - 'fullScreen' | 'modalFooterHeight' | 'visible' | 'loading'  
121 - >;  
122 - const { useWrapper, loading, wrapperProps } = unref(getProps);  
123 - if (!useWrapper) return getSlot(slots);  
124 -  
125 - const showFooter = props.footer !== undefined && !props.footer ? 0 : undefined;  
126 - return (  
127 - <ModalWrapper  
128 - footerOffset={props.wrapperFooterOffset}  
129 - fullScreen={unref(fullScreenRef)}  
130 - ref={modalWrapperRef}  
131 - loading={loading}  
132 - visible={unref(visibleRef)}  
133 - modalFooterHeight={showFooter}  
134 - {...((wrapperProps as unknown) as OmitWrapperType)}  
135 - onGetExtHeight={(height: number) => {  
136 - extHeightRef.value = height;  
137 - }}  
138 - onHeightChange={(height: string) => {  
139 - emit('height-change', height);  
140 - }}  
141 - >  
142 - {() => getSlot(slots)}  
143 - </ModalWrapper>  
144 - );  
145 - }  
146 -  
147 - // 底部按钮自定义实现,  
148 - function renderFooter() {  
149 - const {  
150 - showCancelBtn,  
151 - cancelButtonProps,  
152 - cancelText,  
153 - showOkBtn,  
154 - okType,  
155 - okText,  
156 - okButtonProps,  
157 - confirmLoading,  
158 - } = unref(getProps);  
159 -  
160 - return (  
161 - <>  
162 - {getSlot(slots, 'insertFooter')}  
163 - {showCancelBtn && (  
164 - <Button {...cancelButtonProps} onClick={handleCancel}>  
165 - {() => cancelText}  
166 - </Button>  
167 - )}  
168 - {getSlot(slots, 'centerdFooter')}  
169 - {showOkBtn && (  
170 - <Button  
171 - type={okType as any}  
172 - loading={confirmLoading}  
173 - onClick={() => {  
174 - emit('ok');  
175 - }}  
176 - {...okButtonProps}  
177 - >  
178 - {() => okText}  
179 - </Button>  
180 - )}  
181 - {getSlot(slots, 'appendFooter')}  
182 - </>  
183 - );  
184 - }  
185 -  
186 - /**  
187 - * @description: 关闭按钮  
188 - */  
189 - function renderClose() {  
190 - const { canFullscreen } = unref(getProps);  
191 -  
192 - const fullScreen = unref(fullScreenRef) ? (  
193 - <FullscreenExitOutlined role="full" onClick={handleFullScreen} />  
194 - ) : (  
195 - <FullscreenOutlined role="close" onClick={handleFullScreen} />  
196 - );  
197 -  
198 - const cls = [  
199 - 'custom-close-icon',  
200 - {  
201 - 'can-full': canFullscreen,  
202 - },  
203 - ];  
204 -  
205 - return (  
206 - <div class={cls}>  
207 - {canFullscreen && fullScreen}  
208 - <CloseOutlined onClick={handleCancel} />  
209 - </div>  
210 - );  
211 - }  
212 -  
213 - const modalMethods: ModalMethods = {  
214 - setModalProps,  
215 - };  
216 -  
217 - tryTsxEmit((instance) => {  
218 - emit('register', modalMethods, instance.uid);  
219 - });  
220 - return () => (  
221 - <Modal onCancel={handleCancel} {...unref(getModalBindValue)}>  
222 - {{  
223 - footer: () => renderFooter(),  
224 - closeIcon: () => renderClose(),  
225 - title: () => renderTitle(),  
226 - ...extendSlots(slots, ['default']),  
227 - default: () => renderContent(),  
228 - }}  
229 - </Modal>  
230 - );  
231 - },  
232 -});  
src/components/Modal/src/BasicModal.vue 0 → 100644
  1 +<template>
  2 + <Modal @cancel="handleCancel" v-bind="getBindValue">
  3 + <template #closeIcon v-if="!$slots.closeIcon">
  4 + <ModalClose
  5 + :canFullscreen="getProps.canFullscreen"
  6 + :fullScreen="fullScreenRef"
  7 + @cancel="handleCancel"
  8 + @fullscreen="handleFullScreen"
  9 + />
  10 + </template>
  11 +
  12 + <template #title v-if="!$slots.title">
  13 + <ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" />
  14 + </template>
  15 +
  16 + <template #footer v-if="!$slots.footer">
  17 + <ModalFooter v-bind="getProps" @ok="handleOk" @cancel="handleCancel" />
  18 + </template>
  19 + <ModalWrapper
  20 + :useWrapper="getProps.useWrapper"
  21 + :footerOffset="wrapperFooterOffset"
  22 + :fullScreen="fullScreenRef"
  23 + ref="modalWrapperRef"
  24 + :loading="getProps.loading"
  25 + :visible="visibleRef"
  26 + :modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
  27 + v-bind="omit(getProps.wrapperProps, 'visible')"
  28 + @ext-height="handleExtHeight"
  29 + @height-change="handleHeightChange"
  30 + >
  31 + <slot />
  32 + </ModalWrapper>
  33 + </Modal>
  34 +</template>
  35 +<script lang="ts">
  36 + import type { ModalProps, ModalMethods } from './types';
  37 +
  38 + import {
  39 + defineComponent,
  40 + computed,
  41 + ref,
  42 + watch,
  43 + unref,
  44 + watchEffect,
  45 + toRef,
  46 + getCurrentInstance,
  47 + } from 'vue';
  48 +
  49 + import Modal from './components/Modal';
  50 + import ModalWrapper from './components/ModalWrapper.vue';
  51 + import ModalClose from './components/ModalClose.vue';
  52 + import ModalFooter from './components/ModalFooter.vue';
  53 + import ModalHeader from './components/ModalHeader.vue';
  54 +
  55 + import { isFunction } from '/@/utils/is';
  56 + import { deepMerge } from '/@/utils';
  57 +
  58 + import { basicProps } from './props';
  59 + import { useFullScreen } from './hooks/useModalFullScreen';
  60 + import { omit } from 'lodash-es';
  61 + export default defineComponent({
  62 + name: 'BasicModal',
  63 + components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
  64 + props: basicProps,
  65 + emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register'],
  66 + setup(props, { emit, attrs }) {
  67 + const visibleRef = ref(false);
  68 + const propsRef = ref<Partial<ModalProps> | null>(null);
  69 + const modalWrapperRef = ref<ComponentRef>(null);
  70 + // modal Bottom and top height
  71 + const extHeightRef = ref(0);
  72 + const modalMethods: ModalMethods = {
  73 + setModalProps,
  74 + emitVisible: undefined,
  75 + };
  76 + const instance = getCurrentInstance();
  77 + if (instance) {
  78 + emit('register', modalMethods, instance.uid);
  79 + }
  80 +
  81 + // Custom title component: get title
  82 + const getMergeProps = computed(
  83 + (): ModalProps => {
  84 + return {
  85 + ...props,
  86 + ...(unref(propsRef) as any),
  87 + };
  88 + }
  89 + );
  90 +
  91 + const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
  92 + modalWrapperRef,
  93 + extHeightRef,
  94 + wrapClassName: toRef(getMergeProps.value, 'wrapClassName'),
  95 + });
  96 +
  97 + // modal component does not need title
  98 + const getProps = computed(
  99 + (): ModalProps => {
  100 + const opt = {
  101 + ...unref(getMergeProps),
  102 + visible: unref(visibleRef),
  103 + title: undefined,
  104 + };
  105 +
  106 + return {
  107 + ...opt,
  108 + wrapClassName: unref(getWrapClassName),
  109 + };
  110 + }
  111 + );
  112 +
  113 + const getBindValue = computed((): any => {
  114 + return { ...attrs, ...unref(getProps) };
  115 + });
  116 +
  117 + watchEffect(() => {
  118 + visibleRef.value = !!props.visible;
  119 + });
  120 +
  121 + watch(
  122 + () => unref(visibleRef),
  123 + (v) => {
  124 + emit('visible-change', v);
  125 + instance && modalMethods.emitVisible?.(v, instance.uid);
  126 + },
  127 + {
  128 + immediate: false,
  129 + }
  130 + );
  131 +
  132 + // 取消事件
  133 + async function handleCancel(e: Event) {
  134 + e?.stopPropagation();
  135 +
  136 + if (props.closeFunc && isFunction(props.closeFunc)) {
  137 + const isClose: boolean = await props.closeFunc();
  138 + visibleRef.value = !isClose;
  139 + return;
  140 + }
  141 +
  142 + visibleRef.value = false;
  143 + emit('cancel');
  144 + }
  145 +
  146 + /**
  147 + * @description: 设置modal参数
  148 + */
  149 + function setModalProps(props: Partial<ModalProps>): void {
  150 + // Keep the last setModalProps
  151 + propsRef.value = deepMerge(unref(propsRef) || {}, props);
  152 + if (!Reflect.has(props, 'visible')) return;
  153 + visibleRef.value = !!props.visible;
  154 + }
  155 +
  156 + function handleOk() {
  157 + emit('ok');
  158 + }
  159 +
  160 + function handleHeightChange(height: string) {
  161 + emit('height-change', height);
  162 + }
  163 +
  164 + function handleExtHeight(height: number) {
  165 + extHeightRef.value = height;
  166 + }
  167 +
  168 + return {
  169 + handleCancel,
  170 + getBindValue,
  171 + getProps,
  172 + handleFullScreen,
  173 + fullScreenRef,
  174 + getMergeProps,
  175 + handleOk,
  176 + visibleRef,
  177 + omit,
  178 + modalWrapperRef,
  179 + handleExtHeight,
  180 + handleHeightChange,
  181 + };
  182 + },
  183 + });
  184 +</script>
src/components/Modal/src/Modal.tsx renamed to src/components/Modal/src/components/Modal.tsx
1 import { Modal } from 'ant-design-vue'; 1 import { Modal } from 'ant-design-vue';
2 -import { defineComponent, toRefs } from 'vue';  
3 -import { basicProps } from './props';  
4 -import { useModalDragMove } from './useModalDrag'; 2 +import { defineComponent, toRefs, unref } from 'vue';
  3 +import { basicProps } from '../props';
  4 +import { useModalDragMove } from '../hooks/useModalDrag';
  5 +import { useAttrs } from '/@/hooks/core/useAttrs';
5 import { extendSlots } from '/@/utils/helper/tsxHelper'; 6 import { extendSlots } from '/@/utils/helper/tsxHelper';
6 7
7 export default defineComponent({ 8 export default defineComponent({
8 name: 'Modal', 9 name: 'Modal',
9 inheritAttrs: false, 10 inheritAttrs: false,
10 props: basicProps, 11 props: basicProps,
11 - setup(props, { attrs, slots }) { 12 + setup(props, { slots }) {
12 const { visible, draggable, destroyOnClose } = toRefs(props); 13 const { visible, draggable, destroyOnClose } = toRefs(props);
13 - 14 + const attrs = useAttrs();
14 useModalDragMove({ 15 useModalDragMove({
15 visible, 16 visible,
16 destroyOnClose, 17 destroyOnClose,
@@ -18,7 +19,8 @@ export default defineComponent({ @@ -18,7 +19,8 @@ export default defineComponent({
18 }); 19 });
19 20
20 return () => { 21 return () => {
21 - const propsData = { ...attrs, ...props } as any; 22 + const propsData = { ...unref(attrs), ...props } as Recordable;
  23 +
22 return <Modal {...propsData}>{extendSlots(slots)}</Modal>; 24 return <Modal {...propsData}>{extendSlots(slots)}</Modal>;
23 }; 25 };
24 }, 26 },
src/components/Modal/src/components/ModalClose.vue 0 → 100644
  1 +<template>
  2 + <div :class="getClass">
  3 + <template v-if="canFullscreen">
  4 + <FullscreenExitOutlined role="full" @click="handleFullScreen" v-if="fullScreen" />
  5 +
  6 + <FullscreenOutlined role="close" @click="handleFullScreen" v-else />
  7 + </template>
  8 + <CloseOutlined @click="handleCancel" />
  9 + </div>
  10 +</template>
  11 +<script lang="ts">
  12 + import { defineComponent, computed } from 'vue';
  13 + import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';
  14 + import { useDesign } from '/@/hooks/web/useDesign';
  15 + import { propTypes } from '/@/utils/propTypes';
  16 +
  17 + export default defineComponent({
  18 + name: 'ModalClose',
  19 + components: { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined },
  20 + props: {
  21 + canFullscreen: propTypes.bool.def(true),
  22 + fullScreen: propTypes.bool,
  23 + },
  24 + emits: ['cancel', 'fullscreen'],
  25 + setup(props, { emit }) {
  26 + const { prefixCls } = useDesign('basic-modal-close');
  27 +
  28 + const getClass = computed(() => {
  29 + return [
  30 + prefixCls,
  31 + `${prefixCls}--custom`,
  32 + {
  33 + [`${prefixCls}--can-full`]: props.canFullscreen,
  34 + },
  35 + ];
  36 + });
  37 +
  38 + function handleCancel() {
  39 + emit('cancel');
  40 + }
  41 + function handleFullScreen(e: Event) {
  42 + e?.stopPropagation();
  43 + e?.preventDefault();
  44 + emit('fullscreen');
  45 + }
  46 +
  47 + return {
  48 + getClass,
  49 + prefixCls,
  50 + handleCancel,
  51 + handleFullScreen,
  52 + };
  53 + },
  54 + });
  55 +</script>
  56 +<style lang="less">
  57 + @import (reference) '../../../../design/index.less';
  58 + @prefix-cls: ~'@{namespace}-basic-modal-close';
  59 + .@{prefix-cls} {
  60 + display: flex;
  61 + height: 95%;
  62 + align-items: center;
  63 +
  64 + > span {
  65 + margin-left: 48px;
  66 + font-size: 16px;
  67 + }
  68 +
  69 + &--can-full {
  70 + > span {
  71 + margin-left: 12px;
  72 + }
  73 + }
  74 +
  75 + &:not(&--can-full) {
  76 + > span:nth-child(1) {
  77 + &:hover {
  78 + font-weight: 700;
  79 + }
  80 + }
  81 + }
  82 +
  83 + & span:nth-child(1) {
  84 + display: inline-block;
  85 + padding: 10px;
  86 +
  87 + &:hover {
  88 + color: @primary-color;
  89 + }
  90 + }
  91 +
  92 + & span:nth-child(2) {
  93 + &:hover {
  94 + color: @error-color;
  95 + }
  96 + }
  97 + }
  98 +</style>
src/components/Modal/src/components/ModalFooter.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <slot name="insertFooter" />
  4 + <a-button v-bind="cancelButtonProps" @click="handleCancel" v-if="showCancelBtn">
  5 + {{ cancelText }}
  6 + </a-button>
  7 + <slot name="centerFooter" />
  8 + <a-button
  9 + :type="okType"
  10 + @click="handleOk"
  11 + :loading="confirmLoading"
  12 + v-bind="okButtonProps"
  13 + v-if="showOkBtn"
  14 + >
  15 + {{ okText }}
  16 + </a-button>
  17 + <slot name="appendFooter" />
  18 + </div>
  19 +</template>
  20 +<script lang="ts">
  21 + import { defineComponent } from 'vue';
  22 +
  23 + import { basicProps } from '../props';
  24 + export default defineComponent({
  25 + name: 'BasicModalFooter',
  26 + props: basicProps,
  27 + emits: ['ok', 'cancel'],
  28 + setup(_, { emit }) {
  29 + function handleOk() {
  30 + emit('ok');
  31 + }
  32 +
  33 + function handleCancel() {
  34 + emit('cancel');
  35 + }
  36 + return { handleOk, handleCancel };
  37 + },
  38 + });
  39 +</script>
src/components/Modal/src/components/ModalHeader.vue 0 → 100644
  1 +<template>
  2 + <BasicTitle :helpMessage="helpMessage">
  3 + {{ title }}
  4 + </BasicTitle>
  5 +</template>
  6 +<script lang="ts">
  7 + import type { PropType } from 'vue';
  8 + import { defineComponent } from 'vue';
  9 + import { BasicTitle } from '/@/components/Basic';
  10 +
  11 + import { propTypes } from '/@/utils/propTypes';
  12 + export default defineComponent({
  13 + name: 'BasicModalHeader',
  14 + components: { BasicTitle },
  15 + props: {
  16 + helpMessage: {
  17 + type: [String, Array] as PropType<string | string[]>,
  18 + },
  19 + title: propTypes.string,
  20 + },
  21 + });
  22 +</script>
src/components/Modal/src/ModalWrapper.tsx renamed to src/components/Modal/src/components/ModalWrapper.vue
1 -import type { ModalWrapperProps } from './types';  
2 -import type { CSSProperties } from 'vue';  
3 -  
4 -import {  
5 - defineComponent,  
6 - computed,  
7 - ref,  
8 - watchEffect,  
9 - unref,  
10 - watch,  
11 - onMounted,  
12 - nextTick,  
13 - onUnmounted,  
14 -} from 'vue';  
15 -import { Spin } from 'ant-design-vue';  
16 -  
17 -import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';  
18 -  
19 -import { getSlot } from '/@/utils/helper/tsxHelper';  
20 -import { useElResize } from '/@/hooks/event/useElResize';  
21 -import { propTypes } from '/@/utils/propTypes';  
22 -import { createModalContext } from './useModalContext';  
23 -  
24 -export default defineComponent({  
25 - name: 'ModalWrapper',  
26 - props: {  
27 - loading: propTypes.bool,  
28 - modalHeaderHeight: propTypes.number.def(50),  
29 - modalFooterHeight: propTypes.number.def(54),  
30 - minHeight: propTypes.number.def(200),  
31 - footerOffset: propTypes.number.def(0),  
32 - visible: propTypes.bool,  
33 - fullScreen: propTypes.bool,  
34 - },  
35 - emits: ['heightChange', 'getExtHeight'],  
36 - setup(props: ModalWrapperProps, { slots, emit }) {  
37 - const wrapperRef = ref<ElRef>(null);  
38 - const spinRef = ref<ComponentRef>(null);  
39 - const realHeightRef = ref(0);  
40 -  
41 - let stopElResizeFn: Fn = () => {};  
42 -  
43 - useWindowSizeFn(setModalHeight);  
44 -  
45 - createModalContext({  
46 - redoModalHeight: setModalHeight,  
47 - });  
48 -  
49 - const wrapStyle = computed(  
50 - (): CSSProperties => {  
51 - return {  
52 - minHeight: `${props.minHeight}px`,  
53 - height: `${unref(realHeightRef)}px`,  
54 - overflow: 'auto',  
55 - };  
56 - }  
57 - ); 1 +<template>
  2 + <ScrollContainer ref="wrapperRef" :style="wrapStyle">
  3 + <div ref="spinRef" :style="spinStyle" v-loading="loading" :loading-tip="loadingTip">
  4 + <slot />
  5 + </div>
  6 + </ScrollContainer>
  7 +</template>
  8 +<script lang="ts">
  9 + import type { ModalWrapperProps } from '../types';
  10 + import type { CSSProperties } from 'vue';
  11 +
  12 + import {
  13 + defineComponent,
  14 + computed,
  15 + ref,
  16 + watchEffect,
  17 + unref,
  18 + watch,
  19 + onMounted,
  20 + nextTick,
  21 + onUnmounted,
  22 + } from 'vue';
  23 + import { Spin } from 'ant-design-vue';
  24 +
  25 + import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
  26 + import { ScrollContainer } from '/@/components/Container';
  27 +
  28 + // import { useElResize } from '/@/hooks/event/useElResize';
  29 + import { propTypes } from '/@/utils/propTypes';
  30 + import { createModalContext } from '../hooks/useModalContext';
  31 +
  32 + export default defineComponent({
  33 + name: 'ModalWrapper',
  34 + components: { Spin, ScrollContainer },
  35 + props: {
  36 + loading: propTypes.bool,
  37 + useWrapper: propTypes.bool.def(true),
  38 + modalHeaderHeight: propTypes.number.def(50),
  39 + modalFooterHeight: propTypes.number.def(54),
  40 + minHeight: propTypes.number.def(200),
  41 + footerOffset: propTypes.number.def(0),
  42 + visible: propTypes.bool,
  43 + fullScreen: propTypes.bool,
  44 + loadingTip: propTypes.string,
  45 + },
  46 + emits: ['height-change', 'ext-height'],
  47 + setup(props: ModalWrapperProps, { emit }) {
  48 + const wrapperRef = ref<ComponentRef>(null);
  49 + const spinRef = ref<ElRef>(null);
  50 + const realHeightRef = ref(0);
  51 +
  52 + let stopElResizeFn: Fn = () => {};
  53 +
  54 + useWindowSizeFn(setModalHeight);
  55 +
  56 + createModalContext({
  57 + redoModalHeight: setModalHeight,
  58 + });
58 59
59 - watchEffect(() => {  
60 - setModalHeight();  
61 - }); 60 + const wrapStyle = computed(
  61 + (): CSSProperties => {
  62 + return {
  63 + minHeight: `${props.minHeight}px`,
  64 + height: `${unref(realHeightRef)}px`,
  65 + // overflow: 'auto',
  66 + };
  67 + }
  68 + );
62 69
63 - watch(  
64 - () => props.fullScreen,  
65 - (v) => {  
66 - !v && setModalHeight();  
67 - }  
68 - );  
69 -  
70 - onMounted(() => {  
71 - const { modalHeaderHeight, modalFooterHeight } = props;  
72 - emit('getExtHeight', modalHeaderHeight + modalFooterHeight);  
73 - listenElResize();  
74 - });  
75 -  
76 - onUnmounted(() => {  
77 - stopElResizeFn && stopElResizeFn();  
78 - });  
79 -  
80 - async function setModalHeight() {  
81 - // 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度  
82 - // 加上这个,就必须在使用的时候传递父级的visible  
83 - if (!props.visible) return;  
84 - const wrapperRefDom = unref(wrapperRef);  
85 - if (!wrapperRefDom) return;  
86 - const bodyDom = wrapperRefDom.parentElement;  
87 - if (!bodyDom) return;  
88 - bodyDom.style.padding = '0';  
89 - await nextTick();  
90 -  
91 - try {  
92 - const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;  
93 - if (!modalDom) return;  
94 -  
95 - const modalRect = getComputedStyle(modalDom).top;  
96 - const modalTop = Number.parseInt(modalRect);  
97 - let maxHeight =  
98 - window.innerHeight -  
99 - modalTop * 2 +  
100 - (props.footerOffset! || 0) -  
101 - props.modalFooterHeight -  
102 - props.modalHeaderHeight;  
103 -  
104 - // 距离顶部过进会出现滚动条  
105 - if (modalTop < 40) {  
106 - maxHeight -= 26; 70 + const spinStyle = computed(
  71 + (): CSSProperties => {
  72 + return {
  73 + // padding 28
  74 + height: `${unref(realHeightRef) - 28}px`,
  75 + };
107 } 76 }
108 - await nextTick();  
109 - const spinEl = unref(spinRef); 77 + );
  78 +
  79 + watchEffect(() => {
  80 + props.useWrapper && setModalHeight();
  81 + });
110 82
111 - if (!spinEl) return; 83 + watch(
  84 + () => props.fullScreen,
  85 + () => {
  86 + setTimeout(() => {
  87 + setModalHeight();
  88 + }, 0);
  89 + }
  90 + );
112 91
113 - const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement;  
114 - if (!spinContainerEl) return; 92 + onMounted(() => {
  93 + const { modalHeaderHeight, modalFooterHeight } = props;
  94 + emit('ext-height', modalHeaderHeight + modalFooterHeight);
  95 + // listenElResize();
  96 + });
115 97
116 - const realHeight = spinContainerEl.scrollHeight; 98 + onUnmounted(() => {
  99 + stopElResizeFn && stopElResizeFn();
  100 + });
117 101
118 - if (props.fullScreen) {  
119 - realHeightRef.value =  
120 - window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight;  
121 - } else {  
122 - realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30;  
123 - }  
124 - emit('heightChange', unref(realHeightRef)); 102 + async function setModalHeight() {
  103 + // 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
  104 + // 加上这个,就必须在使用的时候传递父级的visible
  105 + if (!props.visible) return;
  106 + const wrapperRefDom = unref(wrapperRef);
  107 + if (!wrapperRefDom) return;
  108 + const bodyDom = wrapperRefDom.$el.parentElement;
  109 + if (!bodyDom) return;
  110 + bodyDom.style.padding = '0';
  111 + await nextTick();
125 112
126 - nextTick(() => {  
127 - const el = spinEl.$el;  
128 - if (el) {  
129 - el.style.height = `${unref(realHeightRef)}px`; 113 + try {
  114 + const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;
  115 + if (!modalDom) return;
  116 +
  117 + const modalRect = getComputedStyle(modalDom).top;
  118 + const modalTop = Number.parseInt(modalRect);
  119 + let maxHeight =
  120 + window.innerHeight -
  121 + modalTop * 2 +
  122 + (props.footerOffset! || 0) -
  123 + props.modalFooterHeight -
  124 + props.modalHeaderHeight;
  125 +
  126 + // 距离顶部过进会出现滚动条
  127 + if (modalTop < 40) {
  128 + maxHeight -= 26;
130 } 129 }
131 - });  
132 - } catch (error) {  
133 - console.log(error);  
134 - }  
135 - } 130 + await nextTick();
  131 + const spinEl = unref(spinRef);
136 132
137 - function listenElResize() {  
138 - const wrapper = unref(wrapperRef);  
139 - if (!wrapper) return; 133 + if (!spinEl) return;
140 134
141 - const container = wrapper.querySelector('.ant-spin-container');  
142 - if (!container) return; 135 + const realHeight = spinEl.scrollHeight;
143 136
144 - const [start, stop] = useElResize(container, () => {  
145 - setModalHeight();  
146 - });  
147 - stopElResizeFn = stop;  
148 - start();  
149 - }  
150 -  
151 - return () => {  
152 - return (  
153 - <div ref={wrapperRef} style={unref(wrapStyle)}>  
154 - <Spin ref={spinRef} spinning={props.loading}>  
155 - {() => getSlot(slots)}  
156 - </Spin>  
157 - </div>  
158 - );  
159 - };  
160 - },  
161 -}); 137 + if (props.fullScreen) {
  138 + realHeightRef.value =
  139 + window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight;
  140 + } else {
  141 + realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30;
  142 + }
  143 + emit('height-change', unref(realHeightRef));
  144 + } catch (error) {
  145 + console.log(error);
  146 + }
  147 + }
  148 +
  149 + return { wrapStyle, wrapperRef, spinRef, spinStyle };
  150 + },
  151 + });
  152 +</script>
src/components/Modal/src/useModal.ts renamed to src/components/Modal/src/hooks/useModal.ts
@@ -4,7 +4,7 @@ import type { @@ -4,7 +4,7 @@ import type {
4 ModalProps, 4 ModalProps,
5 ReturnMethods, 5 ReturnMethods,
6 UseModalInnerReturnType, 6 UseModalInnerReturnType,
7 -} from './types'; 7 +} from '../types';
8 8
9 import { 9 import {
10 ref, 10 ref,
@@ -19,16 +19,18 @@ import { @@ -19,16 +19,18 @@ import {
19 import { isProdMode } from '/@/utils/env'; 19 import { isProdMode } from '/@/utils/env';
20 import { isFunction } from '/@/utils/is'; 20 import { isFunction } from '/@/utils/is';
21 import { isEqual } from 'lodash-es'; 21 import { isEqual } from 'lodash-es';
22 -import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; 22 +import { tryOnUnmounted, isInSetup } from '/@/utils/helper/vueHelper';
  23 +import { error } from '/@/utils/log';
  24 +import { computed } from 'vue';
23 const dataTransferRef = reactive<any>({}); 25 const dataTransferRef = reactive<any>({});
24 26
  27 +const visibleData = reactive<{ [key: number]: boolean }>({});
  28 +
25 /** 29 /**
26 * @description: Applicable to independent modal and call outside 30 * @description: Applicable to independent modal and call outside
27 */ 31 */
28 export function useModal(): UseModalReturnType { 32 export function useModal(): UseModalReturnType {
29 - if (!getCurrentInstance()) {  
30 - throw new Error('Please put useModal function in the setup function!');  
31 - } 33 + isInSetup();
32 const modalRef = ref<Nullable<ModalMethods>>(null); 34 const modalRef = ref<Nullable<ModalMethods>>(null);
33 const loadedRef = ref<Nullable<boolean>>(false); 35 const loadedRef = ref<Nullable<boolean>>(false);
34 const uidRef = ref<string>(''); 36 const uidRef = ref<string>('');
@@ -45,23 +47,29 @@ export function useModal(): UseModalReturnType { @@ -45,23 +47,29 @@ export function useModal(): UseModalReturnType {
45 if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) return; 47 if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) return;
46 48
47 modalRef.value = modalMethod; 49 modalRef.value = modalMethod;
  50 + modalMethod.emitVisible = (visible: boolean, uid: number) => {
  51 + visibleData[uid] = visible;
  52 + };
48 } 53 }
49 54
50 const getInstance = () => { 55 const getInstance = () => {
51 const instance = unref(modalRef); 56 const instance = unref(modalRef);
52 if (!instance) { 57 if (!instance) {
53 - throw new Error('instance is undefined!'); 58 + error('useModal instance is undefined!');
54 } 59 }
55 return instance; 60 return instance;
56 }; 61 };
57 62
58 const methods: ReturnMethods = { 63 const methods: ReturnMethods = {
59 setModalProps: (props: Partial<ModalProps>): void => { 64 setModalProps: (props: Partial<ModalProps>): void => {
60 - getInstance().setModalProps(props); 65 + getInstance()?.setModalProps(props);
61 }, 66 },
  67 + getVisible: computed((): boolean => {
  68 + return visibleData[~~unref(uidRef)];
  69 + }),
62 70
63 openModal: <T = any>(visible = true, data?: T, openOnSet = true): void => { 71 openModal: <T = any>(visible = true, data?: T, openOnSet = true): void => {
64 - getInstance().setModalProps({ 72 + getInstance()?.setModalProps({
65 visible: visible, 73 visible: visible,
66 }); 74 });
67 75
@@ -83,20 +91,16 @@ export function useModal(): UseModalReturnType { @@ -83,20 +91,16 @@ export function useModal(): UseModalReturnType {
83 91
84 export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { 92 export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
85 const modalInstanceRef = ref<Nullable<ModalMethods>>(null); 93 const modalInstanceRef = ref<Nullable<ModalMethods>>(null);
86 - const currentInstall = getCurrentInstance(); 94 + const currentInstance = getCurrentInstance();
87 const uidRef = ref<string>(''); 95 const uidRef = ref<string>('');
88 96
89 - if (!currentInstall) {  
90 - throw new Error('instance is undefined!');  
91 - }  
92 -  
93 // currentInstall.type.emits = [...currentInstall.type.emits, 'register']; 97 // currentInstall.type.emits = [...currentInstall.type.emits, 'register'];
94 // Object.assign(currentInstall.type.emits, ['register']); 98 // Object.assign(currentInstall.type.emits, ['register']);
95 99
96 const getInstance = () => { 100 const getInstance = () => {
97 const instance = unref(modalInstanceRef); 101 const instance = unref(modalInstanceRef);
98 if (!instance) { 102 if (!instance) {
99 - throw new Error('instance is undefined!'); 103 + error('useModalInner instance is undefined!');
100 } 104 }
101 return instance; 105 return instance;
102 }; 106 };
@@ -108,7 +112,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType =&gt; { @@ -108,7 +112,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType =&gt; {
108 }); 112 });
109 uidRef.value = uuid; 113 uidRef.value = uuid;
110 modalInstanceRef.value = modalInstance; 114 modalInstanceRef.value = modalInstance;
111 - currentInstall.emit('register', modalInstance, uuid); 115 + currentInstance?.emit('register', modalInstance, uuid);
112 }; 116 };
113 117
114 watchEffect(() => { 118 watchEffect(() => {
@@ -124,19 +128,22 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType =&gt; { @@ -124,19 +128,22 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType =&gt; {
124 register, 128 register,
125 { 129 {
126 changeLoading: (loading = true) => { 130 changeLoading: (loading = true) => {
127 - getInstance().setModalProps({ loading }); 131 + getInstance()?.setModalProps({ loading });
128 }, 132 },
  133 + getVisible: computed((): boolean => {
  134 + return visibleData[~~unref(uidRef)];
  135 + }),
129 136
130 changeOkLoading: (loading = true) => { 137 changeOkLoading: (loading = true) => {
131 - getInstance().setModalProps({ confirmLoading: loading }); 138 + getInstance()?.setModalProps({ confirmLoading: loading });
132 }, 139 },
133 140
134 closeModal: () => { 141 closeModal: () => {
135 - getInstance().setModalProps({ visible: false }); 142 + getInstance()?.setModalProps({ visible: false });
136 }, 143 },
137 144
138 setModalProps: (props: Partial<ModalProps>) => { 145 setModalProps: (props: Partial<ModalProps>) => {
139 - getInstance().setModalProps(props); 146 + getInstance()?.setModalProps(props);
140 }, 147 },
141 }, 148 },
142 ]; 149 ];
src/components/Modal/src/useModalContext.ts renamed to src/components/Modal/src/hooks/useModalContext.ts
src/components/Modal/src/useModalDrag.ts renamed to src/components/Modal/src/hooks/useModalDrag.ts
src/components/Modal/src/useFullScreen.ts renamed to src/components/Modal/src/hooks/useModalFullScreen.ts
src/components/Modal/src/index.less
@@ -21,9 +21,12 @@ @@ -21,9 +21,12 @@
21 width: 520px; 21 width: 520px;
22 padding-bottom: 0; 22 padding-bottom: 0;
23 23
24 - .ant-spin-nested-loading {  
25 - padding: 16px; 24 + .scroll-container {
  25 + padding: 14px;
26 } 26 }
  27 + // .ant-spin-nested-loading {
  28 + // padding: 16px;
  29 + // }
27 30
28 &-title { 31 &-title {
29 font-size: 16px; 32 font-size: 16px;
@@ -35,46 +38,6 @@ @@ -35,46 +38,6 @@
35 } 38 }
36 } 39 }
37 40
38 - .custom-close-icon {  
39 - display: flex;  
40 - height: 95%;  
41 - align-items: center;  
42 -  
43 - > span {  
44 - margin-left: 48px;  
45 - font-size: 16px;  
46 - }  
47 -  
48 - &.can-full {  
49 - > span {  
50 - margin-left: 12px;  
51 - }  
52 - }  
53 -  
54 - &:not(.can-full) {  
55 - > span:nth-child(1) {  
56 - &:hover {  
57 - font-weight: 700;  
58 - }  
59 - }  
60 - }  
61 -  
62 - & span:nth-child(1) {  
63 - display: inline-block;  
64 - padding: 10px;  
65 -  
66 - &:hover {  
67 - color: @primary-color;  
68 - }  
69 - }  
70 -  
71 - & span:nth-child(2) {  
72 - &:hover {  
73 - color: @error-color;  
74 - }  
75 - }  
76 - }  
77 -  
78 .ant-modal-body { 41 .ant-modal-body {
79 padding: 0; 42 padding: 0;
80 } 43 }
@@ -96,8 +59,6 @@ @@ -96,8 +59,6 @@
96 } 59 }
97 60
98 &-footer { 61 &-footer {
99 - // padding: 10px 26px 26px 16px;  
100 -  
101 button + button { 62 button + button {
102 margin-left: 10px; 63 margin-left: 10px;
103 } 64 }
src/components/Modal/src/props.ts
1 -import type { PropType } from 'vue'; 1 +import type { PropType, CSSProperties } from 'vue';
2 import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; 2 import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
3 3
4 import { useI18n } from '/@/hooks/web/useI18n'; 4 import { useI18n } from '/@/hooks/web/useI18n';
5 -import { propTypes } from '/@/utils/propTypes'; 5 +import { propTypes, VueNode } from '/@/utils/propTypes';
  6 +import type { ModalWrapperProps } from './types';
6 const { t } = useI18n(); 7 const { t } = useI18n();
7 8
8 export const modalProps = { 9 export const modalProps = {
@@ -26,6 +27,7 @@ export const basicProps = Object.assign({}, modalProps, { @@ -26,6 +27,7 @@ export const basicProps = Object.assign({}, modalProps, {
26 // Whether to setting wrapper 27 // Whether to setting wrapper
27 useWrapper: propTypes.bool.def(true), 28 useWrapper: propTypes.bool.def(true),
28 loading: propTypes.bool, 29 loading: propTypes.bool,
  30 + loadingTip: propTypes.string,
29 /** 31 /**
30 * @description: Show close button 32 * @description: Show close button
31 */ 33 */
@@ -35,65 +37,44 @@ export const basicProps = Object.assign({}, modalProps, { @@ -35,65 +37,44 @@ export const basicProps = Object.assign({}, modalProps, {
35 */ 37 */
36 showOkBtn: propTypes.bool.def(true), 38 showOkBtn: propTypes.bool.def(true),
37 39
38 - wrapperProps: Object as PropType<any>, 40 + wrapperProps: Object as PropType<Partial<ModalWrapperProps>>,
39 41
40 - afterClose: Function as PropType<() => Promise<any>>, 42 + afterClose: Function as PropType<() => Promise<VueNode>>,
41 43
42 - bodyStyle: Object as PropType<any>, 44 + bodyStyle: Object as PropType<CSSProperties>,
43 45
44 - closable: {  
45 - type: Boolean as PropType<boolean>,  
46 - default: true,  
47 - }, 46 + closable: propTypes.bool.def(true),
48 47
49 - closeIcon: Object as PropType<any>, 48 + closeIcon: Object as PropType<VueNode>,
50 49
51 - confirmLoading: Boolean as PropType<boolean>, 50 + confirmLoading: propTypes.bool,
52 51
53 - destroyOnClose: Boolean as PropType<boolean>, 52 + destroyOnClose: propTypes.bool,
54 53
55 - footer: Object as PropType<any>, 54 + footer: Object as PropType<VueNode>,
56 55
57 getContainer: Function as PropType<() => any>, 56 getContainer: Function as PropType<() => any>,
58 57
59 - mask: {  
60 - type: Boolean as PropType<boolean>,  
61 - default: true,  
62 - }, 58 + mask: propTypes.bool.def(true),
63 59
64 - maskClosable: {  
65 - type: Boolean as PropType<boolean>,  
66 - default: true,  
67 - },  
68 - keyboard: {  
69 - type: Boolean as PropType<boolean>,  
70 - default: true,  
71 - }, 60 + maskClosable: propTypes.bool.def(true),
  61 + keyboard: propTypes.bool.def(true),
72 62
73 - maskStyle: Object as PropType<any>, 63 + maskStyle: Object as PropType<CSSProperties>,
74 64
75 - okType: {  
76 - type: String as PropType<string>,  
77 - default: 'primary',  
78 - }, 65 + okType: propTypes.string.def('primary'),
79 66
80 okButtonProps: Object as PropType<ButtonProps>, 67 okButtonProps: Object as PropType<ButtonProps>,
81 68
82 cancelButtonProps: Object as PropType<ButtonProps>, 69 cancelButtonProps: Object as PropType<ButtonProps>,
83 70
84 - title: {  
85 - type: String as PropType<string>,  
86 - }, 71 + title: propTypes.string,
87 72
88 - visible: Boolean as PropType<boolean>, 73 + visible: propTypes.bool,
89 74
90 width: [String, Number] as PropType<string | number>, 75 width: [String, Number] as PropType<string | number>,
91 76
92 - wrapClassName: {  
93 - type: String as PropType<string>,  
94 - }, 77 + wrapClassName: propTypes.string,
95 78
96 - zIndex: {  
97 - type: Number as PropType<number>,  
98 - }, 79 + zIndex: propTypes.number,
99 }); 80 });
src/components/Modal/src/types.ts
1 import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes'; 1 import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes';
2 -import type { CSSProperties, VNodeChild } from 'vue'; 2 +import type { CSSProperties, VNodeChild, ComputedRef } from 'vue';
3 /** 3 /**
4 * @description: 弹窗对外暴露的方法 4 * @description: 弹窗对外暴露的方法
5 */ 5 */
6 export interface ModalMethods { 6 export interface ModalMethods {
7 setModalProps: (props: Partial<ModalProps>) => void; 7 setModalProps: (props: Partial<ModalProps>) => void;
  8 + emitVisible?: (visible: boolean, uid: number) => void;
8 } 9 }
9 10
10 export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void; 11 export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void;
11 12
12 export interface ReturnMethods extends ModalMethods { 13 export interface ReturnMethods extends ModalMethods {
13 openModal: <T = any>(props?: boolean, data?: T, openOnSet?: boolean) => void; 14 openModal: <T = any>(props?: boolean, data?: T, openOnSet?: boolean) => void;
  15 + getVisible?: ComputedRef<boolean>;
14 } 16 }
15 17
16 export type UseModalReturnType = [RegisterFn, ReturnMethods]; 18 export type UseModalReturnType = [RegisterFn, ReturnMethods];
@@ -19,6 +21,7 @@ export interface ReturnInnerMethods extends ModalMethods { @@ -19,6 +21,7 @@ export interface ReturnInnerMethods extends ModalMethods {
19 closeModal: () => void; 21 closeModal: () => void;
20 changeLoading: (loading: boolean) => void; 22 changeLoading: (loading: boolean) => void;
21 changeOkLoading: (loading: boolean) => void; 23 changeOkLoading: (loading: boolean) => void;
  24 + getVisible?: ComputedRef<boolean>;
22 } 25 }
23 26
24 export type UseModalInnerReturnType = [RegisterFn, ReturnInnerMethods]; 27 export type UseModalInnerReturnType = [RegisterFn, ReturnInnerMethods];
@@ -38,6 +41,7 @@ export interface ModalProps { @@ -38,6 +41,7 @@ export interface ModalProps {
38 useWrapper: boolean; 41 useWrapper: boolean;
39 42
40 loading: boolean; 43 loading: boolean;
  44 + loadingTip?: string;
41 45
42 wrapperProps: Omit<ModalWrapperProps, 'loading'>; 46 wrapperProps: Omit<ModalWrapperProps, 'loading'>;
43 47
@@ -193,4 +197,5 @@ export interface ModalWrapperProps { @@ -193,4 +197,5 @@ export interface ModalWrapperProps {
193 minHeight: number; 197 minHeight: number;
194 visible: boolean; 198 visible: boolean;
195 fullScreen: boolean; 199 fullScreen: boolean;
  200 + useWrapper: boolean;
196 } 201 }
src/hooks/core/useAttrs.ts
1 import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue'; 1 import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue';
2 - 2 +import type { Ref } from 'vue';
3 interface Params { 3 interface Params {
4 excludeListeners?: boolean; 4 excludeListeners?: boolean;
5 excludeKeys?: string[]; 5 excludeKeys?: string[];
@@ -12,7 +12,7 @@ export function entries&lt;T&gt;(obj: Hash&lt;T&gt;): [string, T][] { @@ -12,7 +12,7 @@ export function entries&lt;T&gt;(obj: Hash&lt;T&gt;): [string, T][] {
12 return Object.keys(obj).map((key: string) => [key, obj[key]]); 12 return Object.keys(obj).map((key: string) => [key, obj[key]]);
13 } 13 }
14 14
15 -export function useAttrs(params: Params = {}) { 15 +export function useAttrs(params: Params = {}): Ref<Recordable> | {} {
16 const instance = getCurrentInstance(); 16 const instance = getCurrentInstance();
17 if (!instance) return {}; 17 if (!instance) return {};
18 18
src/settings/projectSetting.ts
@@ -37,7 +37,7 @@ const setting: ProjectConfig = { @@ -37,7 +37,7 @@ const setting: ProjectConfig = {
37 showLogo: true, 37 showLogo: true,
38 38
39 // Whether to show footer 39 // Whether to show footer
40 - showFooter: true, 40 + showFooter: false,
41 41
42 // locale setting 42 // locale setting
43 locale: { 43 locale: {
src/utils/propTypes.ts
1 import { CSSProperties, VNodeChild } from 'vue'; 1 import { CSSProperties, VNodeChild } from 'vue';
2 import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types'; 2 import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types';
3 3
4 -type VueNode = VNodeChild | JSX.Element; 4 +export type VueNode = VNodeChild | JSX.Element;
5 5
6 type PropTypes = VueTypesInterface & { 6 type PropTypes = VueTypesInterface & {
7 readonly style: VueTypeValidableDef<CSSProperties>; 7 readonly style: VueTypeValidableDef<CSSProperties>;
src/views/demo/comp/drawer/Drawer3.vue
1 <template> 1 <template>
2 - <BasicDrawer v-bind="$attrs" title="Modal Title" width="50%" showFooter> 2 + <BasicDrawer v-bind="$attrs" title="Modal Title" width="50%" showFooter @ok="handleOk">
3 <p class="h-20" v-for="index in 40" :key="index">根据屏幕高度自适应</p> 3 <p class="h-20" v-for="index in 40" :key="index">根据屏幕高度自适应</p>
  4 + <template #insertFooter>
  5 + <a-button> btn</a-button>
  6 + </template>
  7 + <template #centerFooter>
  8 + <a-button> btn2</a-button>
  9 + </template>
  10 +
  11 + <template #appendFooter>
  12 + <a-button> btn3</a-button>
  13 + </template>
  14 +
  15 + <!-- <template #footer>
  16 + <a-button> customerFooter</a-button>
  17 + </template> -->
4 </BasicDrawer> 18 </BasicDrawer>
5 </template> 19 </template>
6 <script lang="ts"> 20 <script lang="ts">
@@ -9,7 +23,13 @@ @@ -9,7 +23,13 @@
9 export default defineComponent({ 23 export default defineComponent({
10 components: { BasicDrawer }, 24 components: { BasicDrawer },
11 setup() { 25 setup() {
12 - return {}; 26 + return {
  27 + handleOk: () => {
  28 + console.log('=====================');
  29 + console.log('ok');
  30 + console.log('======================');
  31 + },
  32 + };
13 }, 33 },
14 }); 34 });
15 </script> 35 </script>
src/views/demo/comp/drawer/Drawer5.vue
1 <template> 1 <template>
2 <BasicDrawer v-bind="$attrs" :isDetail="true" title="Drawer Title5"> 2 <BasicDrawer v-bind="$attrs" :isDetail="true" title="Drawer Title5">
3 <p class="h-20">Content Message</p> 3 <p class="h-20">Content Message</p>
  4 + <template #titleToolbar> toolbar </template>
4 </BasicDrawer> 5 </BasicDrawer>
5 </template> 6 </template>
6 <script lang="ts"> 7 <script lang="ts">
@@ -8,6 +9,5 @@ @@ -8,6 +9,5 @@
8 import { BasicDrawer } from '/@/components/Drawer'; 9 import { BasicDrawer } from '/@/components/Drawer';
9 export default defineComponent({ 10 export default defineComponent({
10 components: { BasicDrawer }, 11 components: { BasicDrawer },
11 - setup() {},  
12 }); 12 });
13 </script> 13 </script>
src/views/demo/comp/drawer/index.vue
@@ -3,10 +3,10 @@ @@ -3,10 +3,10 @@
3 <Alert message="使用 useDrawer 进行抽屉操作" show-icon /> 3 <Alert message="使用 useDrawer 进行抽屉操作" show-icon />
4 <a-button type="primary" class="my-4" @click="openDrawerLoading">打开Drawer</a-button> 4 <a-button type="primary" class="my-4" @click="openDrawerLoading">打开Drawer</a-button>
5 5
6 - <Alert message="内外同时同时显示隐藏" show-icon />  
7 - <a-button type="primary" class="my-4" @click="openDrawer2">打开Drawer</a-button> 6 + <Alert message="内外同时控制显示隐藏" show-icon />
  7 + <a-button type="primary" class="my-4" @click="openDrawer2(true)">打开Drawer</a-button>
8 <Alert message="自适应高度/显示footer" show-icon /> 8 <Alert message="自适应高度/显示footer" show-icon />
9 - <a-button type="primary" class="my-4" @click="openDrawer3">打开Drawer</a-button> 9 + <a-button type="primary" class="my-4" @click="openDrawer3(true)">打开Drawer</a-button>
10 10
11 <Alert 11 <Alert
12 message="内外数据交互,外部通过 transferModalData 发送,内部通过 receiveDrawerDataRef 接收。该数据具有响应式" 12 message="内外数据交互,外部通过 transferModalData 发送,内部通过 receiveDrawerDataRef 接收。该数据具有响应式"
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /> 14 />
15 <a-button type="primary" class="my-4" @click="send">打开Drawer并传递数据</a-button> 15 <a-button type="primary" class="my-4" @click="send">打开Drawer并传递数据</a-button>
16 <Alert message="详情页模式" show-icon /> 16 <Alert message="详情页模式" show-icon />
17 - <a-button type="primary" class="my-4" @click="openDrawer5">打开详情Drawer</a-button> 17 + <a-button type="primary" class="my-4" @click="openDrawer5(true)">打开详情Drawer</a-button>
18 <Drawer1 @register="register1" /> 18 <Drawer1 @register="register1" />
19 <Drawer2 @register="register2" /> 19 <Drawer2 @register="register2" />
20 <Drawer3 @register="register3" /> 20 <Drawer3 @register="register3" />
src/views/demo/feat/tabs/index.vue
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <a-input placeholder="请输入" /> 4 <a-input placeholder="请输入" />
5 </CollapseContainer> 5 </CollapseContainer>
6 6
7 - <CollapseContainer class="mt-4 px-4" title="标签页操作"> 7 + <CollapseContainer class="mt-4" title="标签页操作">
8 <a-button class="mr-2" @click="closeAll">关闭所有</a-button> 8 <a-button class="mr-2" @click="closeAll">关闭所有</a-button>
9 <a-button class="mr-2" @click="closeLeft">关闭左侧</a-button> 9 <a-button class="mr-2" @click="closeLeft">关闭左侧</a-button>
10 <a-button class="mr-2" @click="closeRight">关闭右侧</a-button> 10 <a-button class="mr-2" @click="closeRight">关闭右侧</a-button>