Vben
2021-03-29 b9b470f4df1cd57ca501666b6b3270a4d4d4f873
提交 | 用户 | age
ebf7c8 1 <template>
V 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>
9edc28 11           <slot name="titleToolbar"></slot>
ebf7c8 12         </template>
V 13       </DrawerHeader>
14     </template>
b9b470 15     <template v-else #title>
V 16       <slot name="title"></slot>
17     </template>
ebf7c8 18
V 19     <ScrollContainer
20       :style="getScrollContentStyle"
21       v-loading="getLoading"
efbde0 22       :loading-tip="loadingText || t('common.loadingText')"
ebf7c8 23     >
9edc28 24       <slot></slot>
ebf7c8 25     </ScrollContainer>
V 26     <DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
27       <template #[item]="data" v-for="item in Object.keys($slots)">
9edc28 28         <slot :name="item" v-bind="data"></slot>
ebf7c8 29       </template>
V 30     </DrawerFooter>
31   </Drawer>
32 </template>
33 <script lang="ts">
34   import type { DrawerInstance, DrawerProps } from './types';
35   import type { CSSProperties } from 'vue';
36
37   import {
38     defineComponent,
39     ref,
40     computed,
41     watchEffect,
42     watch,
43     unref,
44     nextTick,
45     toRaw,
46     getCurrentInstance,
47   } from 'vue';
48   import { Drawer } from 'ant-design-vue';
49
50   import { useI18n } from '/@/hooks/web/useI18n';
51
52   import { isFunction, isNumber } from '/@/utils/is';
53   import { deepMerge } from '/@/utils';
54   import DrawerFooter from './components/DrawerFooter.vue';
55   import DrawerHeader from './components/DrawerHeader.vue';
56   import { ScrollContainer } from '/@/components/Container';
57
58   import { basicProps } from './props';
59   import { useDesign } from '/@/hooks/web/useDesign';
60   import { useAttrs } from '/@/hooks/core/useAttrs';
61
62   export default defineComponent({
63     components: { Drawer, ScrollContainer, DrawerFooter, DrawerHeader },
9edc28 64     inheritAttrs: false,
ebf7c8 65     props: basicProps,
V 66     emits: ['visible-change', 'ok', 'close', 'register'],
67     setup(props, { emit }) {
68       const visibleRef = ref(false);
69       const attrs = useAttrs();
70       const propsRef = ref<Partial<Nullable<DrawerProps>>>(null);
71
72       const { t } = useI18n();
73       const { prefixVar, prefixCls } = useDesign('basic-drawer');
74
75       const drawerInstance: DrawerInstance = {
76         setDrawerProps: setDrawerProps,
77         emitVisible: undefined,
78       };
79
80       const instance = getCurrentInstance();
81
82       instance && emit('register', drawerInstance, instance.uid);
83
84       const getMergeProps = computed(
85         (): DrawerProps => {
86           return deepMerge(toRaw(props), unref(propsRef));
87         }
88       );
89
90       const getProps = computed(
91         (): DrawerProps => {
92           const opt = {
93             placement: 'right',
94             ...unref(attrs),
95             ...unref(getMergeProps),
96             visible: unref(visibleRef),
97           };
98           opt.title = undefined;
99           const { isDetail, width, wrapClassName, getContainer } = opt;
100           if (isDetail) {
101             if (!width) {
102               opt.width = '100%';
103             }
104             const detailCls = `${prefixCls}__detail`;
105             opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
106
107             if (!getContainer) {
108               // TODO type error?
109               opt.getContainer = `.${prefixVar}-layout-content` as any;
110             }
111           }
112           return opt as DrawerProps;
113         }
114       );
115
116       const getBindValues = computed(
117         (): DrawerProps => {
118           return {
119             ...attrs,
120             ...unref(getProps),
121           };
122         }
123       );
124
125       // Custom implementation of the bottom button,
126       const getFooterHeight = computed(() => {
127         const { footerHeight, showFooter } = unref(getProps);
128         if (showFooter && footerHeight) {
129           return isNumber(footerHeight)
130             ? `${footerHeight}px`
131             : `${footerHeight.replace('px', '')}px`;
132         }
133         return `0px`;
134       });
135
136       const getScrollContentStyle = computed(
137         (): CSSProperties => {
138           const footerHeight = unref(getFooterHeight);
139           return {
140             position: 'relative',
141             height: `calc(100% - ${footerHeight})`,
142           };
143         }
144       );
145
146       const getLoading = computed(() => {
147         return !!unref(getProps)?.loading;
148       });
149
150       watchEffect(() => {
151         visibleRef.value = props.visible;
152       });
153
154       watch(
155         () => visibleRef.value,
156         (visible) => {
157           nextTick(() => {
158             emit('visible-change', visible);
159             instance && drawerInstance.emitVisible?.(visible, instance.uid);
160           });
161         }
162       );
163
164       // Cancel event
165       async function onClose(e: Recordable) {
166         const { closeFunc } = unref(getProps);
167         emit('close', e);
168         if (closeFunc && isFunction(closeFunc)) {
169           const res = await closeFunc();
170           visibleRef.value = !res;
171           return;
172         }
173         visibleRef.value = false;
174       }
175
176       function setDrawerProps(props: Partial<DrawerProps>): void {
177         // Keep the last setDrawerProps
178         propsRef.value = deepMerge(unref(propsRef) || {}, props);
179
180         if (Reflect.has(props, 'visible')) {
181           visibleRef.value = !!props.visible;
182         }
183       }
184
185       function handleOk() {
186         emit('ok');
187       }
188
189       return {
190         onClose,
191         t,
192         prefixCls,
193         getMergeProps,
194         getScrollContentStyle,
195         getProps,
196         getLoading,
197         getBindValues,
198         getFooterHeight,
199         handleOk,
200       };
201     },
202   });
203 </script>
204 <style lang="less">
205   @header-height: 60px;
206   @detail-header-height: 40px;
207   @prefix-cls: ~'@{namespace}-basic-drawer';
208   @prefix-cls-detail: ~'@{namespace}-basic-drawer__detail';
209
210   .@{prefix-cls} {
211     .ant-drawer-wrapper-body {
212       overflow: hidden;
213     }
214
215     .ant-drawer-close {
216       &:hover {
217         color: @error-color;
218       }
219     }
220
221     .ant-drawer-body {
222       height: calc(100% - @header-height);
223       padding: 0;
a1c3c5 224       background-color: #fff;
ebf7c8 225
V 226       .scrollbar__wrap {
227         padding: 16px !important;
228         margin-bottom: 0 !important;
229       }
354904 230
V 231       > .scrollbar > .scrollbar__bar.is-horizontal {
232         display: none;
233       }
ebf7c8 234     }
V 235   }
236
237   .@{prefix-cls-detail} {
238     position: absolute;
239
240     .ant-drawer-header {
241       width: 100%;
242       height: @detail-header-height;
243       padding: 0;
244       border-top: 1px solid @border-color-base;
245       box-sizing: border-box;
246     }
247
248     .ant-drawer-title {
249       height: 100%;
250     }
251
252     .ant-drawer-close {
253       height: @detail-header-height;
254       line-height: @detail-header-height;
255     }
256
257     .scrollbar__wrap {
258       padding: 0 !important;
259     }
260
261     .ant-drawer-body {
262       height: calc(100% - @detail-header-height);
263     }
264   }
265 </style>