Commit 551fe50a44d0b6358cf3861f772ca223ea56f0e2
1 parent
745fcfc0
perf(table): the table fills the height according to the screen close #310
Showing
10 changed files
with
135 additions
and
138 deletions
CHANGELOG.zh_CN.md
package.json
... | ... | @@ -109,7 +109,7 @@ |
109 | 109 | "vite-plugin-mock": "^2.2.0", |
110 | 110 | "vite-plugin-purge-icons": "^0.7.0", |
111 | 111 | "vite-plugin-pwa": "^0.5.6", |
112 | - "vite-plugin-style-import": "^0.7.6", | |
112 | + "vite-plugin-style-import": "^0.8.1", | |
113 | 113 | "vite-plugin-svg-icons": "^0.2.1", |
114 | 114 | "vite-plugin-theme": "^0.4.8", |
115 | 115 | "vite-plugin-windicss": "0.6.10", | ... | ... |
src/components/Page/src/PageWrapper.vue
1 | 1 | <template> |
2 | 2 | <div :class="getClass"> |
3 | - <PageHeader :ghost="ghost" v-bind="$attrs" ref="headerRef"> | |
3 | + <PageHeader | |
4 | + :ghost="ghost" | |
5 | + v-bind="$attrs" | |
6 | + ref="headerRef" | |
7 | + v-if="content || $slots.headerContent" | |
8 | + > | |
4 | 9 | <template #default> |
5 | 10 | <template v-if="content"> |
6 | 11 | {{ content }} |
... | ... | @@ -11,7 +16,11 @@ |
11 | 16 | <slot :name="item" v-bind="data"></slot> |
12 | 17 | </template> |
13 | 18 | </PageHeader> |
14 | - <div :class="[`${prefixCls}-content`, $attrs.contentClass]" :style="getContentStyle"> | |
19 | + <div | |
20 | + class="m-4 overflow-hidden" | |
21 | + :class="[`${prefixCls}-content`, contentClass]" | |
22 | + :style="getContentStyle" | |
23 | + > | |
15 | 24 | <slot></slot> |
16 | 25 | </div> |
17 | 26 | <PageFooter v-if="getShowFooter" ref="footerRef"> |
... | ... | @@ -48,6 +57,8 @@ |
48 | 57 | }, |
49 | 58 | contentBackground: propTypes.bool, |
50 | 59 | contentFullHeight: propTypes.bool, |
60 | + contentClass: propTypes.string, | |
61 | + fixedHeight: propTypes.bool, | |
51 | 62 | }, |
52 | 63 | setup(props, { slots }) { |
53 | 64 | const headerRef = ref<ComponentRef>(null); |
... | ... | @@ -73,15 +84,17 @@ |
73 | 84 | |
74 | 85 | const getContentStyle = computed( |
75 | 86 | (): CSSProperties => { |
76 | - const { contentBackground, contentFullHeight, contentStyle } = props; | |
87 | + const { contentBackground, contentFullHeight, contentStyle, fixedHeight } = props; | |
77 | 88 | const bg = contentBackground ? { backgroundColor: '#fff' } : {}; |
78 | 89 | if (!contentFullHeight) { |
79 | 90 | return { ...bg, ...contentStyle }; |
80 | 91 | } |
92 | + const height = `${unref(pageHeight)}px`; | |
81 | 93 | return { |
82 | 94 | ...bg, |
83 | 95 | ...contentStyle, |
84 | - minHeight: `${unref(pageHeight)}px`, | |
96 | + minHeight: height, | |
97 | + ...(fixedHeight ? { height } : {}), | |
85 | 98 | paddingBottom: `${unref(footerHeight)}px`, |
86 | 99 | }; |
87 | 100 | } |
... | ... | @@ -137,18 +150,11 @@ |
137 | 150 | position: relative; |
138 | 151 | |
139 | 152 | .ant-page-header { |
140 | - // padding: 12px 16px; | |
141 | - | |
142 | 153 | &:empty { |
143 | 154 | padding: 0; |
144 | 155 | } |
145 | 156 | } |
146 | 157 | |
147 | - &-content { | |
148 | - // padding: 12px; | |
149 | - margin: 16px; | |
150 | - } | |
151 | - | |
152 | 158 | &--dense { |
153 | 159 | .@{prefix-cls}-content { |
154 | 160 | margin: 0; | ... | ... |
src/components/Table/src/BasicTable.vue
... | ... | @@ -3,6 +3,7 @@ |
3 | 3 | ref="wrapRef" |
4 | 4 | :class="[ |
5 | 5 | prefixCls, |
6 | + $attrs.class, | |
6 | 7 | { |
7 | 8 | [`${prefixCls}-form-container`]: getBindValues.useSearchForm, |
8 | 9 | [`${prefixCls}--inset`]: getBindValues.inset, |
... | ... | @@ -211,6 +212,8 @@ |
211 | 212 | propsData = omit(propsData, 'scroll'); |
212 | 213 | } |
213 | 214 | |
215 | + propsData = omit(propsData, 'class'); | |
216 | + | |
214 | 217 | return propsData; |
215 | 218 | }); |
216 | 219 | ... | ... |
src/components/Table/src/hooks/useTableScroll.ts
... | ... | @@ -55,6 +55,7 @@ export function useTableScroll( |
55 | 55 | // No need to repeat queries |
56 | 56 | let paginationEl: HTMLElement | null; |
57 | 57 | let footerEl: HTMLElement | null; |
58 | + let bodyEl: HTMLElement | null; | |
58 | 59 | |
59 | 60 | async function calcTableHeight() { |
60 | 61 | const { resizeHeightOffset, pagination, maxHeight } = unref(propsRef); |
... | ... | @@ -68,6 +69,7 @@ export function useTableScroll( |
68 | 69 | if (!tableEl) return; |
69 | 70 | |
70 | 71 | const headEl = tableEl.querySelector('.ant-table-thead '); |
72 | + | |
71 | 73 | if (!headEl) return; |
72 | 74 | |
73 | 75 | // Table height from bottom |
... | ... | @@ -117,6 +119,11 @@ export function useTableScroll( |
117 | 119 | |
118 | 120 | height = (height > maxHeight! ? (maxHeight as number) : height) ?? height; |
119 | 121 | setHeight(height); |
122 | + | |
123 | + if (!bodyEl) { | |
124 | + bodyEl = tableEl.querySelector('.ant-table-body'); | |
125 | + } | |
126 | + bodyEl!.style.height = `${height}px`; | |
120 | 127 | } |
121 | 128 | |
122 | 129 | useWindowSizeFn(calcTableHeight, 200); | ... | ... |
src/components/Table/src/style/index.less
... | ... | @@ -4,12 +4,11 @@ |
4 | 4 | |
5 | 5 | .@{prefix-cls} { |
6 | 6 | &-form-container { |
7 | - width: 100%; | |
8 | 7 | padding: 16px; |
9 | 8 | |
10 | 9 | .ant-form { |
11 | - padding: 16px 16px 6px 12px; | |
12 | - margin-bottom: 18px; | |
10 | + padding: 12px 10px 6px 10px; | |
11 | + margin-bottom: 16px; | |
13 | 12 | background: #fff; |
14 | 13 | border-radius: 4px; |
15 | 14 | } |
... | ... | @@ -74,40 +73,6 @@ |
74 | 73 | } |
75 | 74 | } |
76 | 75 | |
77 | - // .ant-table-bordered .ant-table-header > table, | |
78 | - // .ant-table-bordered .ant-table-body > table, | |
79 | - // .ant-table-bordered .ant-table-fixed-left table, | |
80 | - // .ant-table-bordered .ant-table-fixed-right table { | |
81 | - // border: 1px solid @border-color !important; | |
82 | - // } | |
83 | - | |
84 | - // .ant-table-thead { | |
85 | - // tr { | |
86 | - // border: none; | |
87 | - // } | |
88 | - | |
89 | - // th { | |
90 | - // border: none; | |
91 | - // } | |
92 | - // } | |
93 | - | |
94 | - // .ant-table-bordered .ant-table-tbody > tr > td { | |
95 | - // border-bottom: 1px solid @border-color !important; | |
96 | - | |
97 | - // &:last-child { | |
98 | - // border-right: none !important; | |
99 | - // } | |
100 | - // } | |
101 | - | |
102 | - // .ant-table.ant-table-bordered .ant-table-footer, | |
103 | - // .ant-table.ant-table-bordered .ant-table-title { | |
104 | - // border: 1px solid @border-color !important; | |
105 | - // } | |
106 | - | |
107 | - // .ant-table-bordered.ant-table-empty .ant-table-placeholder { | |
108 | - // border: 1px solid @border-color !important; | |
109 | - // } | |
110 | - | |
111 | 76 | .ant-table-tbody > tr > td, |
112 | 77 | .ant-table-tbody > tr > th, |
113 | 78 | .ant-table-thead > tr > td, |
... | ... | @@ -115,62 +80,10 @@ |
115 | 80 | white-space: pre; |
116 | 81 | } |
117 | 82 | |
118 | - // .ant-table-row-cell-last { | |
119 | - // border-right: none !important; | |
120 | - // } | |
121 | - | |
122 | - // .ant-table-bordered .ant-table-thead > tr > th, | |
123 | - // .ant-table-bordered .ant-table-tbody > tr > td { | |
124 | - // border-right: 1px solid @border-color !important; | |
125 | - // } | |
126 | - | |
127 | 83 | .ant-pagination { |
128 | 84 | margin: 10px 0 0 0; |
129 | 85 | } |
130 | 86 | |
131 | - // .ant-table-body { | |
132 | - // overflow-x: auto !important; | |
133 | - // overflow-y: scroll !important; | |
134 | - // } | |
135 | - | |
136 | - // .ant-table-header { | |
137 | - // margin-bottom: 0 !important; | |
138 | - // overflow-x: hidden !important; | |
139 | - // overflow-y: scroll !important; | |
140 | - // } | |
141 | - | |
142 | - // .ant-table-fixed-right { | |
143 | - // right: -1px; | |
144 | - | |
145 | - // .ant-table-header { | |
146 | - // border-left: 1px solid @border-color !important; | |
147 | - | |
148 | - // .ant-table-fixed { | |
149 | - // border-bottom: none; | |
150 | - | |
151 | - // .ant-table-thead th { | |
152 | - // background: rgb(241, 243, 244); | |
153 | - // } | |
154 | - // } | |
155 | - // } | |
156 | - // } | |
157 | - | |
158 | - // .ant-table-fixed-left { | |
159 | - // .ant-table-header { | |
160 | - // overflow-y: hidden !important; | |
161 | - // } | |
162 | - | |
163 | - // .ant-table-fixed { | |
164 | - // border-bottom: none; | |
165 | - // } | |
166 | - // } | |
167 | - | |
168 | - // .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th, | |
169 | - // .ant-table-tbody > tr > td { | |
170 | - // word-break: break-word; | |
171 | - // // border-color: @border-color !important; | |
172 | - // } | |
173 | - | |
174 | 87 | .ant-table-footer { |
175 | 88 | padding: 0; |
176 | 89 | ... | ... |
src/components/Tree/src/index.vue
1 | 1 | <script lang="tsx"> |
2 | 2 | import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types'; |
3 | 3 | |
4 | - import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw, watch } from 'vue'; | |
5 | - import { Tree } from 'ant-design-vue'; | |
4 | + import { | |
5 | + defineComponent, | |
6 | + reactive, | |
7 | + computed, | |
8 | + unref, | |
9 | + ref, | |
10 | + watchEffect, | |
11 | + toRaw, | |
12 | + watch, | |
13 | + CSSProperties, | |
14 | + } from 'vue'; | |
15 | + import { Tree, Empty } from 'ant-design-vue'; | |
6 | 16 | import { TreeIcon } from './TreeIcon'; |
7 | 17 | import TreeHeader from './TreeHeader.vue'; |
18 | + import { ScrollContainer } from '/@/components/Container'; | |
8 | 19 | // import { DownOutlined } from '@ant-design/icons-vue'; |
9 | 20 | |
10 | 21 | import { omit, get } from 'lodash-es'; |
... | ... | @@ -95,6 +106,11 @@ |
95 | 106 | emit('update:value', rawVal); |
96 | 107 | }, |
97 | 108 | onRightClick: handleRightClick, |
109 | + // onSelect: (k, e) => { | |
110 | + // setTimeout(() => { | |
111 | + // emit('select', k, e); | |
112 | + // }, 16); | |
113 | + // }, | |
98 | 114 | }; |
99 | 115 | propsData = omit(propsData, 'treeData', 'class'); |
100 | 116 | return propsData; |
... | ... | @@ -104,6 +120,10 @@ |
104 | 120 | searchState.startSearch ? searchState.searchData : unref(treeDataRef) |
105 | 121 | ); |
106 | 122 | |
123 | + const getNotFound = computed((): boolean => { | |
124 | + return searchState.startSearch && searchState.searchData?.length === 0; | |
125 | + }); | |
126 | + | |
107 | 127 | const { |
108 | 128 | deleteNodeByKey, |
109 | 129 | insertNodeByKey, |
... | ... | @@ -178,10 +198,10 @@ |
178 | 198 | return; |
179 | 199 | } |
180 | 200 | searchState.startSearch = true; |
201 | + const { title: titleField } = unref(getReplaceFields); | |
181 | 202 | |
182 | 203 | searchState.searchData = filter(unref(treeDataRef), (node) => { |
183 | - const { title } = node; | |
184 | - return title?.includes(searchValue) ?? false; | |
204 | + return node[titleField]?.includes(searchValue) ?? false; | |
185 | 205 | }); |
186 | 206 | } |
187 | 207 | |
... | ... | @@ -284,7 +304,7 @@ |
284 | 304 | title: () => ( |
285 | 305 | <span |
286 | 306 | class={`${prefixCls}-title pl-2`} |
287 | - onClick={handleClickNode.bind(null, item.key, children)} | |
307 | + onClick={handleClickNode.bind(null, item[keyField], item[childrenField])} | |
288 | 308 | > |
289 | 309 | {icon && <TreeIcon icon={icon} />} |
290 | 310 | <span |
... | ... | @@ -304,9 +324,11 @@ |
304 | 324 | } |
305 | 325 | return () => { |
306 | 326 | const { title, helpMessage, toolbar, search } = props; |
327 | + const showTitle = title || toolbar || search; | |
328 | + const scrollStyle: CSSProperties = { height: 'calc(100% - 38px)' }; | |
307 | 329 | return ( |
308 | 330 | <div class={[prefixCls, 'h-full bg-white', attrs.class]}> |
309 | - {(title || toolbar || search) && ( | |
331 | + {showTitle && ( | |
310 | 332 | <TreeHeader |
311 | 333 | checkAll={checkAll} |
312 | 334 | expandAll={expandAll} |
... | ... | @@ -318,13 +340,17 @@ |
318 | 340 | onSearch={handleSearch} |
319 | 341 | /> |
320 | 342 | )} |
321 | - <Tree {...unref(getBindValues)} showIcon={false}> | |
322 | - {{ | |
323 | - // switcherIcon: () => <DownOutlined />, | |
324 | - default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }), | |
325 | - ...extendSlots(slots), | |
326 | - }} | |
327 | - </Tree> | |
343 | + <ScrollContainer style={scrollStyle} v-show={!unref(getNotFound)}> | |
344 | + <Tree {...unref(getBindValues)} showIcon={false}> | |
345 | + {{ | |
346 | + // switcherIcon: () => <DownOutlined />, | |
347 | + default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }), | |
348 | + ...extendSlots(slots), | |
349 | + }} | |
350 | + </Tree> | |
351 | + </ScrollContainer> | |
352 | + | |
353 | + <Empty v-show={unref(getNotFound)} class="!mt-4" /> | |
328 | 354 | </div> |
329 | 355 | ); |
330 | 356 | }; | ... | ... |
src/views/demo/system/account/DeptTree.vue
0 → 100644
1 | +<template> | |
2 | + <div class="bg-white m-4 mr-0 overflow-hidden"> | |
3 | + <BasicTree | |
4 | + title="部门列表" | |
5 | + toolbar | |
6 | + search | |
7 | + :clickRowToExpand="false" | |
8 | + :treeData="treeData" | |
9 | + :replaceFields="{ key: 'id', title: 'deptName' }" | |
10 | + @select="handleSelect" | |
11 | + /> | |
12 | + </div> | |
13 | +</template> | |
14 | +<script lang="ts"> | |
15 | + import { defineComponent, onMounted, ref } from 'vue'; | |
16 | + | |
17 | + import { BasicTree, TreeItem } from '/@/components/Tree'; | |
18 | + import { getDeptList } from '/@/api/demo/system'; | |
19 | + | |
20 | + export default defineComponent({ | |
21 | + name: 'DeptTree', | |
22 | + components: { BasicTree }, | |
23 | + | |
24 | + emits: ['select'], | |
25 | + setup(_, { emit }) { | |
26 | + const treeData = ref<TreeItem[]>([]); | |
27 | + | |
28 | + async function fetch() { | |
29 | + treeData.value = ((await getDeptList()) as unknown) as TreeItem[]; | |
30 | + } | |
31 | + | |
32 | + function handleSelect(keys: string, e) { | |
33 | + emit('select', keys[0]); | |
34 | + console.log(keys, e); | |
35 | + } | |
36 | + | |
37 | + onMounted(() => { | |
38 | + fetch(); | |
39 | + }); | |
40 | + return { treeData, handleSelect }; | |
41 | + }, | |
42 | + }); | |
43 | +</script> | ... | ... |
src/views/demo/system/account/index.vue
1 | 1 | <template> |
2 | - <div :class="[prefixCls]"> | |
3 | - <BasicTable @register="registerTable"> | |
2 | + <PageWrapper dense contentFullHeight fixedHeight contentClass="flex"> | |
3 | + <DeptTree class="w-1/4 xl:w-1/5" @select="handleSelect" /> | |
4 | + <BasicTable @register="registerTable" class="w-3/4 xl:w-4/5"> | |
4 | 5 | <template #toolbar> |
5 | 6 | <a-button type="primary" @click="handleCreate"> 新增账号 </a-button> |
6 | 7 | </template> |
... | ... | @@ -24,14 +25,15 @@ |
24 | 25 | </template> |
25 | 26 | </BasicTable> |
26 | 27 | <AccountModal @register="registerModal" @success="handleSuccess" /> |
27 | - </div> | |
28 | + </PageWrapper> | |
28 | 29 | </template> |
29 | 30 | <script lang="ts"> |
30 | 31 | import { defineComponent } from 'vue'; |
31 | 32 | |
32 | - import { useDesign } from '/@/hooks/web/useDesign'; | |
33 | 33 | import { BasicTable, useTable, TableAction } from '/@/components/Table'; |
34 | 34 | import { getAccountList } from '/@/api/demo/system'; |
35 | + import { PageWrapper } from '/@/components/Page'; | |
36 | + import DeptTree from './DeptTree.vue'; | |
35 | 37 | |
36 | 38 | import { useModal } from '/@/components/Modal'; |
37 | 39 | import AccountModal from './AccountModal.vue'; |
... | ... | @@ -40,10 +42,8 @@ |
40 | 42 | |
41 | 43 | export default defineComponent({ |
42 | 44 | name: 'AccountManagement', |
43 | - components: { BasicTable, AccountModal, TableAction }, | |
45 | + components: { BasicTable, PageWrapper, DeptTree, AccountModal, TableAction }, | |
44 | 46 | setup() { |
45 | - const { prefixCls } = useDesign('account-management'); | |
46 | - | |
47 | 47 | const [registerModal, { openModal }] = useModal(); |
48 | 48 | const [registerTable, { reload }] = useTable({ |
49 | 49 | title: '账号列表', |
... | ... | @@ -86,22 +86,19 @@ |
86 | 86 | reload(); |
87 | 87 | } |
88 | 88 | |
89 | + function handleSelect(deptId: string = '') { | |
90 | + reload({ searchInfo: { deptId } }); | |
91 | + } | |
92 | + | |
89 | 93 | return { |
90 | - prefixCls, | |
91 | 94 | registerTable, |
92 | 95 | registerModal, |
93 | 96 | handleCreate, |
94 | 97 | handleEdit, |
95 | 98 | handleDelete, |
96 | 99 | handleSuccess, |
100 | + handleSelect, | |
97 | 101 | }; |
98 | 102 | }, |
99 | 103 | }); |
100 | 104 | </script> |
101 | -<style lang="less" scoped> | |
102 | - @prefix-cls: ~'@{namespace}-account-management'; | |
103 | - | |
104 | - .@{prefix-cls} { | |
105 | - display: flex; | |
106 | - } | |
107 | -</style> | ... | ... |
yarn.lock
... | ... | @@ -3662,10 +3662,10 @@ es-module-lexer@^0.3.26: |
3662 | 3662 | resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.3.26.tgz#7b507044e97d5b03b01d4392c74ffeb9c177a83b" |
3663 | 3663 | integrity sha512-Va0Q/xqtrss45hWzP8CZJwzGSZJjDM5/MJRE3IXXnUCcVLElR9BRaE9F62BopysASyc4nM3uwhSW7FFB9nlWAA== |
3664 | 3664 | |
3665 | -es-module-lexer@^0.4.0: | |
3666 | - version "0.4.0" | |
3667 | - resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.0.tgz#21f4181cc8b7eee06855f1c59e6087c7bc4f77b0" | |
3668 | - integrity sha512-iuEGihqqhKWFgh72Q/Jtch7V2t/ft8w8IPP2aEN8ArYKO+IWyo6hsi96hCdgyeEDQIV3InhYQ9BlwUFPGXrbEQ== | |
3665 | +es-module-lexer@^0.4.1: | |
3666 | + version "0.4.1" | |
3667 | + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e" | |
3668 | + integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA== | |
3669 | 3669 | |
3670 | 3670 | es-to-primitive@^1.2.1: |
3671 | 3671 | version "1.2.1" |
... | ... | @@ -9120,15 +9120,15 @@ vite-plugin-pwa@^0.5.6: |
9120 | 9120 | pretty-bytes "^5.6.0" |
9121 | 9121 | workbox-build "^6.1.1" |
9122 | 9122 | |
9123 | -vite-plugin-style-import@^0.7.6: | |
9124 | - version "0.7.6" | |
9125 | - resolved "https://registry.npmjs.org/vite-plugin-style-import/-/vite-plugin-style-import-0.7.6.tgz#909a5402f3a915fb2512e2a039e9cdb360fd2882" | |
9126 | - integrity sha512-EDjscCzMsmi6mJ0UbMLMkCGLo7LCdFsRJZdjO7sfUIB+2wsC1FjDJcIEGWg0Lzl+4gghv9rk+AP+WCibI83WNw== | |
9123 | +vite-plugin-style-import@^0.8.1: | |
9124 | + version "0.8.1" | |
9125 | + resolved "https://registry.npmjs.org/vite-plugin-style-import/-/vite-plugin-style-import-0.8.1.tgz#e098c633cba3abef9b5a156aaf47f001567ebbb9" | |
9126 | + integrity sha512-qZg73SA2+tbuEk7b0VjubjceUKVzHB6NwDYd3R9Hd6At4+sJ/85UIlTkzxSWHNgkTQh4sIOMQi1olXjkSF7tjg== | |
9127 | 9127 | dependencies: |
9128 | 9128 | "@rollup/pluginutils" "^4.1.0" |
9129 | 9129 | change-case "^4.1.2" |
9130 | 9130 | debug "^4.3.2" |
9131 | - es-module-lexer "^0.4.0" | |
9131 | + es-module-lexer "^0.4.1" | |
9132 | 9132 | magic-string "^0.25.7" |
9133 | 9133 | |
9134 | 9134 | vite-plugin-svg-icons@^0.2.1: | ... | ... |