@qin-ui/element-plus-pro 1.2.4 → 1.3.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 +1 -1
- package/README.md +5 -5
- package/api.json +1 -25
- package/es/__tests__/BaseField.spec.d.ts +2 -0
- package/es/__tests__/BaseField.spec.d.ts.map +1 -0
- package/es/__tests__/BaseFormItem.spec.d.ts +2 -0
- package/es/__tests__/BaseFormItem.spec.d.ts.map +1 -0
- package/es/__tests__/ProForm.spec.d.ts +2 -0
- package/es/__tests__/ProForm.spec.d.ts.map +1 -0
- package/es/__tests__/ProTable.spec.d.ts +2 -0
- package/es/__tests__/ProTable.spec.d.ts.map +1 -0
- package/es/__tests__/SearchForm.spec.d.ts +2 -0
- package/es/__tests__/SearchForm.spec.d.ts.map +1 -0
- package/es/__tests__/controls.spec.d.ts +2 -0
- package/es/__tests__/controls.spec.d.ts.map +1 -0
- package/es/__tests__/cross-package.spec.d.ts +2 -0
- package/es/__tests__/cross-package.spec.d.ts.map +1 -0
- package/es/__tests__/helpers/fixtures.d.ts +15 -0
- package/es/__tests__/helpers/fixtures.d.ts.map +1 -0
- package/es/__tests__/helpers/mount.d.ts +38 -0
- package/es/__tests__/helpers/mount.d.ts.map +1 -0
- package/es/__tests__/hooks-locale.spec.d.ts +2 -0
- package/es/__tests__/hooks-locale.spec.d.ts.map +1 -0
- package/es/__tests__/index.spec.d.ts +2 -0
- package/es/__tests__/index.spec.d.ts.map +1 -0
- package/es/__tests__/integration.spec.d.ts +2 -0
- package/es/__tests__/integration.spec.d.ts.map +1 -0
- package/es/__tests__/setup.d.ts +14 -0
- package/es/__tests__/setup.d.ts.map +1 -0
- package/es/element-plus-pro.css +14 -14
- package/es/index.js +425 -434
- package/es/index.js.map +1 -1
- package/es/{components → src/components}/component-provider/components/index.vue.d.ts +5 -1
- package/es/src/components/component-provider/components/index.vue.d.ts.map +1 -0
- package/es/{components → src/components}/component-provider/constants/index.d.ts +2 -2
- package/es/src/components/component-provider/constants/index.d.ts.map +1 -0
- package/es/src/components/component-provider/index.d.ts.map +1 -0
- package/es/{components → src/components}/component-provider/types/index.d.ts +9 -5
- package/es/src/components/component-provider/types/index.d.ts.map +1 -0
- package/es/{components → src/components}/form/components/BaseField/index.vue.d.ts +4 -1
- package/es/src/components/form/components/BaseField/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/BaseField/utils/index.d.ts +6 -0
- package/es/src/components/form/components/BaseField/utils/index.d.ts.map +1 -0
- package/es/{components → src/components}/form/components/BaseFormItem/index.vue.d.ts +4 -2
- package/es/src/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/ProForm/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/SlotComponent/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/index.d.ts.map +1 -0
- package/es/src/components/form/constants/index.d.ts.map +1 -0
- package/es/src/components/form/hooks/index.d.ts.map +1 -0
- package/es/src/components/form/hooks/useFields.d.ts.map +1 -0
- package/es/src/components/form/hooks/useForm.d.ts.map +1 -0
- package/es/src/components/form/hooks/useFormRef.d.ts.map +1 -0
- package/es/src/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -0
- package/es/src/components/form/index.d.ts.map +1 -0
- package/es/{components → src/components}/form/types/index.d.ts +26 -2
- package/es/src/components/form/types/index.d.ts.map +1 -0
- package/es/{components → src/components}/table/components/ColumnControl.vue.d.ts +4 -1
- package/es/src/components/table/components/ColumnControl.vue.d.ts.map +1 -0
- package/es/src/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -0
- package/es/src/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -0
- package/es/{components → src/components}/table/components/ProTable.vue.d.ts +4 -1
- package/es/src/components/table/components/ProTable.vue.d.ts.map +1 -0
- package/es/{components → src/components}/table/components/SearchForm.vue.d.ts +14 -4
- package/es/src/components/table/components/SearchForm.vue.d.ts.map +1 -0
- package/es/src/components/table/components/SizeControl.vue.d.ts.map +1 -0
- package/es/src/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -0
- package/es/src/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +1 -0
- package/es/src/components/table/components/icons/DownOutlined.vue.d.ts.map +1 -0
- package/es/src/components/table/components/icons/FilterOutlined.vue.d.ts.map +1 -0
- package/es/src/components/table/components/index.d.ts.map +1 -0
- package/es/src/components/table/index.d.ts.map +1 -0
- package/es/{components → src/components}/table/types/index.d.ts +3 -0
- package/es/src/components/table/types/index.d.ts.map +1 -0
- package/es/src/components/table/useTable.d.ts.map +1 -0
- package/es/{index.d.ts → src/index.d.ts} +1 -1
- package/es/src/index.d.ts.map +1 -0
- package/es/src/locale/en-US.d.ts.map +1 -0
- package/es/{locale → src/locale}/index.d.ts +1 -1
- package/es/src/locale/index.d.ts.map +1 -0
- package/es/src/locale/zh-CN.d.ts.map +1 -0
- package/es/src/locale/zh-TW.d.ts.map +1 -0
- package/es/src/shared/core/index.d.ts +6 -0
- package/es/src/shared/core/index.d.ts.map +1 -0
- package/es/{shared → src/shared}/ui/index.d.ts +2 -0
- package/es/src/shared/ui/index.d.ts.map +1 -0
- package/package.json +8 -3
- package/src/components/component-provider/components/index.vue +41 -11
- package/src/components/component-provider/constants/index.ts +6 -5
- package/src/components/component-provider/types/index.ts +9 -5
- package/src/components/form/components/BaseField/index.vue +67 -38
- package/src/components/form/components/BaseField/utils/index.ts +17 -11
- package/src/components/form/components/BaseFormItem/index.vue +48 -41
- package/src/components/form/components/ProForm/index.vue +64 -20
- package/src/components/form/components/SlotComponent/index.vue +9 -6
- package/src/components/form/hooks/useGroupFieldAttrs.ts +13 -6
- package/src/components/form/types/index.ts +26 -2
- package/src/components/table/components/ColumnControl.vue +31 -38
- package/src/components/table/components/ProTable.vue +142 -84
- package/src/components/table/components/SearchForm.vue +49 -202
- package/src/components/table/components/SizeControl.vue +2 -2
- package/src/components/table/components/TableColumnRenderer.vue +16 -11
- package/src/components/table/types/index.ts +3 -0
- package/src/components/table/useTable.ts +12 -7
- package/src/index.ts +1 -1
- package/src/locale/en-US.ts +1 -1
- package/src/locale/index.ts +7 -7
- package/src/locale/zh-CN.ts +1 -1
- package/src/locale/zh-TW.ts +1 -1
- package/src/shared/core/index.ts +15 -2
- package/src/shared/ui/index.ts +20 -9
- package/es/components/component-provider/components/index.vue.d.ts.map +0 -1
- package/es/components/component-provider/constants/index.d.ts.map +0 -1
- package/es/components/component-provider/index.d.ts.map +0 -1
- package/es/components/component-provider/types/index.d.ts.map +0 -1
- package/es/components/form/components/BaseField/index.vue.d.ts.map +0 -1
- package/es/components/form/components/BaseField/utils/index.d.ts +0 -5
- package/es/components/form/components/BaseField/utils/index.d.ts.map +0 -1
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +0 -1
- package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +0 -1
- package/es/components/form/components/ProForm/index.vue.d.ts.map +0 -1
- package/es/components/form/components/SlotComponent/index.vue.d.ts.map +0 -1
- package/es/components/form/components/index.d.ts.map +0 -1
- package/es/components/form/constants/index.d.ts.map +0 -1
- package/es/components/form/hooks/index.d.ts.map +0 -1
- package/es/components/form/hooks/useFields.d.ts.map +0 -1
- package/es/components/form/hooks/useForm.d.ts.map +0 -1
- package/es/components/form/hooks/useFormRef.d.ts.map +0 -1
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +0 -1
- package/es/components/form/index.d.ts.map +0 -1
- package/es/components/form/types/index.d.ts.map +0 -1
- package/es/components/table/components/ColumnControl.vue.d.ts.map +0 -1
- package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +0 -1
- package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +0 -1
- package/es/components/table/components/ProTable.vue.d.ts.map +0 -1
- package/es/components/table/components/SearchForm.vue.d.ts.map +0 -1
- package/es/components/table/components/SizeControl.vue.d.ts.map +0 -1
- package/es/components/table/components/TableColumnRenderer.vue.d.ts.map +0 -1
- package/es/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +0 -1
- package/es/components/table/components/icons/DownOutlined.vue.d.ts.map +0 -1
- package/es/components/table/components/icons/FilterOutlined.vue.d.ts.map +0 -1
- package/es/components/table/components/index.d.ts.map +0 -1
- package/es/components/table/index.d.ts.map +0 -1
- package/es/components/table/types/index.d.ts.map +0 -1
- package/es/components/table/useTable.d.ts.map +0 -1
- package/es/index.d.ts.map +0 -1
- package/es/locale/en-US.d.ts.map +0 -1
- package/es/locale/index.d.ts.map +0 -1
- package/es/locale/zh-CN.d.ts.map +0 -1
- package/es/locale/zh-TW.d.ts.map +0 -1
- package/es/shared/core/index.d.ts +0 -6
- package/es/shared/core/index.d.ts.map +0 -1
- package/es/shared/ui/index.d.ts.map +0 -1
- /package/es/{components → src/components}/component-provider/index.d.ts +0 -0
- /package/es/{components → src/components}/form/components/ContainerFragment/index.vue.d.ts +0 -0
- /package/es/{components → src/components}/form/components/ProForm/index.vue.d.ts +0 -0
- /package/es/{components → src/components}/form/components/SlotComponent/index.vue.d.ts +0 -0
- /package/es/{components → src/components}/form/components/index.d.ts +0 -0
- /package/es/{components → src/components}/form/constants/index.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/index.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useFields.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useForm.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useFormRef.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useGroupFieldAttrs.d.ts +0 -0
- /package/es/{components → src/components}/form/index.d.ts +0 -0
- /package/es/{components → src/components}/table/components/DefaultSearchFormContainer.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/DefaultTableContainer.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/SizeControl.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/TableColumnRenderer.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/icons/ColumnHeightOutlined.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/icons/DownOutlined.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/icons/FilterOutlined.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/index.d.ts +0 -0
- /package/es/{components → src/components}/table/index.d.ts +0 -0
- /package/es/{components → src/components}/table/useTable.d.ts +0 -0
- /package/es/{locale → src/locale}/en-US.d.ts +0 -0
- /package/es/{locale → src/locale}/zh-CN.d.ts +0 -0
- /package/es/{locale → src/locale}/zh-TW.d.ts +0 -0
|
@@ -10,8 +10,13 @@ import { INJECT_CONFIG } from '../../component-provider/constants';
|
|
|
10
10
|
import { FORM_ITEM_SLOT_KEYS } from '../constants';
|
|
11
11
|
import type { Base, Field } from '../types';
|
|
12
12
|
|
|
13
|
-
const formItemPropKeys = Object.keys(formItemProps())
|
|
13
|
+
const formItemPropKeys = Object.keys(formItemProps()).filter(
|
|
14
|
+
k => k !== 'disabled'
|
|
15
|
+
);
|
|
14
16
|
const gridItemPropKeys = Object.keys(gridItemProps());
|
|
17
|
+
// 模块级 Set:字段循环内 O(1) 命中,替代数组 includes 线性扫描
|
|
18
|
+
const gridItemPropKeySet = new Set<string>(gridItemPropKeys);
|
|
19
|
+
const formItemPropKeySet = new Set<string>(formItemPropKeys);
|
|
15
20
|
|
|
16
21
|
export interface GroupedField {
|
|
17
22
|
path: string;
|
|
@@ -31,6 +36,7 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
31
36
|
const injectFormItemProps = inject(config.injectionKey, config.default);
|
|
32
37
|
|
|
33
38
|
return computed<GroupedField[]>(() => {
|
|
39
|
+
const rawInjectProps = toValue(injectFormItemProps) || {};
|
|
34
40
|
return validFields.value.map(field => {
|
|
35
41
|
let gridItemProps: Record<string, any> = {};
|
|
36
42
|
let formItemProps: Record<string, any> = {};
|
|
@@ -54,8 +60,9 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
54
60
|
lg,
|
|
55
61
|
xl,
|
|
56
62
|
...injectRest
|
|
57
|
-
} =
|
|
63
|
+
} = rawInjectProps;
|
|
58
64
|
|
|
65
|
+
// 字段显式 span 优先,否则回退注入配置的响应式断点
|
|
59
66
|
const initGridItemProps =
|
|
60
67
|
span || span === 0
|
|
61
68
|
? { span }
|
|
@@ -71,9 +78,9 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
71
78
|
const container = formItemContainer ?? injectFormItemContainer;
|
|
72
79
|
|
|
73
80
|
Object.keys(rest).forEach(k => {
|
|
74
|
-
if (
|
|
81
|
+
if (gridItemPropKeySet.has(k)) {
|
|
75
82
|
gridItemProps[k] = rest[k];
|
|
76
|
-
} else if (
|
|
83
|
+
} else if (formItemPropKeySet.has(k)) {
|
|
77
84
|
(formItemProps as any)[k] = rest[k];
|
|
78
85
|
} else {
|
|
79
86
|
componentProps[k] = rest[k];
|
|
@@ -89,7 +96,7 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
89
96
|
}
|
|
90
97
|
});
|
|
91
98
|
|
|
92
|
-
const raw = (toValue(
|
|
99
|
+
const raw = (toValue(name) ?? toValue(path)) as
|
|
93
100
|
| string
|
|
94
101
|
| number[]
|
|
95
102
|
| undefined;
|
|
@@ -105,7 +112,7 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
const raw =
|
|
108
|
-
toValue((field as any)?.
|
|
115
|
+
toValue((field as any)?.name) ?? toValue((field as any)?.path);
|
|
109
116
|
return {
|
|
110
117
|
path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
|
|
111
118
|
gridItemProps: gridItemProps as GridItemProps,
|
|
@@ -30,9 +30,33 @@ export type VModelProps<T = any> = {
|
|
|
30
30
|
'onUpdate:modelValue'?: (val: T) => void;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* 字段值格式化/转换处理器
|
|
35
|
+
*
|
|
36
|
+
* @description 支持函数形态或包含 get/set 方法的对象形态。
|
|
37
|
+
*
|
|
38
|
+
* ### 行为与契约说明
|
|
39
|
+
* 1. **旧值参数按需注入**:底层基于函数的形参声明个数(`fn.length > 1`)决定是否读取旧值。若转换逻辑需要使用旧值,请显式声明两个形参 `(val, oldVal) => ...`(请勿使用默认参数或 `...args` 剩余参数,否则 `fn.length` 无法正确识别);
|
|
40
|
+
* 2. **只读快照副本**:传入的 `oldVal` 为只读快照副本,不得依赖原地修改写回;仅当 formatter 声明双形参时才克隆旧值,单参转换零开销。
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // 1. 单参数转换(常见):无需旧值,零多余开销
|
|
45
|
+
* valueFormatter: (val) => val?.trim()
|
|
46
|
+
*
|
|
47
|
+
* // 2. 双参数转换:需要基于旧值做对比或回退
|
|
48
|
+
* valueFormatter: (val, oldVal) => (isValid(val) ? val : oldVal)
|
|
49
|
+
*
|
|
50
|
+
* // 3. 对象形态:分别定义读(展示)与写(存储)转换
|
|
51
|
+
* valueFormatter: {
|
|
52
|
+
* get: (val) => (val ? dayjs(val).format('YYYY-MM-DD') : ''),
|
|
53
|
+
* set: (val, oldVal) => (val ? dayjs(val).valueOf() : undefined),
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
33
57
|
export type ValueFormatter =
|
|
34
|
-
| { (val: any, oldVal
|
|
35
|
-
| { get?: (val: any) => any; set?: (val: any, oldVal
|
|
58
|
+
| { (val: any, oldVal?: any): any }
|
|
59
|
+
| { get?: (val: any) => any; set?: (val: any, oldVal?: any) => any };
|
|
36
60
|
|
|
37
61
|
/**
|
|
38
62
|
* @description 自定义组件
|
|
@@ -6,33 +6,32 @@ import {
|
|
|
6
6
|
DropdownItem,
|
|
7
7
|
Checkbox,
|
|
8
8
|
} from '../../../shared/ui';
|
|
9
|
-
import { computed,
|
|
9
|
+
import { computed, ref, watch } from 'vue';
|
|
10
10
|
import type { Column, Columns, ColumnControlConfig } from '../types';
|
|
11
11
|
import { type Table } from '../useTable';
|
|
12
12
|
import FilterOutlined from './icons/FilterOutlined.vue';
|
|
13
13
|
import { useLocale } from '../../../locale';
|
|
14
|
+
import { normalizePathKey } from '../../../shared/core';
|
|
14
15
|
|
|
15
16
|
type Props = {
|
|
16
17
|
columns: Columns;
|
|
17
18
|
table?: Table;
|
|
18
19
|
columnControl?: ColumnControlConfig;
|
|
19
20
|
};
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} = defineProps<Props>();
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
table: undefined,
|
|
23
|
+
columnControl: undefined,
|
|
24
|
+
});
|
|
25
25
|
|
|
26
26
|
const { t } = useLocale();
|
|
27
27
|
|
|
28
|
-
const open = ref(false);
|
|
29
28
|
const checkAll = ref(false);
|
|
30
29
|
const indeterminate = ref(false);
|
|
31
30
|
|
|
32
31
|
const getColumnKey = (column: Column, index: number) => {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
return
|
|
32
|
+
const rawKey = column.key ?? column.prop;
|
|
33
|
+
if (rawKey === undefined || rawKey === null) return `__col_${index}`;
|
|
34
|
+
return normalizePathKey(rawKey);
|
|
36
35
|
};
|
|
37
36
|
|
|
38
37
|
const checkedColumnsOptions = computed<
|
|
@@ -44,21 +43,27 @@ const checkedColumnsOptions = computed<
|
|
|
44
43
|
column: Column;
|
|
45
44
|
}>
|
|
46
45
|
>(() =>
|
|
47
|
-
columns
|
|
46
|
+
props.columns
|
|
48
47
|
.map((item, index) => {
|
|
49
48
|
const key = getColumnKey(item, index);
|
|
50
|
-
const excluded = columnControl?.excludedKeys?.some(
|
|
49
|
+
const excluded = props.columnControl?.excludedKeys?.some(
|
|
51
50
|
ek => String(ek) === key
|
|
52
51
|
);
|
|
53
|
-
const disabled = columnControl?.disabledKeys?.some(
|
|
52
|
+
const disabled = props.columnControl?.disabledKeys?.some(
|
|
54
53
|
dk => String(dk) === key
|
|
55
54
|
);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
const rawTitle = item.label ?? (item as any).title;
|
|
56
|
+
const titleLabel = typeof rawTitle === 'string' ? rawTitle : undefined;
|
|
57
|
+
const propLabel =
|
|
58
|
+
item.prop !== undefined && item.prop !== null
|
|
59
|
+
? String(item.prop)
|
|
60
|
+
: undefined;
|
|
61
|
+
const label =
|
|
62
|
+
(props.columnControl?.labels?.[key] as string) ??
|
|
63
|
+
titleLabel ??
|
|
64
|
+
propLabel ??
|
|
65
|
+
key ??
|
|
66
|
+
t('table.columnFallback', { index: index + 1 });
|
|
62
67
|
return {
|
|
63
68
|
label,
|
|
64
69
|
value: key,
|
|
@@ -85,7 +90,7 @@ const checkedColumnKeys = computed({
|
|
|
85
90
|
set(val: string[]) {
|
|
86
91
|
checkedColumnsOptions.value.forEach(item => {
|
|
87
92
|
if (item.disabled) return;
|
|
88
|
-
table?.setColumn(item.value as any, {
|
|
93
|
+
props.table?.setColumn(item.value as any, {
|
|
89
94
|
hidden: !val.includes(item.value),
|
|
90
95
|
});
|
|
91
96
|
});
|
|
@@ -97,7 +102,6 @@ const onCheckAllChange = (value: any) => {
|
|
|
97
102
|
checkedColumnKeys.value = checked
|
|
98
103
|
? controllableOptions.value.map(item => item.value)
|
|
99
104
|
: [];
|
|
100
|
-
indeterminate.value = false;
|
|
101
105
|
};
|
|
102
106
|
|
|
103
107
|
const toggleColumnsItem = (val: string) => {
|
|
@@ -128,38 +132,27 @@ watch(
|
|
|
128
132
|
{ immediate: true }
|
|
129
133
|
);
|
|
130
134
|
|
|
131
|
-
//
|
|
132
|
-
onMounted(() => {
|
|
133
|
-
columnControl?.disabledKeys?.forEach(key => {
|
|
134
|
-
table?.setColumn(String(key), { hidden: false });
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
135
|
+
// disabledKeys 指定的列始终强制展示
|
|
138
136
|
watch(
|
|
139
|
-
() => columnControl?.disabledKeys,
|
|
137
|
+
() => props.columnControl?.disabledKeys,
|
|
140
138
|
keys => {
|
|
141
139
|
keys?.forEach(key => {
|
|
142
|
-
table?.setColumn(String(key), { hidden: false });
|
|
140
|
+
props.table?.setColumn(String(key), { hidden: false });
|
|
143
141
|
});
|
|
144
142
|
},
|
|
145
|
-
{ deep: true }
|
|
143
|
+
{ immediate: true, deep: true }
|
|
146
144
|
);
|
|
147
145
|
</script>
|
|
148
146
|
|
|
149
147
|
<template>
|
|
150
|
-
<Dropdown
|
|
151
|
-
trigger="click"
|
|
152
|
-
:hide-on-click="false"
|
|
153
|
-
:visible="open"
|
|
154
|
-
@visible-change="val => (open = val)"
|
|
155
|
-
>
|
|
148
|
+
<Dropdown trigger="click" :hide-on-click="false">
|
|
156
149
|
<Button text class="pro-table_column-control_button">
|
|
157
150
|
<FilterOutlined />
|
|
158
151
|
</Button>
|
|
159
152
|
<template #dropdown>
|
|
160
153
|
<DropdownMenu>
|
|
161
154
|
<DropdownItem
|
|
162
|
-
v-if="!columnControl?.disableCheckAll"
|
|
155
|
+
v-if="!props.columnControl?.disableCheckAll"
|
|
163
156
|
:disabled="true"
|
|
164
157
|
:divided="false"
|
|
165
158
|
>
|
|
@@ -40,12 +40,14 @@ import {
|
|
|
40
40
|
tableProps,
|
|
41
41
|
paginationProps,
|
|
42
42
|
} from '../../../shared/ui';
|
|
43
|
+
import { ElLoading } from 'element-plus';
|
|
43
44
|
import {
|
|
44
45
|
computed,
|
|
45
46
|
nextTick,
|
|
46
47
|
useAttrs,
|
|
47
48
|
onMounted,
|
|
48
49
|
inject,
|
|
50
|
+
toValue,
|
|
49
51
|
type Slot,
|
|
50
52
|
useSlots,
|
|
51
53
|
} from 'vue';
|
|
@@ -60,6 +62,8 @@ import {
|
|
|
60
62
|
camelizeProperties,
|
|
61
63
|
getObject,
|
|
62
64
|
pick,
|
|
65
|
+
isPromise,
|
|
66
|
+
isPlainObject,
|
|
63
67
|
Path,
|
|
64
68
|
} from '../../../shared/core';
|
|
65
69
|
import {
|
|
@@ -71,7 +75,16 @@ import {
|
|
|
71
75
|
import type { Columns, ColumnControlConfig } from '../types';
|
|
72
76
|
import TableColumnRenderer from './TableColumnRenderer.vue';
|
|
73
77
|
|
|
74
|
-
defineOptions({
|
|
78
|
+
defineOptions({
|
|
79
|
+
name: 'ProTable',
|
|
80
|
+
directives: {
|
|
81
|
+
loading: ElLoading.directive,
|
|
82
|
+
},
|
|
83
|
+
inheritAttrs: false,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const tableProperties = Object.keys(tableProps());
|
|
87
|
+
const paginationProperties = Object.keys(paginationProps());
|
|
75
88
|
|
|
76
89
|
type Control = {
|
|
77
90
|
sizeControl: boolean;
|
|
@@ -108,31 +121,29 @@ type Props = {
|
|
|
108
121
|
pagination?: false | Partial<PaginationProps>;
|
|
109
122
|
} & Omit<TableProps, 'data'>;
|
|
110
123
|
|
|
111
|
-
const {
|
|
112
|
-
table
|
|
113
|
-
search
|
|
114
|
-
addIndexColumn
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
columns
|
|
120
|
-
data
|
|
121
|
-
pagination
|
|
122
|
-
}
|
|
124
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
125
|
+
table: undefined,
|
|
126
|
+
search: undefined,
|
|
127
|
+
addIndexColumn: undefined,
|
|
128
|
+
immediateSearch: undefined,
|
|
129
|
+
control: undefined,
|
|
130
|
+
searchFormConfig: undefined,
|
|
131
|
+
tableContainer: undefined,
|
|
132
|
+
columns: undefined,
|
|
133
|
+
data: undefined,
|
|
134
|
+
pagination: undefined,
|
|
135
|
+
});
|
|
123
136
|
|
|
124
137
|
const { t } = useLocale();
|
|
125
138
|
|
|
126
139
|
const config = INJECT_CONFIG['pro-table'];
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
const tableProperties = Object.keys(tableProps());
|
|
130
|
-
const paginationProperties = Object.keys(paginationProps());
|
|
140
|
+
const injectPropsRaw = inject(config.injectionKey, config.default);
|
|
141
|
+
const injectProps = computed(() => toValue(injectPropsRaw) || {});
|
|
131
142
|
|
|
132
143
|
const attrs = useAttrs() as Record<string, any>;
|
|
133
144
|
|
|
134
145
|
const injectTableAttrs = computed(() => {
|
|
135
|
-
return pick(injectProps, tableProperties) as Partial<TableProps>;
|
|
146
|
+
return pick(injectProps.value, tableProperties) as Partial<TableProps>;
|
|
136
147
|
});
|
|
137
148
|
|
|
138
149
|
const attrsTableProps = computed(() => {
|
|
@@ -142,91 +153,95 @@ const attrsTableProps = computed(() => {
|
|
|
142
153
|
) as Partial<TableProps>;
|
|
143
154
|
});
|
|
144
155
|
|
|
145
|
-
const {
|
|
146
|
-
columns: tableColumns,
|
|
147
|
-
data: tableData,
|
|
148
|
-
searchForm,
|
|
149
|
-
pageParam,
|
|
150
|
-
setPageParam,
|
|
151
|
-
} = table || {};
|
|
152
|
-
|
|
153
156
|
const size = defineModel<TableProps['size']>('size');
|
|
154
|
-
const loading = defineModel<boolean>('loading');
|
|
157
|
+
const loading = defineModel<boolean>('loading', { default: false });
|
|
155
158
|
|
|
156
|
-
//
|
|
159
|
+
// 递增序号做请求竞态守卫,仅最新一次请求可变更 loading
|
|
160
|
+
let searchSequence = 0;
|
|
157
161
|
const _search = async () => {
|
|
162
|
+
const seq = ++searchSequence;
|
|
163
|
+
const searchPromise = (props.search || attrs.onSearch)?.();
|
|
164
|
+
if (!isPromise(searchPromise)) return;
|
|
165
|
+
loading.value = true;
|
|
158
166
|
try {
|
|
159
|
-
|
|
160
|
-
await search?.();
|
|
167
|
+
await searchPromise;
|
|
161
168
|
} finally {
|
|
162
|
-
|
|
169
|
+
if (seq === searchSequence) {
|
|
170
|
+
loading.value = false;
|
|
171
|
+
}
|
|
163
172
|
}
|
|
164
173
|
};
|
|
165
174
|
|
|
166
175
|
// 重置:恢复查询参数到初始值,重新搜索
|
|
167
176
|
const reset = () => {
|
|
168
|
-
table?.resetQueryParams();
|
|
177
|
+
props.table?.resetQueryParams?.();
|
|
169
178
|
nextTick(() => {
|
|
170
179
|
_search();
|
|
171
180
|
});
|
|
172
181
|
};
|
|
173
182
|
|
|
183
|
+
const customSlotNames = [
|
|
184
|
+
'search-form',
|
|
185
|
+
'button-bar',
|
|
186
|
+
'toolbar',
|
|
187
|
+
'table',
|
|
188
|
+
] as const;
|
|
189
|
+
|
|
174
190
|
type CustomSlots = Readonly<
|
|
175
|
-
Partial<Record<
|
|
191
|
+
Partial<Record<(typeof customSlotNames)[number], Slot>>
|
|
176
192
|
>;
|
|
177
|
-
|
|
178
193
|
type Slots = CustomSlots & Readonly<Record<string, Slot>>;
|
|
179
194
|
|
|
180
195
|
const slots = useSlots() as Slots;
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
const toolbarSlot = slots['toolbar'];
|
|
184
|
-
|
|
185
|
-
const tableSlots = computed(() => {
|
|
186
|
-
const customNames = new Set([
|
|
187
|
-
'search-form',
|
|
188
|
-
'button-bar',
|
|
189
|
-
'toolbar',
|
|
190
|
-
'table',
|
|
191
|
-
]);
|
|
192
|
-
return Object.fromEntries(
|
|
193
|
-
Object.entries(slots).filter(([name]) => !customNames.has(name))
|
|
194
|
-
) as Record<string, Slot>;
|
|
195
|
-
});
|
|
196
|
-
const tableSlotNames = computed(() => Object.keys(tableSlots.value));
|
|
196
|
+
const forwardedSlotNames = () =>
|
|
197
|
+
Object.keys(slots).filter(name => !customSlotNames.includes(name as any));
|
|
197
198
|
|
|
198
199
|
const searchPage1st = () => {
|
|
199
|
-
setPageParam?.({ current: 1 });
|
|
200
|
+
props.table?.setPageParam?.({ current: 1 });
|
|
200
201
|
_search();
|
|
201
202
|
};
|
|
202
203
|
|
|
203
204
|
const computedSearchFormConfig = computed(() => {
|
|
204
205
|
const container =
|
|
205
|
-
searchFormConfig?.container ??
|
|
206
|
-
injectProps.searchFormConfig?.container ??
|
|
206
|
+
props.searchFormConfig?.container ??
|
|
207
|
+
(injectProps.value.searchFormConfig as any)?.container ??
|
|
207
208
|
DefaultSearchFormContainer;
|
|
208
209
|
return {
|
|
209
|
-
...injectProps.searchFormConfig,
|
|
210
|
-
...getObject(searchFormConfig),
|
|
210
|
+
...injectProps.value.searchFormConfig,
|
|
211
|
+
...getObject(props.searchFormConfig),
|
|
211
212
|
container: container ? container : undefined,
|
|
212
213
|
};
|
|
213
214
|
});
|
|
214
215
|
|
|
215
216
|
const computedTableContainer = computed(() => {
|
|
216
217
|
const container =
|
|
217
|
-
|
|
218
|
+
props.tableContainer ??
|
|
219
|
+
injectProps.value.tableContainer ??
|
|
220
|
+
DefaultTableContainer;
|
|
218
221
|
return container ? container : undefined;
|
|
219
222
|
});
|
|
220
223
|
|
|
224
|
+
const getIndex = (i: number) => {
|
|
225
|
+
if (props.table?.pageParam && computedPagination.value !== false) {
|
|
226
|
+
const current = props.table.pageParam.current ?? 1;
|
|
227
|
+
const pageSize = props.table.pageParam.pageSize ?? 10;
|
|
228
|
+
return (current - 1) * pageSize + i + 1;
|
|
229
|
+
}
|
|
230
|
+
return i + 1;
|
|
231
|
+
};
|
|
232
|
+
|
|
221
233
|
const normalizedRawColumns = computed(() => {
|
|
222
|
-
const source = (columns ??
|
|
223
|
-
|
|
234
|
+
const source = (props.columns ??
|
|
235
|
+
toValue(props.table?.columns) ??
|
|
236
|
+
[]) as any[];
|
|
237
|
+
if (props.addIndexColumn ?? injectProps.value.addIndexColumn) {
|
|
224
238
|
return [
|
|
225
239
|
{
|
|
226
240
|
key: '_index',
|
|
227
241
|
type: 'index',
|
|
228
242
|
label: t('table.index'),
|
|
229
243
|
width: 80,
|
|
244
|
+
index: getIndex,
|
|
230
245
|
},
|
|
231
246
|
...source,
|
|
232
247
|
];
|
|
@@ -236,7 +251,13 @@ const normalizedRawColumns = computed(() => {
|
|
|
236
251
|
|
|
237
252
|
const computedDataSource = computed<any[]>(() => {
|
|
238
253
|
const attrsData = attrsTableProps.value.data as any[] | undefined;
|
|
239
|
-
return
|
|
254
|
+
return (
|
|
255
|
+
props.data ??
|
|
256
|
+
attrsData ??
|
|
257
|
+
toValue((props.table as any)?.dataSource) ??
|
|
258
|
+
toValue(props.table?.data) ??
|
|
259
|
+
[]
|
|
260
|
+
);
|
|
240
261
|
});
|
|
241
262
|
|
|
242
263
|
const computedTableProps = computed<TableProps>(() => {
|
|
@@ -248,40 +269,68 @@ const computedTableProps = computed<TableProps>(() => {
|
|
|
248
269
|
return merged;
|
|
249
270
|
});
|
|
250
271
|
|
|
272
|
+
// 分页优先级:tableAttrs 显式配置 > provider 全局配置 > 默认合成
|
|
251
273
|
const computedPagination = computed<false | Partial<PaginationProps>>(() => {
|
|
252
|
-
|
|
253
|
-
|
|
274
|
+
const attrsPagination = (attrsTableProps.value as any).pagination;
|
|
275
|
+
if (props.pagination === false || attrsPagination === false) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
const hasUserPagination =
|
|
279
|
+
isPlainObject(props.pagination) || isPlainObject(attrsPagination);
|
|
280
|
+
if (hasUserPagination) {
|
|
281
|
+
const pageConfig = (props.pagination ??
|
|
282
|
+
attrsPagination ??
|
|
283
|
+
{}) as Partial<PaginationProps>;
|
|
284
|
+
const globalPagination =
|
|
285
|
+
(injectProps.value.pagination as any) === false
|
|
286
|
+
? {}
|
|
287
|
+
: (injectProps.value.pagination as Partial<PaginationProps>) || {};
|
|
288
|
+
const pageParam = props.table?.pageParam;
|
|
289
|
+
return {
|
|
290
|
+
...globalPagination,
|
|
291
|
+
...pick(pageConfig as Record<string, any>, paginationProperties),
|
|
292
|
+
currentPage: pageParam?.current ?? (pageConfig as any).currentPage ?? 1,
|
|
293
|
+
pageSize: pageParam?.pageSize ?? (pageConfig as any).pageSize ?? 10,
|
|
294
|
+
total: pageParam?.total ?? (pageConfig as any).total ?? 0,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if ((injectProps.value.pagination as any) === false) return false;
|
|
299
|
+
|
|
300
|
+
const pageParam = props.table?.pageParam;
|
|
254
301
|
return {
|
|
255
|
-
...injectProps.pagination,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
total: (pageConfig as any).total ?? pageParam?.total ?? 0,
|
|
302
|
+
...(injectProps.value.pagination as Partial<PaginationProps>),
|
|
303
|
+
currentPage: pageParam?.current ?? 1,
|
|
304
|
+
pageSize: pageParam?.pageSize ?? 10,
|
|
305
|
+
total: pageParam?.total ?? 0,
|
|
260
306
|
};
|
|
261
307
|
});
|
|
262
308
|
|
|
263
309
|
const showPagination = computed(() => computedPagination.value !== false);
|
|
264
310
|
|
|
265
311
|
const onCurrentChange = (current: number) => {
|
|
266
|
-
setPageParam?.({ current });
|
|
312
|
+
props.table?.setPageParam?.({ current });
|
|
267
313
|
_search();
|
|
268
314
|
};
|
|
269
315
|
|
|
270
316
|
const onSizeChange = (pageSize: number) => {
|
|
271
|
-
setPageParam?.({ pageSize, current: 1 });
|
|
317
|
+
props.table?.setPageParam?.({ pageSize, current: 1 });
|
|
272
318
|
_search();
|
|
273
319
|
};
|
|
274
320
|
|
|
275
321
|
const onPaginationChange = (currentPage: number, pageSize: number) => {
|
|
322
|
+
// 透传 el-pagination change 回调
|
|
276
323
|
attrs.onChange?.(currentPage, pageSize);
|
|
277
324
|
};
|
|
278
325
|
|
|
279
|
-
const
|
|
326
|
+
const searchFormVisible = computed(() => {
|
|
327
|
+
const searchFields = props.table?.searchForm?.fields;
|
|
328
|
+
const fieldList =
|
|
329
|
+
(Array.isArray(searchFields) ? searchFields : searchFields?.value) ?? [];
|
|
280
330
|
return !!(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
searchForm.fields.value.filter(item => !item.hidden)?.length > 0)
|
|
331
|
+
(computedSearchFormConfig.value as any)?.hidden !== true &&
|
|
332
|
+
props.table?.searchForm &&
|
|
333
|
+
fieldList.filter((item: any) => !toValue(item.hidden))?.length > 0
|
|
285
334
|
);
|
|
286
335
|
});
|
|
287
336
|
|
|
@@ -289,13 +338,13 @@ const computedControl = computed<{
|
|
|
289
338
|
sizeControl: boolean;
|
|
290
339
|
columnControl: boolean | ColumnControlConfig<Path<RecordType>>;
|
|
291
340
|
}>(() => {
|
|
292
|
-
const mergedControl = control ?? injectProps.control;
|
|
341
|
+
const mergedControl = props.control ?? injectProps.value.control;
|
|
293
342
|
if (typeof mergedControl === 'boolean') {
|
|
294
343
|
return { sizeControl: mergedControl, columnControl: mergedControl };
|
|
295
344
|
}
|
|
296
345
|
const c = mergedControl as Control;
|
|
297
|
-
const sizeControl = c
|
|
298
|
-
const columnControl = c
|
|
346
|
+
const sizeControl = c?.sizeControl ?? false;
|
|
347
|
+
const columnControl = c?.columnControl ?? false;
|
|
299
348
|
return { sizeControl: !!sizeControl, columnControl };
|
|
300
349
|
});
|
|
301
350
|
|
|
@@ -313,22 +362,27 @@ const resolvedSize = computed(() => {
|
|
|
313
362
|
});
|
|
314
363
|
|
|
315
364
|
onMounted(() => {
|
|
316
|
-
if (immediateSearch ?? injectProps.immediateSearch) {
|
|
365
|
+
if (props.immediateSearch ?? injectProps.value.immediateSearch) {
|
|
317
366
|
_search();
|
|
318
367
|
}
|
|
319
368
|
});
|
|
369
|
+
|
|
370
|
+
defineExpose({
|
|
371
|
+
/** 手动触发一次查询(与内部搜索共用 loading 竞态守卫),便于外部刷新列表 */
|
|
372
|
+
search: _search,
|
|
373
|
+
});
|
|
320
374
|
</script>
|
|
321
375
|
|
|
322
376
|
<template>
|
|
323
377
|
<div class="pro-table" :class="attrs.class" :style="attrs.style">
|
|
324
378
|
<ContainerFragment
|
|
325
|
-
v-if="
|
|
379
|
+
v-if="searchFormVisible || $slots['search-form']"
|
|
326
380
|
:component="computedSearchFormConfig.container"
|
|
327
381
|
>
|
|
328
382
|
<slot name="search-form">
|
|
329
383
|
<SearchForm
|
|
330
|
-
v-if="searchForm"
|
|
331
|
-
:form="searchForm"
|
|
384
|
+
v-if="props.table?.searchForm"
|
|
385
|
+
:form="props.table.searchForm"
|
|
332
386
|
v-bind="computedSearchFormConfig"
|
|
333
387
|
@search="searchPage1st"
|
|
334
388
|
@reset="reset"
|
|
@@ -338,10 +392,10 @@ onMounted(() => {
|
|
|
338
392
|
|
|
339
393
|
<ContainerFragment :component="computedTableContainer">
|
|
340
394
|
<div class="pro-table_header">
|
|
341
|
-
<div v-if="
|
|
395
|
+
<div v-if="$slots['button-bar']" class="pro-table_header_button-bar">
|
|
342
396
|
<slot name="button-bar" />
|
|
343
397
|
</div>
|
|
344
|
-
<div v-if="
|
|
398
|
+
<div v-if="$slots['toolbar']" class="pro-table_header_toolbar">
|
|
345
399
|
<slot name="toolbar" />
|
|
346
400
|
</div>
|
|
347
401
|
<div
|
|
@@ -358,7 +412,7 @@ onMounted(() => {
|
|
|
358
412
|
:columns="
|
|
359
413
|
normalizedRawColumns.filter(item => item.key !== '_index')
|
|
360
414
|
"
|
|
361
|
-
:table="table"
|
|
415
|
+
:table="props.table"
|
|
362
416
|
:column-control="columnControlConfig"
|
|
363
417
|
/>
|
|
364
418
|
</div>
|
|
@@ -373,7 +427,11 @@ onMounted(() => {
|
|
|
373
427
|
:data="computedDataSource"
|
|
374
428
|
>
|
|
375
429
|
<TableColumnRenderer :columns="normalizedRawColumns" />
|
|
376
|
-
<template
|
|
430
|
+
<template
|
|
431
|
+
v-for="name in forwardedSlotNames()"
|
|
432
|
+
:key="name"
|
|
433
|
+
#[name]="scoped"
|
|
434
|
+
>
|
|
377
435
|
<slot :name="name" v-bind="scoped" />
|
|
378
436
|
</template>
|
|
379
437
|
</ElTable>
|