@qin-ui/antd-vue-pro 2.4.5 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-CONTEXT.md +27 -3
- package/README.md +45 -38
- package/api.json +24 -9
- package/es/antd-vue-pro.css +15 -15
- package/es/components/component-provider/components/index.vue.d.ts +3 -3
- package/es/components/component-provider/components/index.vue.d.ts.map +1 -1
- package/es/components/component-provider/constants/index.d.ts +20 -4
- package/es/components/component-provider/constants/index.d.ts.map +1 -1
- package/es/components/component-provider/types/index.d.ts +7 -3
- package/es/components/component-provider/types/index.d.ts.map +1 -1
- package/es/components/form/components/BaseField/index.vue.d.ts +7 -1
- package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
- package/es/components/form/components/BaseField/utils/index.d.ts +18 -0
- package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -1
- package/es/components/form/components/BaseFormItem/index.vue.d.ts +3 -0
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
- package/es/components/form/components/ContainerFragment/index.vue.d.ts +1 -0
- package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -1
- package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
- package/es/components/form/components/SlotComponent/index.vue.d.ts +1 -0
- package/es/components/form/components/SlotComponent/index.vue.d.ts.map +1 -1
- package/es/components/form/components/index.d.ts +4 -0
- package/es/components/form/components/index.d.ts.map +1 -1
- package/es/components/form/constants/index.d.ts.map +1 -1
- package/es/components/form/hooks/useFields.d.ts +2 -0
- package/es/components/form/hooks/useFields.d.ts.map +1 -1
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts +10 -0
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
- package/es/components/form/index.d.ts +2 -2
- package/es/components/form/index.d.ts.map +1 -1
- package/es/components/form/types/index.d.ts +75 -44
- package/es/components/form/types/index.d.ts.map +1 -1
- package/es/components/form/utils/fieldKey.d.ts +13 -0
- package/es/components/form/utils/fieldKey.d.ts.map +1 -0
- package/es/components/table/components/ColumnControl.vue.d.ts +3 -0
- package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
- package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -1
- package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -1
- package/es/components/table/components/ProTable.vue.d.ts +8 -1
- package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
- package/es/components/table/components/SearchForm.vue.d.ts +9 -1
- package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
- package/es/components/table/components/SizeControl.vue.d.ts +7 -2
- package/es/components/table/components/SizeControl.vue.d.ts.map +1 -1
- package/es/components/table/hooks/useTable.d.ts.map +1 -1
- package/es/components/table/index.d.ts +4 -1
- package/es/components/table/index.d.ts.map +1 -1
- package/es/components/table/types/index.d.ts +14 -6
- package/es/components/table/types/index.d.ts.map +1 -1
- package/es/index.d.ts +8 -4
- package/es/index.d.ts.map +1 -1
- package/es/index.js +259 -126
- package/es/index.js.map +1 -1
- package/es/locale/index.d.ts +2 -0
- package/es/locale/index.d.ts.map +1 -1
- package/es/shared/core/index.d.ts +1 -1
- package/es/shared/core/index.d.ts.map +1 -1
- package/es/shared/ui/index.d.ts +15 -3
- package/es/shared/ui/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/components/component-provider/components/index.vue +26 -12
- package/src/components/component-provider/constants/index.ts +33 -6
- package/src/components/component-provider/types/index.ts +17 -9
- package/src/components/form/components/BaseField/index.vue +41 -13
- package/src/components/form/components/BaseField/utils/index.ts +19 -1
- package/src/components/form/components/BaseFormItem/index.vue +29 -34
- package/src/components/form/components/ContainerFragment/index.vue +3 -1
- package/src/components/form/components/ProForm/index.vue +18 -13
- package/src/components/form/components/SlotComponent/index.vue +14 -4
- package/src/components/form/components/index.ts +8 -1
- package/src/components/form/constants/index.ts +3 -4
- package/src/components/form/hooks/useFields.ts +3 -1
- package/src/components/form/hooks/useGroupFieldAttrs.ts +29 -13
- package/src/components/form/index.ts +1 -3
- package/src/components/form/types/index.ts +79 -46
- package/src/components/form/utils/fieldKey.ts +27 -0
- package/src/components/table/components/ColumnControl.vue +17 -3
- package/src/components/table/components/DefaultSearchFormContainer.vue +5 -0
- package/src/components/table/components/DefaultTableContainer.vue +5 -0
- package/src/components/table/components/ProTable.vue +228 -69
- package/src/components/table/components/SearchForm.vue +56 -19
- package/src/components/table/components/SizeControl.vue +21 -1
- package/src/components/table/hooks/useTable.ts +1 -1
- package/src/components/table/index.ts +8 -3
- package/src/components/table/types/index.ts +15 -7
- package/src/index.ts +12 -5
- package/src/locale/index.ts +2 -0
- package/src/shared/core/index.ts +11 -0
- package/src/shared/ui/index.ts +22 -4
- package/tsconfig.json +0 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* 列显隐控制控件,以下拉菜单列出可控列并支持全选/取消全选
|
|
4
|
+
*
|
|
5
|
+
* @component ColumnControl
|
|
6
|
+
*
|
|
7
|
+
* @param {Columns} columns - 参与控制的列配置(由 ProTable 下发)
|
|
8
|
+
* @param {Table} [table] - 表格实例,勾选结果经其 `setColumn` 写回列 `hidden`
|
|
9
|
+
* @param {ColumnControlConfig} [columnControl] - 控制项配置:labels / excludedKeys / disabledKeys / disableCheckAll
|
|
10
|
+
*/
|
|
2
11
|
import {
|
|
3
12
|
Button,
|
|
4
13
|
MenuDivider,
|
|
@@ -7,15 +16,19 @@ import {
|
|
|
7
16
|
MenuItem,
|
|
8
17
|
Checkbox,
|
|
9
18
|
} from '../../../shared/ui';
|
|
10
|
-
import { computed, ref, watch } from 'vue';
|
|
19
|
+
import { computed, ref, toValue, watch } from 'vue';
|
|
20
|
+
import { isObjectItem } from '../../../shared/core';
|
|
11
21
|
import type { Columns, ColumnControlConfig } from '../types';
|
|
12
22
|
import { type Table } from '../hooks/useTable';
|
|
13
23
|
import FilterOutlined from './icons/FilterOutlined.vue';
|
|
14
24
|
import { useLocale } from '../../../locale';
|
|
15
25
|
|
|
16
26
|
type Props = {
|
|
27
|
+
/** 参与控制的列配置(由 ProTable 下发) */
|
|
17
28
|
columns: Columns;
|
|
29
|
+
/** 表格实例,勾选结果经其 `setColumn` 写回列 `hidden` */
|
|
18
30
|
table?: Table;
|
|
31
|
+
/** 控制项配置:labels / excludedKeys / disabledKeys / disableCheckAll */
|
|
19
32
|
columnControl?: ColumnControlConfig;
|
|
20
33
|
};
|
|
21
34
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -30,6 +43,7 @@ const open = ref(false);
|
|
|
30
43
|
const checkAll = ref(false);
|
|
31
44
|
const indeterminate = ref(false);
|
|
32
45
|
|
|
46
|
+
/** 下拉项:逐列解析出 key、显示名、勾选态与是否禁用 */
|
|
33
47
|
const checkedColumnsOptions = computed<
|
|
34
48
|
Array<{
|
|
35
49
|
label: string;
|
|
@@ -38,7 +52,7 @@ const checkedColumnsOptions = computed<
|
|
|
38
52
|
disabled: boolean;
|
|
39
53
|
}>
|
|
40
54
|
>(() =>
|
|
41
|
-
props.columns.flatMap(item => {
|
|
55
|
+
props.columns.filter(isObjectItem).flatMap(item => {
|
|
42
56
|
const rawKey =
|
|
43
57
|
item.key ??
|
|
44
58
|
(Array.isArray(item.dataIndex)
|
|
@@ -62,7 +76,7 @@ const checkedColumnsOptions = computed<
|
|
|
62
76
|
return {
|
|
63
77
|
label,
|
|
64
78
|
value: key,
|
|
65
|
-
checked: !item.hidden || disabled,
|
|
79
|
+
checked: !toValue(item.hidden) || disabled,
|
|
66
80
|
disabled,
|
|
67
81
|
};
|
|
68
82
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts" setup generic="T extends Table<any> = Table">
|
|
2
2
|
/**
|
|
3
|
+
* 配置驱动的表格组件
|
|
4
|
+
*
|
|
3
5
|
* @component ProTable
|
|
4
|
-
* @description 配置驱动的表格组件
|
|
5
6
|
*
|
|
6
7
|
* ## 架构概览
|
|
7
8
|
*
|
|
@@ -25,24 +26,38 @@
|
|
|
25
26
|
* └── 转发所有 table 插槽
|
|
26
27
|
* ```
|
|
27
28
|
*
|
|
28
|
-
* ##
|
|
29
|
+
* ## 搜索行为
|
|
30
|
+
*
|
|
31
|
+
* 1. 搜索:分页回到第一页后发起查询
|
|
32
|
+
* 2. 分页变化:分页参数同步后发起查询
|
|
33
|
+
* 3. 重置:查询参数恢复初始值后重新查询
|
|
34
|
+
*
|
|
35
|
+
* @template T - 表格实例类型(useTable 返回值)
|
|
29
36
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
37
|
+
* @param {T} [table] - 列表表格对象,来自 useTable
|
|
38
|
+
* @param {() => Promise<unknown>} [search] - 列表数据查询方法
|
|
39
|
+
* @param {boolean} [addIndexColumn] - 是否在首列插入序号列
|
|
40
|
+
* @param {boolean} [immediateSearch] - 挂载后是否立即触发一次查询
|
|
41
|
+
* @param {boolean | Partial<Control>} [control] - 是否展示尺寸与列控制按钮;显式传入 columns 时列控入口不渲染
|
|
42
|
+
* @param {SearchFormConfig} [searchFormConfig] - 搜索栏查询字段表单配置
|
|
43
|
+
* @param {ContainerComponent | false} [tableContainer] - 表格区域包裹容器
|
|
44
|
+
*
|
|
45
|
+
* @slot search-form - 搜索区域插槽,作用域参数 form / search / reset
|
|
46
|
+
* @slot button-bar - 表格头部按钮区插槽
|
|
47
|
+
* @slot toolbar - 工具栏插槽
|
|
48
|
+
* @slot default - 透传给宿主表格的默认插槽(可用于以宿主列组件声明列)
|
|
49
|
+
* @slot table - 表格区域插槽,作用域参数 columns / dataSource / pagination / loading / size / onChange
|
|
34
50
|
*/
|
|
35
|
-
import {
|
|
51
|
+
import type {
|
|
36
52
|
PaginationProps,
|
|
37
|
-
Table as ATable,
|
|
38
53
|
TableProps,
|
|
39
54
|
ColumnType,
|
|
40
|
-
tableProps,
|
|
41
55
|
} from '../../../shared/ui';
|
|
56
|
+
import { Table as ATable, tableProps } from '../../../shared/ui';
|
|
42
57
|
import {
|
|
43
58
|
computed,
|
|
59
|
+
getCurrentInstance,
|
|
44
60
|
nextTick,
|
|
45
|
-
useAttrs,
|
|
46
61
|
onMounted,
|
|
47
62
|
inject,
|
|
48
63
|
toValue,
|
|
@@ -52,23 +67,26 @@ import { INJECT_CONFIG } from '../../component-provider/constants';
|
|
|
52
67
|
import ContainerFragment from '../../form/components/ContainerFragment/index.vue';
|
|
53
68
|
import type { ContainerComponent } from '../../form/types';
|
|
54
69
|
import SearchForm from './SearchForm.vue';
|
|
55
|
-
import { SearchFormProps } from './SearchForm.vue';
|
|
70
|
+
import type { SearchFormProps } from './SearchForm.vue';
|
|
56
71
|
import type { Table } from '../hooks/useTable';
|
|
57
72
|
import { useLocale } from '../../../locale';
|
|
73
|
+
import type { Path } from '../../../shared/core';
|
|
58
74
|
import {
|
|
75
|
+
camelizeProperties,
|
|
59
76
|
getObject,
|
|
77
|
+
isObjectItem,
|
|
60
78
|
isPlainObject,
|
|
61
|
-
pick,
|
|
62
79
|
isPromise,
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
omitBy,
|
|
81
|
+
pick,
|
|
82
|
+
useReactiveAttrs,
|
|
65
83
|
} from '../../../shared/core';
|
|
66
|
-
import { ComponentSlots } from 'vue-component-type-helpers';
|
|
84
|
+
import type { ComponentSlots } from 'vue-component-type-helpers';
|
|
67
85
|
import DefaultSearchFormContainer from './DefaultSearchFormContainer.vue';
|
|
68
86
|
import DefaultTableContainer from './DefaultTableContainer.vue';
|
|
69
87
|
import SizeControl from './SizeControl.vue';
|
|
70
88
|
import ColumnControl from './ColumnControl.vue';
|
|
71
|
-
import { Column, Columns, ColumnControlConfig } from '../types';
|
|
89
|
+
import type { Column, Columns, ColumnControlConfig } from '../types';
|
|
72
90
|
|
|
73
91
|
defineOptions({ name: 'ProTable', inheritAttrs: false });
|
|
74
92
|
|
|
@@ -87,19 +105,19 @@ type SearchFormConfig = Omit<SearchFormProps, 'form'> & {
|
|
|
87
105
|
};
|
|
88
106
|
|
|
89
107
|
type Props = {
|
|
90
|
-
|
|
108
|
+
/** 列表表格对象,来自 `useTable` */
|
|
91
109
|
table?: T;
|
|
92
|
-
|
|
110
|
+
/** 列表数据查询方法 */
|
|
93
111
|
search?: () => Promise<unknown>;
|
|
94
|
-
|
|
112
|
+
/** 是否在首列插入序号列 */
|
|
95
113
|
addIndexColumn?: boolean;
|
|
96
|
-
|
|
114
|
+
/** 挂载后是否立即触发一次查询 */
|
|
97
115
|
immediateSearch?: boolean;
|
|
98
|
-
|
|
116
|
+
/** 是否展示尺寸与列控制按钮;显式传入 columns 时列控入口不渲染 */
|
|
99
117
|
control?: boolean | Partial<Control>;
|
|
100
|
-
|
|
118
|
+
/** 搜索栏查询字段表单配置 */
|
|
101
119
|
searchFormConfig?: SearchFormConfig;
|
|
102
|
-
|
|
120
|
+
/** 表格区域包裹容器 */
|
|
103
121
|
tableContainer?: ContainerComponent | false;
|
|
104
122
|
} & /* @vue-ignore */ TableProps;
|
|
105
123
|
|
|
@@ -119,6 +137,7 @@ const config = INJECT_CONFIG['pro-table'];
|
|
|
119
137
|
const injectPropsRaw = inject(config.injectionKey, config.default);
|
|
120
138
|
const injectProps = computed(() => toValue(injectPropsRaw) || {});
|
|
121
139
|
|
|
140
|
+
/* Provider 注入的不可注入键(columns / dataSource / loading)已在注入层剔除,此处直接取用其注入属性 */
|
|
122
141
|
const injectAttrs = computed<TableProps>(
|
|
123
142
|
() => pick(injectProps.value, tableProperties) as TableProps
|
|
124
143
|
);
|
|
@@ -135,33 +154,45 @@ const computedSearchFormConfig = computed(() => {
|
|
|
135
154
|
};
|
|
136
155
|
});
|
|
137
156
|
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
157
|
+
/** SearchForm 属性面:容器与显隐由 ProTable 自身消费,不下发 SearchForm */
|
|
158
|
+
const searchFormProps = computed(() => {
|
|
159
|
+
const {
|
|
160
|
+
container: _container,
|
|
161
|
+
hidden: _hidden,
|
|
162
|
+
...rest
|
|
163
|
+
} = computedSearchFormConfig.value;
|
|
164
|
+
return rest;
|
|
141
165
|
});
|
|
142
166
|
|
|
167
|
+
const attrs: Record<string, any> = useReactiveAttrs();
|
|
168
|
+
|
|
143
169
|
const size = defineModel<TableProps['size']>('size');
|
|
144
170
|
const loading = defineModel<boolean>('loading', { default: false });
|
|
145
171
|
|
|
146
172
|
// 递增序号做请求竞态守卫,仅最新一次请求可变更 loading
|
|
147
173
|
let searchSequence = 0;
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
174
|
+
// 告警文案按原因分键,各原因独立去重
|
|
175
|
+
const SEARCH_WARN_MESSAGES = {
|
|
176
|
+
duplicate:
|
|
177
|
+
'同时配置了 :search prop 与 @search 监听,@search 监听不会被执行,请仅保留其一。',
|
|
178
|
+
'not-promise':
|
|
179
|
+
'search 未返回 Promise,本次查询不参与 loading 竞态管理(在途请求的 loading 状态保持不变)。',
|
|
180
|
+
} as const;
|
|
181
|
+
const warnedSearchReasons = new Set<keyof typeof SEARCH_WARN_MESSAGES>();
|
|
182
|
+
const warnSearchConfig = (reason: keyof typeof SEARCH_WARN_MESSAGES) => {
|
|
183
|
+
if (warnedSearchReasons.has(reason)) return;
|
|
184
|
+
warnedSearchReasons.add(reason);
|
|
185
|
+
console.warn(`[ProTable] ${SEARCH_WARN_MESSAGES[reason]}`);
|
|
153
186
|
};
|
|
154
187
|
const _search = async () => {
|
|
188
|
+
const search = props.search || attrs.onSearch;
|
|
155
189
|
if (props.search && attrs.onSearch) {
|
|
156
|
-
warnSearchConfig(
|
|
157
|
-
'同时配置了 :search prop 与 @search 监听,@search 监听不会被执行,请仅保留其一。'
|
|
158
|
-
);
|
|
190
|
+
warnSearchConfig('duplicate');
|
|
159
191
|
}
|
|
160
|
-
|
|
192
|
+
if (!search) return;
|
|
193
|
+
const searchPromise = search();
|
|
161
194
|
if (!isPromise(searchPromise)) {
|
|
162
|
-
warnSearchConfig(
|
|
163
|
-
'search 未返回 Promise,本次查询不参与 loading 竞态管理(在途请求的 loading 状态保持不变)。'
|
|
164
|
-
);
|
|
195
|
+
warnSearchConfig('not-promise');
|
|
165
196
|
return;
|
|
166
197
|
}
|
|
167
198
|
const seq = ++searchSequence;
|
|
@@ -218,10 +249,15 @@ const columnRequiredSlotNames = new Set([
|
|
|
218
249
|
]);
|
|
219
250
|
|
|
220
251
|
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
252
|
+
* 判定插槽在当前渲染状态下是否可渲染。
|
|
253
|
+
*
|
|
254
|
+
* @description
|
|
255
|
+
* 依赖列信息的插槽(`bodyCell` / `headerCell` / `customFilterDropdown` / `customFilterIcon`)仅在存在可见列且作用域中存在 `column` 时渲染;
|
|
256
|
+
* 非列依赖的表格级插槽(如 `emptyText`、`footer`、`summary` 等)始终渲染。
|
|
257
|
+
*
|
|
258
|
+
* @param name - 插槽名
|
|
259
|
+
* @param scoped - 插槽作用域参数
|
|
260
|
+
* @returns 该插槽是否参与渲染
|
|
225
261
|
*/
|
|
226
262
|
const isSlotRenderable = (name: string, scoped: any) => {
|
|
227
263
|
if (columnRequiredSlotNames.has(name)) {
|
|
@@ -235,13 +271,65 @@ const searchPage1st = () => {
|
|
|
235
271
|
_search();
|
|
236
272
|
};
|
|
237
273
|
|
|
274
|
+
const instance = getCurrentInstance();
|
|
275
|
+
|
|
276
|
+
/** 调用方是否显式绑定该 prop(含 kebab-case 与 v-model 写法) */
|
|
277
|
+
const isBoundProp = (name: string) => {
|
|
278
|
+
const vnodeProps = instance?.vnode.props || {};
|
|
279
|
+
const kebab = name.replace(/[A-Z]/g, char => `-${char.toLowerCase()}`);
|
|
280
|
+
return (
|
|
281
|
+
name in vnodeProps ||
|
|
282
|
+
kebab in vnodeProps ||
|
|
283
|
+
`onUpdate:${name}` in vnodeProps
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/** attrs 的驼峰视图:模板中以 kebab-case 写法的绑定键在此归一 */
|
|
288
|
+
const camelAttrs = computed<Record<string, any>>(() =>
|
|
289
|
+
camelizeProperties(attrs)
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
/*
|
|
293
|
+
* 组件自有键取值:`Props` 中声明的键取 prop,其余宿主键取 attrs(模板 kebab-case 绑定经归一后同键);
|
|
294
|
+
* 均以调用方显式绑定为唯一来源,未绑定时不取值(留给 Provider 注入兜底)
|
|
295
|
+
*/
|
|
296
|
+
const resolveOwnedProp = (name: string) => {
|
|
297
|
+
const attrValue = camelAttrs.value[name];
|
|
298
|
+
return isBoundProp(name)
|
|
299
|
+
? ((props as Record<string, any>)[name] ?? attrValue)
|
|
300
|
+
: attrValue;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/** 调用方是否显式传入 columns(prop 或 attrs 形态,值为 undefined 时视为未指定) */
|
|
304
|
+
const hasExplicitColumns = computed(
|
|
305
|
+
() => resolveOwnedProp('columns') !== undefined
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
/*
|
|
309
|
+
* 表格尺寸单一来源:本地模型(显式绑定或 SizeControl 回写)> Provider 注入;
|
|
310
|
+
* 本地模型为 undefined 时视为未指定,Provider 注入值继续生效
|
|
311
|
+
*/
|
|
312
|
+
const effectiveSize = computed<TableProps['size']>(() => {
|
|
313
|
+
return size.value ?? injectAttrs.value.size;
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
/*
|
|
317
|
+
* 下发给宿主表格的属性:Provider 注入为底(其不可注入键已在注入层剔除),调用方绑定与 v-model 透传值覆盖同键。
|
|
318
|
+
* size / loading 为声明模型,其 onUpdate:* 回写通道不进入 attrs,无需另行剔除
|
|
319
|
+
*/
|
|
238
320
|
const computedTableProps = computed<TableProps>(() => {
|
|
239
|
-
|
|
321
|
+
const merged = {
|
|
240
322
|
...injectAttrs.value,
|
|
241
|
-
...
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
323
|
+
...omitBy(
|
|
324
|
+
pick(camelAttrs.value, tableProperties),
|
|
325
|
+
value => value === undefined
|
|
326
|
+
),
|
|
327
|
+
} as Record<string, any>;
|
|
328
|
+
// onChange 由模板监听器收口,散落 attrs 中的同名监听器并入会形成重复回调
|
|
329
|
+
delete merged.onChange;
|
|
330
|
+
merged.size = effectiveSize.value;
|
|
331
|
+
merged.loading = undefined;
|
|
332
|
+
return merged as TableProps;
|
|
245
333
|
});
|
|
246
334
|
|
|
247
335
|
const computedTableContainer = computed(() => {
|
|
@@ -255,15 +343,17 @@ const computedTableContainer = computed(() => {
|
|
|
255
343
|
type ReturnGenericParameterTypes<V> = V extends Table<any, infer R> ? R : never;
|
|
256
344
|
type RecordType = ReturnGenericParameterTypes<T>;
|
|
257
345
|
|
|
258
|
-
|
|
346
|
+
/*
|
|
347
|
+
* 序号列随有效分页偏移:分页关闭时退化为 index + 1;
|
|
348
|
+
* current / pageSize 取有效分页(显式 :pagination 或 Provider 注入),table.pageParam 仅作兜底
|
|
349
|
+
*/
|
|
259
350
|
const getIndex = (i: number) => {
|
|
351
|
+
const pagination = computedPagination.value;
|
|
352
|
+
if (pagination === false) return i + 1;
|
|
260
353
|
const pageParam = props.table?.pageParam;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return (current - 1) * pageSize + i + 1;
|
|
265
|
-
}
|
|
266
|
-
return i + 1;
|
|
354
|
+
const current = pagination.current ?? pageParam?.current ?? 1;
|
|
355
|
+
const pageSize = pagination.pageSize ?? pageParam?.pageSize ?? 10;
|
|
356
|
+
return (current - 1) * pageSize + i + 1;
|
|
267
357
|
};
|
|
268
358
|
|
|
269
359
|
const indexColumn = computed<ColumnType>(() => ({
|
|
@@ -274,26 +364,72 @@ const indexColumn = computed<ColumnType>(() => ({
|
|
|
274
364
|
customRender: ({ index }) => getIndex(index),
|
|
275
365
|
}));
|
|
276
366
|
|
|
367
|
+
// 列来源优先级:显式绑定 > table 实例(useTable 创建的 columns)
|
|
277
368
|
const computedColumns = computed<Columns<RecordType>>(() => {
|
|
278
|
-
const cols = (
|
|
369
|
+
const cols = (resolveOwnedProp('columns') ??
|
|
279
370
|
toValue(props.table?.columns) ??
|
|
280
371
|
[]) as any;
|
|
281
372
|
const needIndex = props.addIndexColumn ?? injectProps.value.addIndexColumn;
|
|
282
373
|
return needIndex ? [indexColumn.value, ...cols] : cols;
|
|
283
374
|
});
|
|
284
375
|
|
|
376
|
+
/*
|
|
377
|
+
* 递归过滤 hidden 列(含分组子列):仅对被过滤过子列的列生成浅拷贝,
|
|
378
|
+
* 未发生变动的列保持原对象引用(每次 columns 变化执行一次,不在渲染期递归)
|
|
379
|
+
*/
|
|
380
|
+
const filterVisibleColumns = (
|
|
381
|
+
columns: any[],
|
|
382
|
+
ancestors: Set<object> = new Set()
|
|
383
|
+
): { list: any[]; changed: boolean } => {
|
|
384
|
+
const list: any[] = [];
|
|
385
|
+
let changed = false;
|
|
386
|
+
for (const column of columns) {
|
|
387
|
+
if (!isObjectItem(column) || toValue((column as Column).hidden)) {
|
|
388
|
+
changed = true;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (Array.isArray(column.children) && column.children.length > 0) {
|
|
392
|
+
// 环状分组列不再深入,重复出现的祖先引用直接剔除
|
|
393
|
+
if (ancestors.has(column)) {
|
|
394
|
+
changed = true;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
ancestors.add(column);
|
|
398
|
+
const filtered = filterVisibleColumns(column.children, ancestors);
|
|
399
|
+
ancestors.delete(column);
|
|
400
|
+
if (filtered.changed) {
|
|
401
|
+
changed = true;
|
|
402
|
+
list.push({ ...column, children: filtered.list });
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
list.push(column);
|
|
407
|
+
}
|
|
408
|
+
return { list, changed };
|
|
409
|
+
};
|
|
410
|
+
|
|
285
411
|
const visibleComputedColumns = computed(() => {
|
|
286
|
-
return computedColumns.value
|
|
412
|
+
return filterVisibleColumns(computedColumns.value).list;
|
|
287
413
|
});
|
|
288
414
|
|
|
415
|
+
// 数据源优先级:显式绑定 > table 实例(useTable 创建的 dataSource)
|
|
289
416
|
const computedDataSource = computed<RecordType[]>(() => {
|
|
290
|
-
return
|
|
417
|
+
return (
|
|
418
|
+
resolveOwnedProp('dataSource') ?? toValue(props.table?.dataSource) ?? []
|
|
419
|
+
);
|
|
291
420
|
});
|
|
292
421
|
|
|
293
|
-
|
|
422
|
+
/*
|
|
423
|
+
* 分页优先级:显式绑定 > provider 全局配置 > 默认合成;
|
|
424
|
+
* provider 作为基础配置与 table.pageParam 合并,显式绑定的对象形态在其之上覆盖
|
|
425
|
+
*/
|
|
294
426
|
const computedPagination = computed<PaginationProps | false>(() => {
|
|
295
|
-
|
|
296
|
-
|
|
427
|
+
const attrsPagination = resolveOwnedProp('pagination') as
|
|
428
|
+
| PaginationProps
|
|
429
|
+
| false
|
|
430
|
+
| undefined;
|
|
431
|
+
if (attrsPagination === false) return false;
|
|
432
|
+
if (isPlainObject(attrsPagination)) {
|
|
297
433
|
const globalPagination =
|
|
298
434
|
injectAttrs.value.pagination === false
|
|
299
435
|
? {}
|
|
@@ -301,9 +437,9 @@ const computedPagination = computed<PaginationProps | false>(() => {
|
|
|
301
437
|
return {
|
|
302
438
|
...globalPagination,
|
|
303
439
|
...props.table?.pageParam,
|
|
304
|
-
...
|
|
440
|
+
...attrsPagination,
|
|
305
441
|
showTotal:
|
|
306
|
-
|
|
442
|
+
attrsPagination?.showTotal ??
|
|
307
443
|
globalPagination?.showTotal ??
|
|
308
444
|
((total: number) => t('pagination.total', { total })),
|
|
309
445
|
};
|
|
@@ -332,11 +468,15 @@ const searchFormVisible = computed(() => {
|
|
|
332
468
|
return !!(
|
|
333
469
|
computedSearchFormConfig.value?.hidden !== true &&
|
|
334
470
|
props.table?.searchForm &&
|
|
335
|
-
fieldList.filter(item => !toValue(item.hidden))
|
|
471
|
+
fieldList.filter(item => isObjectItem(item) && !toValue(item.hidden))
|
|
472
|
+
.length > 0
|
|
336
473
|
);
|
|
337
474
|
});
|
|
338
475
|
|
|
339
|
-
|
|
476
|
+
/*
|
|
477
|
+
* 控制项来源:显式控制配置 > Provider 全局配置
|
|
478
|
+
*/
|
|
479
|
+
const resolvedControl = computed<{
|
|
340
480
|
sizeControl: boolean;
|
|
341
481
|
columnControl: boolean | ColumnControlConfig<Path<RecordType>>;
|
|
342
482
|
}>(() => {
|
|
@@ -353,6 +493,13 @@ const computedControl = computed<{
|
|
|
353
493
|
};
|
|
354
494
|
});
|
|
355
495
|
|
|
496
|
+
/* 显式 columns 模式下渲染源不受 table.setColumn 影响,列控入口隐藏 */
|
|
497
|
+
const computedControl = computed(() =>
|
|
498
|
+
hasExplicitColumns.value && resolvedControl.value.columnControl
|
|
499
|
+
? { ...resolvedControl.value, columnControl: false }
|
|
500
|
+
: resolvedControl.value
|
|
501
|
+
);
|
|
502
|
+
|
|
356
503
|
const columnControlConfig = computed(() => {
|
|
357
504
|
const cc = computedControl.value.columnControl;
|
|
358
505
|
return typeof cc === 'object' ? cc : undefined;
|
|
@@ -376,11 +523,14 @@ defineExpose({
|
|
|
376
523
|
v-if="searchFormVisible || $slots['search-form']"
|
|
377
524
|
:component="computedSearchFormConfig.container"
|
|
378
525
|
>
|
|
379
|
-
<slot
|
|
526
|
+
<slot
|
|
527
|
+
name="search-form"
|
|
528
|
+
v-bind="{ form: props.table?.searchForm, search: searchPage1st, reset }"
|
|
529
|
+
>
|
|
380
530
|
<SearchForm
|
|
381
531
|
v-if="props.table?.searchForm"
|
|
382
532
|
:form="props.table.searchForm"
|
|
383
|
-
v-bind="
|
|
533
|
+
v-bind="searchFormProps"
|
|
384
534
|
@search="searchPage1st"
|
|
385
535
|
@reset="reset"
|
|
386
536
|
/>
|
|
@@ -399,7 +549,7 @@ defineExpose({
|
|
|
399
549
|
v-if="computedControl.sizeControl"
|
|
400
550
|
class="pro-table_header_size-control"
|
|
401
551
|
>
|
|
402
|
-
<SizeControl v-model:size="size" />
|
|
552
|
+
<SizeControl v-model:size="size" :effective-size="effectiveSize" />
|
|
403
553
|
</div>
|
|
404
554
|
<div
|
|
405
555
|
v-if="computedControl.columnControl"
|
|
@@ -413,11 +563,20 @@ defineExpose({
|
|
|
413
563
|
</div>
|
|
414
564
|
</div>
|
|
415
565
|
|
|
416
|
-
<slot
|
|
566
|
+
<slot
|
|
567
|
+
name="table"
|
|
568
|
+
v-bind="{
|
|
569
|
+
columns: visibleComputedColumns,
|
|
570
|
+
dataSource: computedDataSource,
|
|
571
|
+
pagination: computedPagination,
|
|
572
|
+
loading: loading,
|
|
573
|
+
size: effectiveSize,
|
|
574
|
+
onChange: onTableChange,
|
|
575
|
+
}"
|
|
576
|
+
>
|
|
417
577
|
<ATable
|
|
418
578
|
class="pro-table_content"
|
|
419
579
|
v-bind="computedTableProps"
|
|
420
|
-
:size="size"
|
|
421
580
|
:loading="loading"
|
|
422
581
|
:columns="visibleComputedColumns"
|
|
423
582
|
:data-source="computedDataSource"
|