Commit 391da9ec2884885f9dfe86ddb869ccc0d193491e
1 parent
b2a1951f
feat(table): add expandAll/collapseAll function close #333
Showing
28 changed files
with
286 additions
and
271 deletions
CHANGELOG.zh_CN.md
package.json
src/components/Table/src/BasicTable.vue
1 | 1 | <template> |
2 | - <div | |
3 | - ref="wrapRef" | |
4 | - :class="[ | |
5 | - prefixCls, | |
6 | - $attrs.class, | |
7 | - { | |
8 | - [`${prefixCls}-form-container`]: getBindValues.useSearchForm, | |
9 | - [`${prefixCls}--inset`]: getBindValues.inset, | |
10 | - }, | |
11 | - ]" | |
12 | - > | |
2 | + <div ref="wrapRef" :class="getWrapperClass"> | |
13 | 3 | <BasicForm |
14 | 4 | submitOnReset |
15 | 5 | v-bind="getFormProps" |
... | ... | @@ -32,9 +22,10 @@ |
32 | 22 | v-show="getEmptyDataIsShowTable" |
33 | 23 | @change="handleTableChange" |
34 | 24 | > |
35 | - <template #[item]="data" v-for="item in Object.keys($slots)"> | |
25 | + <template #[item]="data" v-for="item in Object.keys($slots)" :key="item"> | |
36 | 26 | <slot :name="item" v-bind="data"></slot> |
37 | 27 | </template> |
28 | + | |
38 | 29 | <template #[`header-${column.dataIndex}`] v-for="column in columns" :key="column.dataIndex"> |
39 | 30 | <HeaderCell :column="column" /> |
40 | 31 | </template> |
... | ... | @@ -47,8 +38,8 @@ |
47 | 38 | import { defineComponent, ref, computed, unref } from 'vue'; |
48 | 39 | import { Table } from 'ant-design-vue'; |
49 | 40 | import { BasicForm, useForm } from '/@/components/Form/index'; |
50 | - | |
51 | - import { omit } from 'lodash-es'; | |
41 | + import expandIcon from './components/ExpandIcon'; | |
42 | + import HeaderCell from './components/HeaderCell.vue'; | |
52 | 43 | |
53 | 44 | import { usePagination } from './hooks/usePagination'; |
54 | 45 | import { useColumns } from './hooks/useColumns'; |
... | ... | @@ -59,17 +50,16 @@ |
59 | 50 | import { useCustomRow } from './hooks/useCustomRow'; |
60 | 51 | import { useTableStyle } from './hooks/useTableStyle'; |
61 | 52 | import { useTableHeader } from './hooks/useTableHeader'; |
53 | + import { useTableExpand } from './hooks/useTableExpand'; | |
62 | 54 | import { createTableContext } from './hooks/useTableContext'; |
63 | 55 | import { useTableFooter } from './hooks/useTableFooter'; |
64 | 56 | import { useTableForm } from './hooks/useTableForm'; |
65 | 57 | import { useExpose } from '/@/hooks/core/useExpose'; |
66 | 58 | import { useDesign } from '/@/hooks/web/useDesign'; |
67 | 59 | |
60 | + import { omit } from 'lodash-es'; | |
68 | 61 | import { basicProps } from './props'; |
69 | - import expandIcon from './components/ExpandIcon'; | |
70 | - import HeaderCell from './components/HeaderCell.vue'; | |
71 | 62 | |
72 | - import './style/index.less'; | |
73 | 63 | export default defineComponent({ |
74 | 64 | components: { |
75 | 65 | Table, |
... | ... | @@ -91,6 +81,7 @@ |
91 | 81 | 'edit-cancel', |
92 | 82 | 'edit-row-end', |
93 | 83 | 'edit-change', |
84 | + 'expanded-rows-change', | |
94 | 85 | ], |
95 | 86 | setup(props, { attrs, emit, slots }) { |
96 | 87 | const tableElRef = ref<ComponentRef>(null); |
... | ... | @@ -175,6 +166,8 @@ |
175 | 166 | |
176 | 167 | const { getRowClassName } = useTableStyle(getProps, prefixCls); |
177 | 168 | |
169 | + const { getExpandOption, expandAll, collapseAll } = useTableExpand(getProps, tableData, emit); | |
170 | + | |
178 | 171 | const { getHeaderProps } = useTableHeader(getProps, slots); |
179 | 172 | |
180 | 173 | const { getFooterProps } = useTableFooter( |
... | ... | @@ -208,6 +201,7 @@ |
208 | 201 | pagination: unref(getPaginationInfo), |
209 | 202 | dataSource: unref(getDataSourceRef), |
210 | 203 | footer: unref(getFooterProps), |
204 | + ...unref(getExpandOption), | |
211 | 205 | }; |
212 | 206 | if (slots.expandedRowRender) { |
213 | 207 | propsData = omit(propsData, 'scroll'); |
... | ... | @@ -218,6 +212,18 @@ |
218 | 212 | return propsData; |
219 | 213 | }); |
220 | 214 | |
215 | + const getWrapperClass = computed(() => { | |
216 | + const values = unref(getBindValues); | |
217 | + return [ | |
218 | + prefixCls, | |
219 | + attrs.class, | |
220 | + { | |
221 | + [`${prefixCls}-form-container`]: values.useSearchForm, | |
222 | + [`${prefixCls}--inset`]: values.inset, | |
223 | + }, | |
224 | + ]; | |
225 | + }); | |
226 | + | |
221 | 227 | const getEmptyDataIsShowTable = computed(() => { |
222 | 228 | const { emptyDataIsShowTable, useSearchForm } = unref(getProps); |
223 | 229 | if (emptyDataIsShowTable || !useSearchForm) { |
... | ... | @@ -253,6 +259,8 @@ |
253 | 259 | setShowPagination, |
254 | 260 | getShowPagination, |
255 | 261 | setCacheColumnsByField, |
262 | + expandAll, | |
263 | + collapseAll, | |
256 | 264 | getSize: () => { |
257 | 265 | return unref(getBindValues).size as SizeType; |
258 | 266 | }, |
... | ... | @@ -278,9 +286,100 @@ |
278 | 286 | getFormProps, |
279 | 287 | replaceFormSlotKey, |
280 | 288 | getFormSlotKeys, |
281 | - prefixCls, | |
289 | + getWrapperClass, | |
282 | 290 | columns: getViewColumns, |
283 | 291 | }; |
284 | 292 | }, |
285 | 293 | }); |
286 | 294 | </script> |
295 | +<style lang="less"> | |
296 | + @border-color: #cecece4d; | |
297 | + | |
298 | + @prefix-cls: ~'@{namespace}-basic-table'; | |
299 | + | |
300 | + .@{prefix-cls} { | |
301 | + &-form-container { | |
302 | + padding: 16px; | |
303 | + | |
304 | + .ant-form { | |
305 | + padding: 12px 10px 6px 10px; | |
306 | + margin-bottom: 16px; | |
307 | + background: #fff; | |
308 | + border-radius: 4px; | |
309 | + } | |
310 | + } | |
311 | + | |
312 | + &-row__striped { | |
313 | + td { | |
314 | + background: #fafafa; | |
315 | + } | |
316 | + } | |
317 | + | |
318 | + &--inset { | |
319 | + .ant-table-wrapper { | |
320 | + padding: 0; | |
321 | + } | |
322 | + } | |
323 | + | |
324 | + .ant-tag { | |
325 | + margin-right: 0; | |
326 | + } | |
327 | + | |
328 | + .ant-table-wrapper { | |
329 | + padding: 6px; | |
330 | + background: #fff; | |
331 | + border-radius: 2px; | |
332 | + | |
333 | + .ant-table-title { | |
334 | + padding: 0 0 8px 0 !important; | |
335 | + } | |
336 | + | |
337 | + .ant-table.ant-table-bordered .ant-table-title { | |
338 | + border: none !important; | |
339 | + } | |
340 | + } | |
341 | + | |
342 | + // | |
343 | + .ant-table { | |
344 | + width: 100%; | |
345 | + overflow-x: hidden; | |
346 | + | |
347 | + &-title { | |
348 | + display: flex; | |
349 | + padding: 8px 6px; | |
350 | + border-bottom: none; | |
351 | + justify-content: space-between; | |
352 | + align-items: center; | |
353 | + } | |
354 | + | |
355 | + .ant-table-tbody > tr.ant-table-row-selected td { | |
356 | + background: fade(@primary-color, 8%) !important; | |
357 | + } | |
358 | + } | |
359 | + | |
360 | + .ant-pagination { | |
361 | + margin: 10px 0 0 0; | |
362 | + } | |
363 | + | |
364 | + .ant-table-footer { | |
365 | + padding: 0; | |
366 | + | |
367 | + .ant-table-wrapper { | |
368 | + padding: 0; | |
369 | + } | |
370 | + | |
371 | + table { | |
372 | + border: none !important; | |
373 | + } | |
374 | + | |
375 | + .ant-table-body { | |
376 | + overflow-x: hidden !important; | |
377 | + overflow-y: scroll !important; | |
378 | + } | |
379 | + | |
380 | + td { | |
381 | + padding: 12px 8px; | |
382 | + } | |
383 | + } | |
384 | + } | |
385 | +</style> | |
... | ... |
src/components/Table/src/components/EditTableHeaderIcon.vue
... | ... | @@ -6,16 +6,15 @@ |
6 | 6 | </span> |
7 | 7 | </template> |
8 | 8 | <script lang="ts"> |
9 | - import { defineComponent, PropType } from 'vue'; | |
9 | + import { defineComponent } from 'vue'; | |
10 | 10 | import { FormOutlined } from '@ant-design/icons-vue'; |
11 | + | |
12 | + import { propTypes } from '/@/utils/propTypes'; | |
11 | 13 | export default defineComponent({ |
12 | 14 | name: 'EditTableHeaderIcon', |
13 | 15 | components: { FormOutlined }, |
14 | 16 | props: { |
15 | - title: { | |
16 | - type: String as PropType<string>, | |
17 | - default: '', | |
18 | - }, | |
17 | + title: propTypes.string.def(''), | |
19 | 18 | }, |
20 | 19 | }); |
21 | 20 | </script> |
... | ... |
src/components/Table/src/components/HeaderCell.vue
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | import { defineComponent, computed } from 'vue'; |
13 | 13 | import BasicHelp from '/@/components/Basic/src/BasicHelp.vue'; |
14 | 14 | import EditTableHeaderCell from './EditTableHeaderIcon.vue'; |
15 | + | |
15 | 16 | import { useDesign } from '/@/hooks/web/useDesign'; |
16 | 17 | export default defineComponent({ |
17 | 18 | name: 'TableHeaderCell', |
... | ... | @@ -27,17 +28,10 @@ |
27 | 28 | }, |
28 | 29 | setup(props) { |
29 | 30 | const { prefixCls } = useDesign('basic-table-header-cell'); |
30 | - const getIsEdit = computed(() => { | |
31 | - return !!props.column?.edit; | |
32 | - }); | |
33 | - | |
34 | - const getTitle = computed(() => { | |
35 | - return props.column?.customTitle; | |
36 | - }); | |
37 | 31 | |
38 | - const getHelpMessage = computed(() => { | |
39 | - return props.column?.helpMessage; | |
40 | - }); | |
32 | + const getIsEdit = computed(() => !!props.column?.edit); | |
33 | + const getTitle = computed(() => props.column?.customTitle); | |
34 | + const getHelpMessage = computed(() => props.column?.helpMessage); | |
41 | 35 | |
42 | 36 | return { prefixCls, getIsEdit, getTitle, getHelpMessage }; |
43 | 37 | }, |
... | ... |
src/components/Table/src/components/TableAction.vue
... | ... | @@ -19,17 +19,21 @@ |
19 | 19 | </div> |
20 | 20 | </template> |
21 | 21 | <script lang="ts"> |
22 | - import { defineComponent, PropType, computed } from 'vue'; | |
22 | + import { defineComponent, PropType, computed, toRaw } from 'vue'; | |
23 | + | |
24 | + import { MoreOutlined } from '@ant-design/icons-vue'; | |
23 | 25 | import Icon from '/@/components/Icon/index'; |
24 | 26 | import { ActionItem, TableActionType } from '/@/components/Table'; |
25 | 27 | import { PopConfirmButton } from '/@/components/Button'; |
26 | 28 | import { Divider } from 'ant-design-vue'; |
27 | 29 | import { Dropdown } from '/@/components/Dropdown'; |
30 | + | |
28 | 31 | import { useDesign } from '/@/hooks/web/useDesign'; |
29 | - import { MoreOutlined } from '@ant-design/icons-vue'; | |
30 | - import { propTypes } from '/@/utils/propTypes'; | |
31 | 32 | import { useTableContext } from '../hooks/useTableContext'; |
33 | + | |
34 | + import { propTypes } from '/@/utils/propTypes'; | |
32 | 35 | import { ACTION_COLUMN_FLAG } from '../const'; |
36 | + | |
33 | 37 | export default defineComponent({ |
34 | 38 | name: 'TableAction', |
35 | 39 | components: { Icon, PopConfirmButton, Divider, Dropdown, MoreOutlined }, |
... | ... | @@ -52,26 +56,12 @@ |
52 | 56 | table = useTableContext(); |
53 | 57 | } |
54 | 58 | |
55 | - // const getSize = computed(() => { | |
56 | - // const size = table?.getSize?.(); | |
57 | - // if (size === 'middle' || !size) { | |
58 | - // return; | |
59 | - // } | |
60 | - | |
61 | - // if (size === 'default') { | |
62 | - // return 'large'; | |
63 | - // } | |
64 | - // return size; | |
65 | - // }); | |
66 | - | |
67 | 59 | const getActions = computed(() => { |
68 | - return (props.actions || []).map((action) => { | |
60 | + return (toRaw(props.actions) || []).map((action) => { | |
69 | 61 | const { popConfirm } = action; |
70 | - // const size = unref(getSize); | |
71 | 62 | return { |
72 | 63 | type: 'link', |
73 | 64 | size: 'small', |
74 | - // ...(size ? { size } : {}), | |
75 | 65 | ...action, |
76 | 66 | ...(popConfirm || {}), |
77 | 67 | onConfirm: popConfirm?.confirm, |
... | ... | @@ -82,7 +72,7 @@ |
82 | 72 | }); |
83 | 73 | |
84 | 74 | const getDropList = computed(() => { |
85 | - return (props.dropDownActions || []).map((action, index) => { | |
75 | + return (toRaw(props.dropDownActions) || []).map((action, index) => { | |
86 | 76 | const { label } = action; |
87 | 77 | return { |
88 | 78 | ...action, |
... | ... |
src/components/Table/src/components/TableHeader.vue
1 | 1 | <template> |
2 | 2 | <slot name="tableTitle" v-if="$slots.tableTitle"></slot> |
3 | + | |
3 | 4 | <TableTitle :helpMessage="titleHelpMessage" :title="title" v-if="!$slots.tableTitle && title" /> |
4 | 5 | |
5 | 6 | <div :class="`${prefixCls}__toolbar`"> |
... | ... | @@ -11,19 +12,20 @@ |
11 | 12 | <script lang="ts"> |
12 | 13 | import type { TableSetting } from '../types/table'; |
13 | 14 | import type { PropType } from 'vue'; |
14 | - import { Divider } from 'ant-design-vue'; | |
15 | + | |
15 | 16 | import { defineComponent } from 'vue'; |
17 | + import { Divider } from 'ant-design-vue'; | |
18 | + import TableSettingComponent from './settings/index.vue'; | |
19 | + import TableTitle from './TableTitle.vue'; | |
16 | 20 | |
17 | 21 | import { useDesign } from '/@/hooks/web/useDesign'; |
18 | - import TableSettingComp from './settings/index.vue'; | |
19 | - import TableTitle from './TableTitle.vue'; | |
20 | 22 | |
21 | 23 | export default defineComponent({ |
22 | 24 | name: 'BasicTableHeader', |
23 | 25 | components: { |
24 | 26 | Divider, |
25 | 27 | TableTitle, |
26 | - TableSetting: TableSettingComp, | |
28 | + TableSetting: TableSettingComponent, | |
27 | 29 | }, |
28 | 30 | props: { |
29 | 31 | title: { |
... | ... |
src/components/Table/src/components/TableImg.vue
src/components/Table/src/components/editable/EditableCell.vue
... | ... | @@ -83,18 +83,6 @@ |
83 | 83 | return unref(ruleMessage) && unref(ruleVisible); |
84 | 84 | }); |
85 | 85 | |
86 | - // const getSize = computed(() => { | |
87 | - // const size = table?.getSize?.(); | |
88 | - // if (size === 'middle' || !size) { | |
89 | - // return; | |
90 | - // } | |
91 | - | |
92 | - // if (size === 'default') { | |
93 | - // return 'large'; | |
94 | - // } | |
95 | - // return size; | |
96 | - // }); | |
97 | - | |
98 | 86 | const getIsCheckComp = computed(() => { |
99 | 87 | const component = unref(getComponent); |
100 | 88 | return ['Checkbox', 'Switch'].includes(component); |
... | ... |
src/components/Table/src/components/settings/ColumnSetting.vue
src/components/Table/src/components/settings/FullScreenSetting.vue
... | ... | @@ -3,17 +3,18 @@ |
3 | 3 | <template #title> |
4 | 4 | <span>{{ t('component.table.settingFullScreen') }}</span> |
5 | 5 | </template> |
6 | - <FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" /> | |
7 | - <FullscreenExitOutlined @click="handleFullScreen" v-else /> | |
6 | + <FullscreenOutlined @click="toggleFullscreen" v-if="!isFullscreenRef" /> | |
7 | + <FullscreenExitOutlined @click="toggleFullscreen" v-else /> | |
8 | 8 | </Tooltip> |
9 | 9 | </template> |
10 | 10 | <script lang="ts"> |
11 | 11 | import { defineComponent } from 'vue'; |
12 | - import { useTableContext } from '../../hooks/useTableContext'; | |
13 | 12 | import { Tooltip } from 'ant-design-vue'; |
14 | 13 | import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue'; |
14 | + | |
15 | 15 | import { useFullscreen } from '/@/hooks/web/useFullScreen'; |
16 | 16 | import { useI18n } from '/@/hooks/web/useI18n'; |
17 | + import { useTableContext } from '../../hooks/useTableContext'; | |
17 | 18 | |
18 | 19 | export default defineComponent({ |
19 | 20 | name: 'FullScreenSetting', |
... | ... | @@ -26,15 +27,10 @@ |
26 | 27 | setup() { |
27 | 28 | const table = useTableContext(); |
28 | 29 | const { t } = useI18n(); |
29 | - | |
30 | 30 | const { toggleFullscreen, isFullscreenRef } = useFullscreen(table.wrapRef); |
31 | 31 | |
32 | - function handleFullScreen() { | |
33 | - toggleFullscreen(); | |
34 | - } | |
35 | - | |
36 | 32 | return { |
37 | - handleFullScreen, | |
33 | + toggleFullscreen, | |
38 | 34 | isFullscreenRef, |
39 | 35 | t, |
40 | 36 | }; |
... | ... |
src/components/Table/src/components/settings/RedoSetting.vue
... | ... | @@ -8,10 +8,11 @@ |
8 | 8 | </template> |
9 | 9 | <script lang="ts"> |
10 | 10 | import { defineComponent } from 'vue'; |
11 | - import { useTableContext } from '../../hooks/useTableContext'; | |
12 | 11 | import { Tooltip } from 'ant-design-vue'; |
13 | 12 | import { RedoOutlined } from '@ant-design/icons-vue'; |
13 | + | |
14 | 14 | import { useI18n } from '/@/hooks/web/useI18n'; |
15 | + import { useTableContext } from '../../hooks/useTableContext'; | |
15 | 16 | |
16 | 17 | export default defineComponent({ |
17 | 18 | name: 'RedoSetting', |
... | ... | @@ -19,7 +20,6 @@ |
19 | 20 | RedoOutlined, |
20 | 21 | Tooltip, |
21 | 22 | }, |
22 | - | |
23 | 23 | setup() { |
24 | 24 | const table = useTableContext(); |
25 | 25 | const { t } = useI18n(); |
... | ... | @@ -28,10 +28,7 @@ |
28 | 28 | table.reload(); |
29 | 29 | } |
30 | 30 | |
31 | - return { | |
32 | - redo, | |
33 | - t, | |
34 | - }; | |
31 | + return { redo, t }; | |
35 | 32 | }, |
36 | 33 | }); |
37 | 34 | </script> |
... | ... |
src/components/Table/src/components/settings/SizeSetting.vue
... | ... | @@ -23,15 +23,16 @@ |
23 | 23 | </Tooltip> |
24 | 24 | </template> |
25 | 25 | <script lang="ts"> |
26 | + import type { SizeType } from '../../types/table'; | |
27 | + | |
26 | 28 | import { defineComponent, ref } from 'vue'; |
27 | - import { useTableContext } from '../../hooks/useTableContext'; | |
28 | 29 | import { Tooltip, Dropdown, Menu } from 'ant-design-vue'; |
29 | 30 | import { ColumnHeightOutlined } from '@ant-design/icons-vue'; |
31 | + | |
30 | 32 | import { useI18n } from '/@/hooks/web/useI18n'; |
33 | + import { useTableContext } from '../../hooks/useTableContext'; | |
31 | 34 | import { getPopupContainer } from '/@/utils'; |
32 | 35 | |
33 | - import type { SizeType } from '../../types/table'; | |
34 | - | |
35 | 36 | export default defineComponent({ |
36 | 37 | name: 'SizeSetting', |
37 | 38 | components: { |
... | ... |
src/components/Table/src/components/settings/index.vue
... | ... | @@ -7,13 +7,18 @@ |
7 | 7 | </div> |
8 | 8 | </template> |
9 | 9 | <script lang="ts"> |
10 | - import { defineComponent, PropType, computed } from 'vue'; | |
10 | + import type { PropType } from 'vue'; | |
11 | 11 | import type { TableSetting } from '../../types/table'; |
12 | - import { useI18n } from '/@/hooks/web/useI18n'; | |
12 | + | |
13 | + import { defineComponent, computed } from 'vue'; | |
14 | + | |
13 | 15 | import ColumnSetting from './ColumnSetting.vue'; |
14 | 16 | import SizeSetting from './SizeSetting.vue'; |
15 | 17 | import RedoSetting from './RedoSetting.vue'; |
16 | 18 | import FullScreenSetting from './FullScreenSetting.vue'; |
19 | + | |
20 | + import { useI18n } from '/@/hooks/web/useI18n'; | |
21 | + | |
17 | 22 | export default defineComponent({ |
18 | 23 | name: 'TableSetting', |
19 | 24 | components: { |
... | ... |
src/components/Table/src/const.ts
... | ... | @@ -6,22 +6,21 @@ const { pageSizeOptions, defaultPageSize, fetchSetting, defaultSortFn, defaultFi |
6 | 6 | |
7 | 7 | export const ROW_KEY = 'key'; |
8 | 8 | |
9 | -// 可选的每页显示条数; | |
9 | +// Optional display number per page; | |
10 | 10 | export const PAGE_SIZE_OPTIONS = pageSizeOptions; |
11 | 11 | |
12 | -// 每页显示条数 | |
12 | +// Number of items displayed per page | |
13 | 13 | export const PAGE_SIZE = defaultPageSize; |
14 | 14 | |
15 | -// 通用接口字段设置 | |
15 | +// Common interface field settings | |
16 | 16 | export const FETCH_SETTING = fetchSetting; |
17 | 17 | |
18 | -// 配置通用排序函数 | |
18 | +// Configure general sort function | |
19 | 19 | export const DEFAULT_SORT_FN = defaultSortFn; |
20 | 20 | |
21 | 21 | export const DEFAULT_FILTER_FN = defaultFilterFn; |
22 | 22 | |
23 | -// 表格单元格默认布局 | |
23 | +// Default layout of table cells | |
24 | 24 | export const DEFAULT_ALIGN = 'center'; |
25 | - | |
26 | 25 | export const INDEX_COLUMN_FLAG = 'INDEX'; |
27 | 26 | export const ACTION_COLUMN_FLAG = 'ACTION'; |
... | ... |
src/components/Table/src/hooks/useColumns.ts
1 | 1 | import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table'; |
2 | 2 | import type { PaginationProps } from '../types/pagination'; |
3 | -import { unref, ComputedRef, Ref, computed, watch, ref, toRaw } from 'vue'; | |
4 | -import { isBoolean, isArray, isString, isObject } from '/@/utils/is'; | |
5 | -import { DEFAULT_ALIGN, PAGE_SIZE, INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG } from '../const'; | |
3 | +import type { ComputedRef } from 'vue'; | |
4 | + | |
5 | +import { unref, Ref, computed, watch, ref, toRaw } from 'vue'; | |
6 | + | |
7 | +import { renderEditCell } from '../components/editable'; | |
8 | + | |
6 | 9 | import { useI18n } from '/@/hooks/web/useI18n'; |
10 | + | |
11 | +import { isBoolean, isArray, isString, isObject, isFunction } from '/@/utils/is'; | |
7 | 12 | import { isEqual, cloneDeep } from 'lodash-es'; |
8 | -import { isFunction } from '/@/utils/is'; | |
9 | 13 | import { formatToDate } from '/@/utils/dateUtil'; |
10 | -import { renderEditCell } from '../components/editable'; | |
11 | 14 | |
12 | -const { t } = useI18n(); | |
15 | +import { DEFAULT_ALIGN, PAGE_SIZE, INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG } from '../const'; | |
13 | 16 | |
14 | 17 | function handleItem(item: BasicColumn, ellipsis: boolean) { |
15 | 18 | const { key, dataIndex, children } = item; |
... | ... | @@ -43,6 +46,8 @@ function handleIndexColumn( |
43 | 46 | getPaginationRef: ComputedRef<boolean | PaginationProps>, |
44 | 47 | columns: BasicColumn[] |
45 | 48 | ) { |
49 | + const { t } = useI18n(); | |
50 | + | |
46 | 51 | const { showIndexColumn, indexColumnProps, isTreeTable } = unref(propsRef); |
47 | 52 | |
48 | 53 | let pushIndexColumns = false; |
... | ... | @@ -163,15 +168,6 @@ export function useColumns( |
163 | 168 | } |
164 | 169 | ); |
165 | 170 | |
166 | - // watchEffect(() => { | |
167 | - // const columns = toRaw(unref(propsRef).columns); | |
168 | - // console.log('======================'); | |
169 | - // console.log(111); | |
170 | - // console.log('======================'); | |
171 | - // columnsRef.value = columns; | |
172 | - // cacheColumns = columns?.filter((item) => !item.flag) ?? []; | |
173 | - // }); | |
174 | - | |
175 | 171 | function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) { |
176 | 172 | if (!dataIndex || !value) { |
177 | 173 | return; |
... | ... |
src/components/Table/src/hooks/useDataSource.ts
... | ... | @@ -52,11 +52,6 @@ export function useDataSource( |
52 | 52 | }); |
53 | 53 | const dataSourceRef = ref<Recordable[]>([]); |
54 | 54 | |
55 | - // watchEffect(() => { | |
56 | - // const { dataSource, api } = unref(propsRef); | |
57 | - // !api && dataSource && (dataSourceRef.value = dataSource); | |
58 | - // }); | |
59 | - | |
60 | 55 | watchEffect(() => { |
61 | 56 | tableData.value = unref(dataSourceRef); |
62 | 57 | }); |
... | ... |
src/components/Table/src/hooks/useLoading.ts
... | ... | @@ -11,9 +11,7 @@ export function useLoading(props: ComputedRef<BasicTableProps>) { |
11 | 11 | } |
12 | 12 | ); |
13 | 13 | |
14 | - const getLoading = computed(() => { | |
15 | - return unref(loadingRef); | |
16 | - }); | |
14 | + const getLoading = computed(() => unref(loadingRef)); | |
17 | 15 | |
18 | 16 | function setLoading(loading: boolean) { |
19 | 17 | loadingRef.value = loading; |
... | ... |
src/components/Table/src/hooks/usePagination.tsx
... | ... | @@ -25,11 +25,11 @@ function itemRender({ page, type, originalElement }: ItemRender) { |
25 | 25 | } |
26 | 26 | |
27 | 27 | export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
28 | - const configRef = ref<PaginationProps>({}); | |
28 | + const { t } = useI18n(); | |
29 | 29 | |
30 | + const configRef = ref<PaginationProps>({}); | |
30 | 31 | const show = ref(true); |
31 | 32 | |
32 | - const { t } = useI18n(); | |
33 | 33 | const getPaginationInfo = computed((): PaginationProps | boolean => { |
34 | 34 | const { pagination } = unref(refProps); |
35 | 35 | |
... | ... |
src/components/Table/src/hooks/useTable.ts
1 | 1 | import type { BasicTableProps, TableActionType, FetchParams, BasicColumn } from '../types/table'; |
2 | 2 | import type { PaginationProps } from '../types/pagination'; |
3 | 3 | import type { DynamicProps } from '/#/utils'; |
4 | -import { getDynamicProps } from '/@/utils'; | |
4 | +import type { FormActionType } from '/@/components/Form'; | |
5 | +import type { WatchStopHandle } from 'vue'; | |
5 | 6 | |
7 | +import { getDynamicProps } from '/@/utils'; | |
6 | 8 | import { ref, onUnmounted, unref, watch, toRaw } from 'vue'; |
7 | 9 | import { isProdMode } from '/@/utils/env'; |
8 | 10 | import { error } from '/@/utils/log'; |
9 | -import type { FormActionType } from '/@/components/Form'; | |
10 | 11 | |
11 | 12 | type Props = Partial<DynamicProps<BasicTableProps>>; |
12 | 13 | |
... | ... | @@ -21,6 +22,8 @@ export function useTable( |
21 | 22 | const loadedRef = ref<Nullable<boolean>>(false); |
22 | 23 | const formRef = ref<Nullable<UseTableMethod>>(null); |
23 | 24 | |
25 | + let stopWatch: WatchStopHandle; | |
26 | + | |
24 | 27 | function register(instance: TableActionType, formInstance: UseTableMethod) { |
25 | 28 | isProdMode() && |
26 | 29 | onUnmounted(() => { |
... | ... | @@ -28,15 +31,16 @@ export function useTable( |
28 | 31 | loadedRef.value = null; |
29 | 32 | }); |
30 | 33 | |
31 | - if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) { | |
32 | - return; | |
33 | - } | |
34 | + if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) return; | |
35 | + | |
34 | 36 | tableRef.value = instance; |
35 | 37 | formRef.value = formInstance; |
36 | 38 | tableProps && instance.setProps(getDynamicProps(tableProps)); |
37 | 39 | loadedRef.value = true; |
38 | 40 | |
39 | - watch( | |
41 | + stopWatch?.(); | |
42 | + | |
43 | + stopWatch = watch( | |
40 | 44 | () => tableProps, |
41 | 45 | () => { |
42 | 46 | tableProps && instance.setProps(getDynamicProps(tableProps)); |
... | ... | @@ -128,6 +132,12 @@ export function useTable( |
128 | 132 | getShowPagination: () => { |
129 | 133 | return toRaw(getTableInstance().getShowPagination()); |
130 | 134 | }, |
135 | + expandAll: () => { | |
136 | + getTableInstance().expandAll(); | |
137 | + }, | |
138 | + collapseAll: () => { | |
139 | + getTableInstance().collapseAll(); | |
140 | + }, | |
131 | 141 | }; |
132 | 142 | |
133 | 143 | return [register, methods]; |
... | ... |
src/components/Table/src/hooks/useTableExpand.ts
0 → 100644
1 | +import type { ComputedRef, Ref } from 'vue'; | |
2 | +import type { BasicTableProps } from '../types/table'; | |
3 | + | |
4 | +import { computed, unref, ref, toRaw } from 'vue'; | |
5 | +import { ROW_KEY } from '../const'; | |
6 | + | |
7 | +export function useTableExpand( | |
8 | + propsRef: ComputedRef<BasicTableProps>, | |
9 | + tableData: Ref<Recordable[]>, | |
10 | + emit: EmitType | |
11 | +) { | |
12 | + const expandedRowKeys = ref<string[]>([]); | |
13 | + | |
14 | + const getAutoCreateKey = computed(() => { | |
15 | + return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey; | |
16 | + }); | |
17 | + | |
18 | + const getRowKey = computed(() => { | |
19 | + const { rowKey } = unref(propsRef); | |
20 | + return unref(getAutoCreateKey) ? ROW_KEY : rowKey; | |
21 | + }); | |
22 | + | |
23 | + const getExpandOption = computed(() => { | |
24 | + const { isTreeTable } = unref(propsRef); | |
25 | + if (!isTreeTable) return {}; | |
26 | + | |
27 | + return { | |
28 | + expandedRowKeys: unref(expandedRowKeys), | |
29 | + onExpandedRowsChange: (keys: string[]) => { | |
30 | + expandedRowKeys.value = keys; | |
31 | + emit('expanded-rows-change', keys); | |
32 | + }, | |
33 | + }; | |
34 | + }); | |
35 | + | |
36 | + function expandAll() { | |
37 | + const keys = getAllKeys(); | |
38 | + expandedRowKeys.value = keys; | |
39 | + } | |
40 | + | |
41 | + function getAllKeys(data?: Recordable[]) { | |
42 | + const keys: string[] = []; | |
43 | + const { childrenColumnName } = unref(propsRef); | |
44 | + toRaw(data || unref(tableData)).forEach((item) => { | |
45 | + keys.push(item[unref(getRowKey) as string]); | |
46 | + const children = item[childrenColumnName || 'children']; | |
47 | + if (children?.length) { | |
48 | + keys.push(...getAllKeys(children)); | |
49 | + } | |
50 | + }); | |
51 | + return keys; | |
52 | + } | |
53 | + | |
54 | + function collapseAll() { | |
55 | + expandedRowKeys.value = []; | |
56 | + } | |
57 | + | |
58 | + return { getExpandOption, expandAll, collapseAll }; | |
59 | +} | |
... | ... |
src/components/Table/src/hooks/useTableHeader.ts
1 | 1 | import type { ComputedRef, Slots } from 'vue'; |
2 | 2 | import type { BasicTableProps } from '../types/table'; |
3 | + | |
3 | 4 | import { unref, computed, h } from 'vue'; |
4 | -import { isString } from '/@/utils/is'; | |
5 | 5 | import TableHeader from '../components/TableHeader.vue'; |
6 | -import { getSlot } from '../../../../utils/helper/tsxHelper'; | |
6 | + | |
7 | +import { isString } from '/@/utils/is'; | |
8 | +import { getSlot } from '/@/utils/helper/tsxHelper'; | |
7 | 9 | |
8 | 10 | export function useTableHeader(propsRef: ComputedRef<BasicTableProps>, slots: Slots) { |
9 | 11 | const getHeaderProps = computed( |
... | ... |
src/components/Table/src/hooks/useTableStyle.ts
1 | 1 | import type { ComputedRef } from 'vue'; |
2 | 2 | import type { BasicTableProps, TableCustomRecord } from '../types/table'; |
3 | + | |
3 | 4 | import { unref } from 'vue'; |
4 | 5 | import { isFunction } from '/@/utils/is'; |
5 | 6 | export function useTableStyle(propsRef: ComputedRef<BasicTableProps>, prefixCls: string) { |
... | ... |
src/components/Table/src/props.ts
... | ... | @@ -12,45 +12,32 @@ import type { FormProps } from '/@/components/Form'; |
12 | 12 | import { DEFAULT_FILTER_FN, DEFAULT_SORT_FN, FETCH_SETTING } from './const'; |
13 | 13 | import { propTypes } from '/@/utils/propTypes'; |
14 | 14 | |
15 | -// 注释看 types/table | |
16 | 15 | export const basicProps = { |
17 | 16 | clickToRowSelect: propTypes.bool.def(true), |
18 | - | |
19 | 17 | isTreeTable: propTypes.bool.def(false), |
20 | - | |
21 | - tableSetting: { | |
22 | - type: Object as PropType<TableSetting>, | |
23 | - }, | |
24 | - | |
18 | + tableSetting: propTypes.shape<TableSetting>({}), | |
25 | 19 | inset: propTypes.bool, |
26 | - | |
27 | 20 | sortFn: { |
28 | 21 | type: Function as PropType<(sortInfo: SorterResult) => any>, |
29 | 22 | default: DEFAULT_SORT_FN, |
30 | 23 | }, |
31 | - | |
32 | 24 | filterFn: { |
33 | 25 | type: Function as PropType<(data: Partial<Recordable<string[]>>) => any>, |
34 | 26 | default: DEFAULT_FILTER_FN, |
35 | 27 | }, |
36 | - | |
37 | 28 | showTableSetting: propTypes.bool, |
38 | 29 | autoCreateKey: propTypes.bool.def(true), |
39 | 30 | striped: propTypes.bool.def(true), |
40 | 31 | showSummary: propTypes.bool, |
41 | - | |
42 | 32 | summaryFunc: { |
43 | 33 | type: [Function, Array] as PropType<(...arg: any[]) => any[]>, |
44 | 34 | default: null, |
45 | 35 | }, |
46 | - | |
47 | 36 | summaryData: { |
48 | 37 | type: Array as PropType<Recordable[]>, |
49 | 38 | default: null, |
50 | 39 | }, |
51 | - | |
52 | 40 | indentSize: propTypes.number.def(24), |
53 | - | |
54 | 41 | canColDrag: propTypes.bool.def(true), |
55 | 42 | api: { |
56 | 43 | type: Function as PropType<(...arg: any[]) => Promise<any>>, |
... | ... |
src/components/Table/src/style/index.less deleted
100644 → 0
1 | -@border-color: #cecece4d; | |
2 | - | |
3 | -@prefix-cls: ~'@{namespace}-basic-table'; | |
4 | - | |
5 | -.@{prefix-cls} { | |
6 | - &-form-container { | |
7 | - padding: 16px; | |
8 | - | |
9 | - .ant-form { | |
10 | - padding: 12px 10px 6px 10px; | |
11 | - margin-bottom: 16px; | |
12 | - background: #fff; | |
13 | - border-radius: 4px; | |
14 | - } | |
15 | - | |
16 | - // .ant-table-wrapper { | |
17 | - // border-radius: 2px; | |
18 | - // } | |
19 | - } | |
20 | - | |
21 | - &-row__striped { | |
22 | - td { | |
23 | - background: #fafafa; | |
24 | - } | |
25 | - } | |
26 | - | |
27 | - &--inset { | |
28 | - .ant-table-wrapper { | |
29 | - padding: 0; | |
30 | - } | |
31 | - } | |
32 | - | |
33 | - .ant-tag { | |
34 | - margin-right: 0; | |
35 | - } | |
36 | - | |
37 | - .ant-table-wrapper { | |
38 | - padding: 6px; | |
39 | - background: #fff; | |
40 | - border-radius: 2px; | |
41 | - | |
42 | - .ant-table-title { | |
43 | - padding: 0 0 8px 0 !important; | |
44 | - } | |
45 | - | |
46 | - .ant-table.ant-table-bordered .ant-table-title { | |
47 | - border: none !important; | |
48 | - } | |
49 | - } | |
50 | - | |
51 | - // | |
52 | - .ant-table { | |
53 | - width: 100%; | |
54 | - overflow-x: hidden; | |
55 | - // border: none; | |
56 | - | |
57 | - &-title { | |
58 | - display: flex; | |
59 | - padding: 8px 6px; | |
60 | - border-bottom: none; | |
61 | - justify-content: space-between; | |
62 | - align-items: center; | |
63 | - } | |
64 | - | |
65 | - // .ant-table-thead > tr > th, | |
66 | - // .ant-table-header { | |
67 | - // background: #f1f3f4; | |
68 | - // background-color: #f1f3f4 !important; | |
69 | - // } | |
70 | - | |
71 | - .ant-table-tbody > tr.ant-table-row-selected td { | |
72 | - background: fade(@primary-color, 8%) !important; | |
73 | - } | |
74 | - } | |
75 | - | |
76 | - // .ant-table-tbody > tr > td, | |
77 | - // .ant-table-tbody > tr > th, | |
78 | - // .ant-table-thead > tr > td, | |
79 | - // .ant-table-thead > tr > th { | |
80 | - // white-space: pre; | |
81 | - // } | |
82 | - | |
83 | - .ant-pagination { | |
84 | - margin: 10px 0 0 0; | |
85 | - } | |
86 | - | |
87 | - .ant-table-footer { | |
88 | - padding: 0; | |
89 | - | |
90 | - .ant-table-wrapper { | |
91 | - padding: 0; | |
92 | - } | |
93 | - | |
94 | - table { | |
95 | - border: none !important; | |
96 | - } | |
97 | - | |
98 | - .ant-table-body { | |
99 | - overflow-x: hidden !important; | |
100 | - overflow-y: scroll !important; | |
101 | - } | |
102 | - | |
103 | - td { | |
104 | - padding: 12px 8px; | |
105 | - } | |
106 | - } | |
107 | -} |
src/components/Table/src/types/table.ts
... | ... | @@ -87,6 +87,8 @@ export interface TableActionType { |
87 | 87 | reload: (opt?: FetchParams) => Promise<void>; |
88 | 88 | getSelectRows: <T = Recordable>() => T[]; |
89 | 89 | clearSelectedRowKeys: () => void; |
90 | + expandAll: () => void; | |
91 | + collapseAll: () => void; | |
90 | 92 | getSelectRowKeys: () => string[]; |
91 | 93 | deleteSelectRowByKey: (key: string) => void; |
92 | 94 | setPagination: (info: Partial<PaginationProps>) => void; |
... | ... | @@ -208,7 +210,7 @@ export interface BasicTableProps<T = any> { |
208 | 210 | * @default 'children' |
209 | 211 | * @type string | string[] |
210 | 212 | */ |
211 | - childrenColumnName?: string | string[]; | |
213 | + childrenColumnName?: string; | |
212 | 214 | |
213 | 215 | /** |
214 | 216 | * Override default table elements |
... | ... |
src/views/demo/table/TreeTable.vue
1 | 1 | <template> |
2 | 2 | <div class="p-4"> |
3 | - <BasicTable | |
4 | - :rowSelection="{ type: 'checkbox' }" | |
5 | - title="树形表格" | |
6 | - titleHelpMessage="树形组件不能和序列号列同时存在" | |
7 | - :columns="columns" | |
8 | - :dataSource="data" | |
9 | - rowKey="id" | |
10 | - :indentSize="20" | |
11 | - isTreeTable | |
12 | - /> | |
3 | + <BasicTable @register="register"> | |
4 | + <template #toolbar> | |
5 | + <a-button type="primary" @click="expandAll">展开全部</a-button> | |
6 | + <a-button type="primary" @click="collapseAll">折叠全部</a-button> | |
7 | + </template> | |
8 | + </BasicTable> | |
13 | 9 | </div> |
14 | 10 | </template> |
15 | 11 | <script lang="ts"> |
16 | 12 | import { defineComponent } from 'vue'; |
17 | - import { BasicTable } from '/@/components/Table'; | |
13 | + import { BasicTable, useTable } from '/@/components/Table'; | |
18 | 14 | import { getBasicColumns, getTreeTableData } from './tableData'; |
19 | 15 | |
20 | 16 | export default defineComponent({ |
21 | 17 | components: { BasicTable }, |
22 | 18 | setup() { |
23 | - return { | |
19 | + const [register, { expandAll, collapseAll }] = useTable({ | |
20 | + title: '树形表格', | |
21 | + isTreeTable: true, | |
22 | + rowSelection: { type: 'checkbox' }, | |
23 | + titleHelpMessage: '树形组件不能和序列号列同时存在', | |
24 | 24 | columns: getBasicColumns(), |
25 | - data: getTreeTableData(), | |
26 | - }; | |
25 | + dataSource: getTreeTableData(), | |
26 | + rowKey: 'id', | |
27 | + }); | |
28 | + return { register, expandAll, collapseAll }; | |
27 | 29 | }, |
28 | 30 | }); |
29 | 31 | </script> |
... | ... |
yarn.lock
... | ... | @@ -2180,10 +2180,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: |
2180 | 2180 | dependencies: |
2181 | 2181 | color-convert "^2.0.1" |
2182 | 2182 | |
2183 | -ant-design-vue@2.0.1: | |
2184 | - version "2.0.1" | |
2185 | - resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.1.tgz#3a5964523aac10fd2b16d84d651145cd2b65f1d5" | |
2186 | - integrity sha512-CFIF+srTui4ZwdKPBXNoFA9/0fkSpypanQeOts0PAq1vEuMLxUoZHapDDn7wzsxZH3sYLF+mvMp8gYMRkaNn+w== | |
2183 | +ant-design-vue@2.1.0: | |
2184 | + version "2.1.0" | |
2185 | + resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.1.0.tgz#2489240f638f39874281e237544b857ebce52d18" | |
2186 | + integrity sha512-wzgwHRuwZrSvixccNlvas2gTWBkmfMrifbSsP+ga8VV6F0C6DdlimeFo+P99AxnVgpNVk8OUq9RVDQjb1UGk6g== | |
2187 | 2187 | dependencies: |
2188 | 2188 | "@ant-design-vue/use" "^0.0.1-0" |
2189 | 2189 | "@ant-design/icons-vue" "^6.0.0" |
... | ... |