Commit 391da9ec2884885f9dfe86ddb869ccc0d193491e

Authored by Vben
1 parent b2a1951f

feat(table): add expandAll/collapseAll function close #333

CHANGELOG.zh_CN.md
... ... @@ -3,6 +3,7 @@
3 3 ### ✨ Features
4 4  
5 5 - 路由新增 hideChildrenInMenu 配置。用于隐藏子菜单
  6 +- 树形表格内置展开/折叠全部函数
6 7  
7 8 ### ✨ Refactor
8 9  
... ...
package.json
... ... @@ -35,7 +35,7 @@
35 35 "@iconify/iconify": "^2.0.0-rc.6",
36 36 "@vueuse/core": "^4.4.1",
37 37 "@zxcvbn-ts/core": "^0.3.0",
38   - "ant-design-vue": "2.0.1",
  38 + "ant-design-vue": "2.1.0",
39 39 "apexcharts": "^3.26.0",
40 40 "axios": "^0.21.1",
41 41 "crypto-js": "^4.0.0",
... ...
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
... ... @@ -31,8 +31,8 @@
31 31 const getWrapStyle = computed(
32 32 (): CSSProperties => {
33 33 const { size } = props;
34   - const wh = `${size}px`;
35   - return { height: wh, width: wh };
  34 + const s = `${size}px`;
  35 + return { height: s, width: s };
36 36 }
37 37 );
38 38  
... ...
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
... ... @@ -3,7 +3,6 @@
3 3 <template #title>
4 4 <span>{{ t('component.table.settingColumn') }}</span>
5 5 </template>
6   - <!-- :getPopupContainer="getPopupContainer" -->
7 6 <Popover
8 7 placement="bottomLeft"
9 8 trigger="click"
... ...
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&lt;BasicTableProps&gt;) {
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 &#39;/@/components/Form&#39;;
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&lt;T = any&gt; {
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"
... ...