@qin-ui/element-plus-pro 1.2.3 → 1.3.0

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.
Files changed (177) hide show
  1. package/README.md +5 -5
  2. package/api.json +1 -25
  3. package/es/__tests__/BaseField.spec.d.ts +2 -0
  4. package/es/__tests__/BaseField.spec.d.ts.map +1 -0
  5. package/es/__tests__/BaseFormItem.spec.d.ts +2 -0
  6. package/es/__tests__/BaseFormItem.spec.d.ts.map +1 -0
  7. package/es/__tests__/ProForm.spec.d.ts +2 -0
  8. package/es/__tests__/ProForm.spec.d.ts.map +1 -0
  9. package/es/__tests__/ProTable.spec.d.ts +2 -0
  10. package/es/__tests__/ProTable.spec.d.ts.map +1 -0
  11. package/es/__tests__/SearchForm.spec.d.ts +2 -0
  12. package/es/__tests__/SearchForm.spec.d.ts.map +1 -0
  13. package/es/__tests__/controls.spec.d.ts +2 -0
  14. package/es/__tests__/controls.spec.d.ts.map +1 -0
  15. package/es/__tests__/helpers/fixtures.d.ts +15 -0
  16. package/es/__tests__/helpers/fixtures.d.ts.map +1 -0
  17. package/es/__tests__/helpers/mount.d.ts +38 -0
  18. package/es/__tests__/helpers/mount.d.ts.map +1 -0
  19. package/es/__tests__/hooks-locale.spec.d.ts +2 -0
  20. package/es/__tests__/hooks-locale.spec.d.ts.map +1 -0
  21. package/es/__tests__/index.spec.d.ts +2 -0
  22. package/es/__tests__/index.spec.d.ts.map +1 -0
  23. package/es/__tests__/integration.spec.d.ts +2 -0
  24. package/es/__tests__/integration.spec.d.ts.map +1 -0
  25. package/es/__tests__/setup.d.ts +14 -0
  26. package/es/__tests__/setup.d.ts.map +1 -0
  27. package/es/element-plus-pro.css +14 -14
  28. package/es/index.js +478 -358
  29. package/es/index.js.map +1 -1
  30. package/es/{components → src/components}/component-provider/components/index.vue.d.ts +5 -1
  31. package/es/src/components/component-provider/components/index.vue.d.ts.map +1 -0
  32. package/es/src/components/component-provider/constants/index.d.ts.map +1 -0
  33. package/es/src/components/component-provider/index.d.ts.map +1 -0
  34. package/es/{components → src/components}/component-provider/types/index.d.ts +9 -5
  35. package/es/src/components/component-provider/types/index.d.ts.map +1 -0
  36. package/es/{components → src/components}/form/components/BaseField/index.vue.d.ts +4 -1
  37. package/es/src/components/form/components/BaseField/index.vue.d.ts.map +1 -0
  38. package/es/src/components/form/components/BaseField/utils/index.d.ts +5 -0
  39. package/es/src/components/form/components/BaseField/utils/index.d.ts.map +1 -0
  40. package/es/{components → src/components}/form/components/BaseFormItem/index.vue.d.ts +4 -2
  41. package/es/src/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -0
  42. package/es/src/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -0
  43. package/es/src/components/form/components/ProForm/index.vue.d.ts.map +1 -0
  44. package/es/src/components/form/components/SlotComponent/index.vue.d.ts.map +1 -0
  45. package/es/src/components/form/components/index.d.ts.map +1 -0
  46. package/es/{components → src/components}/form/constants/index.d.ts +1 -1
  47. package/es/src/components/form/constants/index.d.ts.map +1 -0
  48. package/es/src/components/form/hooks/index.d.ts.map +1 -0
  49. package/es/src/components/form/hooks/useFields.d.ts.map +1 -0
  50. package/es/src/components/form/hooks/useForm.d.ts.map +1 -0
  51. package/es/src/components/form/hooks/useFormRef.d.ts.map +1 -0
  52. package/es/src/components/form/hooks/useGroupFieldAttrs.d.ts +18 -0
  53. package/es/src/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -0
  54. package/es/src/components/form/index.d.ts.map +1 -0
  55. package/es/{components → src/components}/form/types/index.d.ts +26 -2
  56. package/es/src/components/form/types/index.d.ts.map +1 -0
  57. package/es/{components → src/components}/table/components/ColumnControl.vue.d.ts +4 -1
  58. package/es/src/components/table/components/ColumnControl.vue.d.ts.map +1 -0
  59. package/es/src/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -0
  60. package/es/src/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -0
  61. package/es/src/components/table/components/ProTable.vue.d.ts.map +1 -0
  62. package/es/{components → src/components}/table/components/SearchForm.vue.d.ts +14 -4
  63. package/es/src/components/table/components/SearchForm.vue.d.ts.map +1 -0
  64. package/es/src/components/table/components/SizeControl.vue.d.ts.map +1 -0
  65. package/es/src/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -0
  66. package/es/src/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +1 -0
  67. package/es/src/components/table/components/icons/DownOutlined.vue.d.ts.map +1 -0
  68. package/es/src/components/table/components/icons/FilterOutlined.vue.d.ts.map +1 -0
  69. package/es/src/components/table/components/index.d.ts.map +1 -0
  70. package/es/src/components/table/index.d.ts.map +1 -0
  71. package/es/src/components/table/types/index.d.ts.map +1 -0
  72. package/es/src/components/table/useTable.d.ts.map +1 -0
  73. package/es/{index.d.ts → src/index.d.ts} +1 -1
  74. package/es/src/index.d.ts.map +1 -0
  75. package/es/src/locale/en-US.d.ts.map +1 -0
  76. package/es/src/locale/index.d.ts.map +1 -0
  77. package/es/src/locale/zh-CN.d.ts.map +1 -0
  78. package/es/src/locale/zh-TW.d.ts.map +1 -0
  79. package/es/src/shared/core/index.d.ts +6 -0
  80. package/es/src/shared/core/index.d.ts.map +1 -0
  81. package/es/{shared → src/shared}/ui/index.d.ts +2 -0
  82. package/es/src/shared/ui/index.d.ts.map +1 -0
  83. package/package.json +8 -3
  84. package/src/components/component-provider/components/index.vue +37 -8
  85. package/src/components/component-provider/constants/index.ts +2 -2
  86. package/src/components/component-provider/types/index.ts +9 -5
  87. package/src/components/form/components/BaseField/index.vue +67 -35
  88. package/src/components/form/components/BaseField/utils/index.ts +6 -11
  89. package/src/components/form/components/BaseFormItem/index.vue +87 -100
  90. package/src/components/form/components/ProForm/index.vue +72 -18
  91. package/src/components/form/constants/index.ts +1 -5
  92. package/src/components/form/hooks/useGroupFieldAttrs.ts +123 -0
  93. package/src/components/form/types/index.ts +26 -2
  94. package/src/components/table/components/ColumnControl.vue +18 -24
  95. package/src/components/table/components/ProTable.vue +112 -57
  96. package/src/components/table/components/SearchForm.vue +85 -60
  97. package/src/components/table/components/SizeControl.vue +2 -2
  98. package/src/components/table/components/TableColumnRenderer.vue +12 -9
  99. package/src/components/table/useTable.ts +12 -7
  100. package/src/index.ts +1 -1
  101. package/src/locale/index.ts +6 -2
  102. package/src/shared/core/index.ts +4 -3
  103. package/src/shared/ui/index.ts +11 -5
  104. package/es/components/component-provider/components/index.vue.d.ts.map +0 -1
  105. package/es/components/component-provider/constants/index.d.ts.map +0 -1
  106. package/es/components/component-provider/index.d.ts.map +0 -1
  107. package/es/components/component-provider/types/index.d.ts.map +0 -1
  108. package/es/components/form/components/BaseField/index.vue.d.ts.map +0 -1
  109. package/es/components/form/components/BaseField/utils/index.d.ts +0 -5
  110. package/es/components/form/components/BaseField/utils/index.d.ts.map +0 -1
  111. package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +0 -1
  112. package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +0 -1
  113. package/es/components/form/components/GroupedFieldAttrs/index.vue.d.ts +0 -31
  114. package/es/components/form/components/GroupedFieldAttrs/index.vue.d.ts.map +0 -1
  115. package/es/components/form/components/PathProvider/index.vue.d.ts +0 -24
  116. package/es/components/form/components/PathProvider/index.vue.d.ts.map +0 -1
  117. package/es/components/form/components/ProForm/index.vue.d.ts.map +0 -1
  118. package/es/components/form/components/SlotComponent/index.vue.d.ts.map +0 -1
  119. package/es/components/form/components/index.d.ts.map +0 -1
  120. package/es/components/form/constants/index.d.ts.map +0 -1
  121. package/es/components/form/hooks/index.d.ts.map +0 -1
  122. package/es/components/form/hooks/useFields.d.ts.map +0 -1
  123. package/es/components/form/hooks/useForm.d.ts.map +0 -1
  124. package/es/components/form/hooks/useFormRef.d.ts.map +0 -1
  125. package/es/components/form/index.d.ts.map +0 -1
  126. package/es/components/form/types/index.d.ts.map +0 -1
  127. package/es/components/table/components/ColumnControl.vue.d.ts.map +0 -1
  128. package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +0 -1
  129. package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +0 -1
  130. package/es/components/table/components/ProTable.vue.d.ts.map +0 -1
  131. package/es/components/table/components/SearchForm.vue.d.ts.map +0 -1
  132. package/es/components/table/components/SizeControl.vue.d.ts.map +0 -1
  133. package/es/components/table/components/TableColumnRenderer.vue.d.ts.map +0 -1
  134. package/es/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +0 -1
  135. package/es/components/table/components/icons/DownOutlined.vue.d.ts.map +0 -1
  136. package/es/components/table/components/icons/FilterOutlined.vue.d.ts.map +0 -1
  137. package/es/components/table/components/index.d.ts.map +0 -1
  138. package/es/components/table/index.d.ts.map +0 -1
  139. package/es/components/table/types/index.d.ts.map +0 -1
  140. package/es/components/table/useTable.d.ts.map +0 -1
  141. package/es/index.d.ts.map +0 -1
  142. package/es/locale/en-US.d.ts.map +0 -1
  143. package/es/locale/index.d.ts.map +0 -1
  144. package/es/locale/zh-CN.d.ts.map +0 -1
  145. package/es/locale/zh-TW.d.ts.map +0 -1
  146. package/es/shared/core/index.d.ts +0 -6
  147. package/es/shared/core/index.d.ts.map +0 -1
  148. package/es/shared/ui/index.d.ts.map +0 -1
  149. package/src/components/form/components/GroupedFieldAttrs/index.vue +0 -115
  150. package/src/components/form/components/PathProvider/index.vue +0 -19
  151. /package/es/{components → src/components}/component-provider/constants/index.d.ts +0 -0
  152. /package/es/{components → src/components}/component-provider/index.d.ts +0 -0
  153. /package/es/{components → src/components}/form/components/ContainerFragment/index.vue.d.ts +0 -0
  154. /package/es/{components → src/components}/form/components/ProForm/index.vue.d.ts +0 -0
  155. /package/es/{components → src/components}/form/components/SlotComponent/index.vue.d.ts +0 -0
  156. /package/es/{components → src/components}/form/components/index.d.ts +0 -0
  157. /package/es/{components → src/components}/form/hooks/index.d.ts +0 -0
  158. /package/es/{components → src/components}/form/hooks/useFields.d.ts +0 -0
  159. /package/es/{components → src/components}/form/hooks/useForm.d.ts +0 -0
  160. /package/es/{components → src/components}/form/hooks/useFormRef.d.ts +0 -0
  161. /package/es/{components → src/components}/form/index.d.ts +0 -0
  162. /package/es/{components → src/components}/table/components/DefaultSearchFormContainer.vue.d.ts +0 -0
  163. /package/es/{components → src/components}/table/components/DefaultTableContainer.vue.d.ts +0 -0
  164. /package/es/{components → src/components}/table/components/ProTable.vue.d.ts +0 -0
  165. /package/es/{components → src/components}/table/components/SizeControl.vue.d.ts +0 -0
  166. /package/es/{components → src/components}/table/components/TableColumnRenderer.vue.d.ts +0 -0
  167. /package/es/{components → src/components}/table/components/icons/ColumnHeightOutlined.vue.d.ts +0 -0
  168. /package/es/{components → src/components}/table/components/icons/DownOutlined.vue.d.ts +0 -0
  169. /package/es/{components → src/components}/table/components/icons/FilterOutlined.vue.d.ts +0 -0
  170. /package/es/{components → src/components}/table/components/index.d.ts +0 -0
  171. /package/es/{components → src/components}/table/index.d.ts +0 -0
  172. /package/es/{components → src/components}/table/types/index.d.ts +0 -0
  173. /package/es/{components → src/components}/table/useTable.d.ts +0 -0
  174. /package/es/{locale → src/locale}/en-US.d.ts +0 -0
  175. /package/es/{locale → src/locale}/index.d.ts +0 -0
  176. /package/es/{locale → src/locale}/zh-CN.d.ts +0 -0
  177. /package/es/{locale → src/locale}/zh-TW.d.ts +0 -0
@@ -0,0 +1,123 @@
1
+ import { computed, inject, mergeProps, toValue, type ComputedRef } from 'vue';
2
+ import {
3
+ formItemProps,
4
+ gridItemProps,
5
+ type FormItemProps,
6
+ type GridItemProps,
7
+ } from '../../../shared/ui';
8
+ import { isPlainObject } from '../../../shared/core';
9
+ import { INJECT_CONFIG } from '../../component-provider/constants';
10
+ import { FORM_ITEM_SLOT_KEYS } from '../constants';
11
+ import type { Base, Field } from '../types';
12
+
13
+ const formItemPropKeys = Object.keys(formItemProps()).filter(
14
+ k => k !== 'disabled'
15
+ );
16
+ const gridItemPropKeys = Object.keys(gridItemProps());
17
+
18
+ export interface GroupedField {
19
+ path: string;
20
+ gridItemProps: GridItemProps;
21
+ formItemProps: FormItemProps;
22
+ formItemSlots: Record<(typeof FORM_ITEM_SLOT_KEYS)[number], any>;
23
+ componentProps: Record<string, any>;
24
+ container?: Base['formItemContainer'];
25
+ rawField: Field;
26
+ }
27
+
28
+ /**
29
+ * 将 fields 配置数组逐个拆分为 grid / formItem / component / slots 分组。
30
+ */
31
+ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
32
+ const config = INJECT_CONFIG['pro-form-item'];
33
+ const injectFormItemProps = inject(config.injectionKey, config.default);
34
+
35
+ return computed<GroupedField[]>(() => {
36
+ const rawInjectProps = toValue(injectFormItemProps) || {};
37
+ return validFields.value.map(field => {
38
+ let gridItemProps: Record<string, any> = {};
39
+ let formItemProps: Record<string, any> = {};
40
+ const componentProps: Record<string, any> = { slots: {} };
41
+ const formItemSlots: Record<string, any> = {};
42
+
43
+ if (isPlainObject(field)) {
44
+ // prettier-ignore
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ const { path, name, fields, formItemClass, formItemStyle, formItemContainer, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
47
+ field as any;
48
+
49
+ const {
50
+ class: injectClassName,
51
+ style: injectStyle,
52
+ span: injectSpan,
53
+ formItemContainer: injectFormItemContainer,
54
+ xs,
55
+ sm,
56
+ md,
57
+ lg,
58
+ xl,
59
+ ...injectRest
60
+ } = rawInjectProps;
61
+
62
+ // 字段显式 span 优先,否则回退注入配置的响应式断点
63
+ const initGridItemProps =
64
+ span || span === 0
65
+ ? { span }
66
+ : { xs, sm, md, lg, xl, span: injectSpan };
67
+
68
+ gridItemProps = fields ? { span: span ?? 24 } : initGridItemProps;
69
+ formItemProps = mergeProps(
70
+ injectRest as any,
71
+ { class: injectClassName, style: injectStyle },
72
+ { class: toValue(formItemClass), style: toValue(formItemStyle) },
73
+ formItemDataAttrs
74
+ );
75
+ const container = formItemContainer ?? injectFormItemContainer;
76
+
77
+ Object.keys(rest).forEach(k => {
78
+ if (gridItemPropKeys.includes(k)) {
79
+ gridItemProps[k] = rest[k];
80
+ } else if (formItemPropKeys.includes(k)) {
81
+ (formItemProps as any)[k] = rest[k];
82
+ } else {
83
+ componentProps[k] = rest[k];
84
+ }
85
+ });
86
+
87
+ Object.assign(componentProps, componentDataAttrs);
88
+ Object.keys(slots).forEach(k => {
89
+ if (FORM_ITEM_SLOT_KEYS.includes(k as any)) {
90
+ formItemSlots[k] = slots[k];
91
+ } else {
92
+ componentProps.slots[k] = slots[k];
93
+ }
94
+ });
95
+
96
+ const raw = (toValue(name) ?? toValue(path)) as
97
+ | string
98
+ | number[]
99
+ | undefined;
100
+ return {
101
+ path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
102
+ gridItemProps: gridItemProps as GridItemProps,
103
+ formItemProps: formItemProps as any,
104
+ formItemSlots,
105
+ componentProps,
106
+ container,
107
+ rawField: field,
108
+ };
109
+ }
110
+
111
+ const raw =
112
+ toValue((field as any)?.name) ?? toValue((field as any)?.path);
113
+ return {
114
+ path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
115
+ gridItemProps: gridItemProps as GridItemProps,
116
+ formItemProps: formItemProps as any,
117
+ formItemSlots,
118
+ componentProps,
119
+ rawField: field,
120
+ };
121
+ });
122
+ });
123
+ }
@@ -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: any): any }
35
- | { get?: (val: any) => any; set?: (val: any, oldVal: any) => any };
58
+ | { (val: any, oldVal?: any): any }
59
+ | { get?: (val: any) => any; set?: (val: any, oldVal?: any) => any };
36
60
 
37
61
  /**
38
62
  * @description 自定义组件
@@ -11,28 +11,27 @@ 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
- columns,
22
- table = undefined,
23
- columnControl = undefined,
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 key = column.key ?? column.prop;
34
- if (key === undefined || key === null) return `__col_${index}`;
35
- return Array.isArray(key) ? key.join('.') : String(key);
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,13 +43,13 @@ 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
55
  const label = String(
@@ -85,7 +84,7 @@ const checkedColumnKeys = computed({
85
84
  set(val: string[]) {
86
85
  checkedColumnsOptions.value.forEach(item => {
87
86
  if (item.disabled) return;
88
- table?.setColumn(item.value as any, {
87
+ props.table?.setColumn(item.value as any, {
89
88
  hidden: !val.includes(item.value),
90
89
  });
91
90
  });
@@ -130,16 +129,16 @@ watch(
130
129
 
131
130
  // 强制 disabledKeys 列始终展示
132
131
  onMounted(() => {
133
- columnControl?.disabledKeys?.forEach(key => {
134
- table?.setColumn(String(key), { hidden: false });
132
+ props.columnControl?.disabledKeys?.forEach(key => {
133
+ props.table?.setColumn(String(key), { hidden: false });
135
134
  });
136
135
  });
137
136
 
138
137
  watch(
139
- () => columnControl?.disabledKeys,
138
+ () => props.columnControl?.disabledKeys,
140
139
  keys => {
141
140
  keys?.forEach(key => {
142
- table?.setColumn(String(key), { hidden: false });
141
+ props.table?.setColumn(String(key), { hidden: false });
143
142
  });
144
143
  },
145
144
  { deep: true }
@@ -147,19 +146,14 @@ watch(
147
146
  </script>
148
147
 
149
148
  <template>
150
- <Dropdown
151
- trigger="click"
152
- :hide-on-click="false"
153
- :visible="open"
154
- @visible-change="val => (open = val)"
155
- >
149
+ <Dropdown trigger="click" :hide-on-click="false">
156
150
  <Button text class="pro-table_column-control_button">
157
151
  <FilterOutlined />
158
152
  </Button>
159
153
  <template #dropdown>
160
154
  <DropdownMenu>
161
155
  <DropdownItem
162
- v-if="!columnControl?.disableCheckAll"
156
+ v-if="!props.columnControl?.disableCheckAll"
163
157
  :disabled="true"
164
158
  :divided="false"
165
159
  >
@@ -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,13 @@ import {
71
75
  import type { Columns, ColumnControlConfig } from '../types';
72
76
  import TableColumnRenderer from './TableColumnRenderer.vue';
73
77
 
74
- defineOptions({ name: 'ProTable', inheritAttrs: false });
78
+ defineOptions({
79
+ name: 'ProTable',
80
+ directives: {
81
+ loading: ElLoading.directive,
82
+ },
83
+ inheritAttrs: false,
84
+ });
75
85
 
76
86
  type Control = {
77
87
  sizeControl: boolean;
@@ -108,23 +118,24 @@ type Props = {
108
118
  pagination?: false | Partial<PaginationProps>;
109
119
  } & Omit<TableProps, 'data'>;
110
120
 
111
- const {
112
- table = undefined,
113
- search = undefined,
114
- addIndexColumn = undefined,
115
- control = undefined,
116
- tableContainer = undefined,
117
- immediateSearch = undefined,
118
- searchFormConfig = undefined,
119
- columns = undefined,
120
- data = undefined,
121
- pagination = undefined,
122
- } = defineProps<Props>();
121
+ const props = withDefaults(defineProps<Props>(), {
122
+ table: undefined,
123
+ search: undefined,
124
+ addIndexColumn: undefined,
125
+ immediateSearch: undefined,
126
+ control: undefined,
127
+ searchFormConfig: undefined,
128
+ tableContainer: undefined,
129
+ columns: undefined,
130
+ data: undefined,
131
+ pagination: undefined,
132
+ });
123
133
 
124
134
  const { t } = useLocale();
125
135
 
126
136
  const config = INJECT_CONFIG['pro-table'];
127
- const injectProps = inject(config.injectionKey, config.default) as any;
137
+ const injectPropsRaw = inject(config.injectionKey, config.default);
138
+ const injectProps = computed(() => toValue(injectPropsRaw) || {});
128
139
 
129
140
  const tableProperties = Object.keys(tableProps());
130
141
  const paginationProperties = Object.keys(paginationProps());
@@ -132,7 +143,7 @@ const paginationProperties = Object.keys(paginationProps());
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,30 +153,25 @@ 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 状态,调用外部 search 函数
157
160
  const _search = async () => {
158
- try {
161
+ const searchPromise = (props.search || attrs.onSearch)?.();
162
+ if (isPromise(searchPromise)) {
159
163
  loading.value = true;
160
- await search?.();
161
- } finally {
162
- loading.value = false;
164
+ try {
165
+ await searchPromise;
166
+ } finally {
167
+ loading.value = false;
168
+ }
163
169
  }
164
170
  };
165
171
 
166
172
  // 重置:恢复查询参数到初始值,重新搜索
167
173
  const reset = () => {
168
- table?.resetQueryParams();
174
+ props.table?.resetQueryParams?.();
169
175
  nextTick(() => {
170
176
  _search();
171
177
  });
@@ -196,37 +202,51 @@ const tableSlots = computed(() => {
196
202
  const tableSlotNames = computed(() => Object.keys(tableSlots.value));
197
203
 
198
204
  const searchPage1st = () => {
199
- setPageParam?.({ current: 1 });
205
+ props.table?.setPageParam?.({ current: 1 });
200
206
  _search();
201
207
  };
202
208
 
203
209
  const computedSearchFormConfig = computed(() => {
204
210
  const container =
205
- searchFormConfig?.container ??
206
- injectProps.searchFormConfig?.container ??
211
+ props.searchFormConfig?.container ??
212
+ (injectProps.value.searchFormConfig as any)?.container ??
207
213
  DefaultSearchFormContainer;
208
214
  return {
209
- ...injectProps.searchFormConfig,
210
- ...getObject(searchFormConfig),
215
+ ...injectProps.value.searchFormConfig,
216
+ ...getObject(props.searchFormConfig),
211
217
  container: container ? container : undefined,
212
218
  };
213
219
  });
214
220
 
215
221
  const computedTableContainer = computed(() => {
216
222
  const container =
217
- tableContainer ?? injectProps.tableContainer ?? DefaultTableContainer;
223
+ props.tableContainer ??
224
+ injectProps.value.tableContainer ??
225
+ DefaultTableContainer;
218
226
  return container ? container : undefined;
219
227
  });
220
228
 
229
+ const getIndex = (i: number) => {
230
+ if (props.table?.pageParam && computedPagination.value !== false) {
231
+ const current = props.table.pageParam.current ?? 1;
232
+ const pageSize = props.table.pageParam.pageSize ?? 10;
233
+ return (current - 1) * pageSize + i + 1;
234
+ }
235
+ return i + 1;
236
+ };
237
+
221
238
  const normalizedRawColumns = computed(() => {
222
- const source = (columns ?? tableColumns?.value ?? []) as any[];
223
- if (addIndexColumn ?? injectProps.addIndexColumn) {
239
+ const source = (props.columns ??
240
+ toValue(props.table?.columns) ??
241
+ []) as any[];
242
+ if (props.addIndexColumn ?? injectProps.value.addIndexColumn) {
224
243
  return [
225
244
  {
226
245
  key: '_index',
227
246
  type: 'index',
228
247
  label: t('table.index'),
229
248
  width: 80,
249
+ index: getIndex,
230
250
  },
231
251
  ...source,
232
252
  ];
@@ -236,7 +256,13 @@ const normalizedRawColumns = computed(() => {
236
256
 
237
257
  const computedDataSource = computed<any[]>(() => {
238
258
  const attrsData = attrsTableProps.value.data as any[] | undefined;
239
- return data ?? attrsData ?? tableData?.value ?? [];
259
+ return (
260
+ props.data ??
261
+ attrsData ??
262
+ toValue((props.table as any)?.dataSource) ??
263
+ toValue(props.table?.data) ??
264
+ []
265
+ );
240
266
  });
241
267
 
242
268
  const computedTableProps = computed<TableProps>(() => {
@@ -248,40 +274,69 @@ const computedTableProps = computed<TableProps>(() => {
248
274
  return merged;
249
275
  });
250
276
 
277
+ // 分页优先级:tableAttrs 显式配置 > provider 全局配置 > 默认合成
251
278
  const computedPagination = computed<false | Partial<PaginationProps>>(() => {
252
- if (pagination === false) return false;
253
- const pageConfig = pagination ?? {};
279
+ const attrsPagination = (attrsTableProps.value as any).pagination;
280
+ if (props.pagination === false || attrsPagination === false) {
281
+ return false;
282
+ }
283
+ const hasUserPagination =
284
+ isPlainObject(props.pagination) || isPlainObject(attrsPagination);
285
+ if (hasUserPagination) {
286
+ const pageConfig = (props.pagination ??
287
+ attrsPagination ??
288
+ {}) as Partial<PaginationProps>;
289
+ const globalPagination =
290
+ (injectProps.value.pagination as any) === false
291
+ ? {}
292
+ : (injectProps.value.pagination as Partial<PaginationProps>) || {};
293
+ const pageParam = props.table?.pageParam;
294
+ return {
295
+ ...globalPagination,
296
+ ...pick(pageConfig as Record<string, any>, paginationProperties),
297
+ currentPage: pageParam?.current ?? (pageConfig as any).currentPage ?? 1,
298
+ pageSize: pageParam?.pageSize ?? (pageConfig as any).pageSize ?? 10,
299
+ total: pageParam?.total ?? (pageConfig as any).total ?? 0,
300
+ };
301
+ }
302
+
303
+ if ((injectProps.value.pagination as any) === false) return false;
304
+
305
+ const pageParam = props.table?.pageParam;
254
306
  return {
255
- ...injectProps.pagination,
256
- ...pick(pageConfig as Record<string, any>, paginationProperties),
257
- currentPage: (pageConfig as any).currentPage ?? pageParam?.current ?? 1,
258
- pageSize: (pageConfig as any).pageSize ?? pageParam?.pageSize ?? 10,
259
- total: (pageConfig as any).total ?? pageParam?.total ?? 0,
307
+ ...(injectProps.value.pagination as Partial<PaginationProps>),
308
+ currentPage: pageParam?.current ?? 1,
309
+ pageSize: pageParam?.pageSize ?? 10,
310
+ total: pageParam?.total ?? 0,
260
311
  };
261
312
  });
262
313
 
263
314
  const showPagination = computed(() => computedPagination.value !== false);
264
315
 
265
316
  const onCurrentChange = (current: number) => {
266
- setPageParam?.({ current });
317
+ props.table?.setPageParam?.({ current });
267
318
  _search();
268
319
  };
269
320
 
270
321
  const onSizeChange = (pageSize: number) => {
271
- setPageParam?.({ pageSize, current: 1 });
322
+ props.table?.setPageParam?.({ pageSize, current: 1 });
272
323
  _search();
273
324
  };
274
325
 
275
326
  const onPaginationChange = (currentPage: number, pageSize: number) => {
327
+ // 透传 el-pagination change 回调
276
328
  attrs.onChange?.(currentPage, pageSize);
277
329
  };
278
330
 
279
331
  const showSearch = computed(() => {
332
+ const searchFields = props.table?.searchForm?.fields;
333
+ const fieldList =
334
+ (Array.isArray(searchFields) ? searchFields : searchFields?.value) ?? [];
280
335
  return !!(
281
336
  searchFormSlot ||
282
- (computedSearchFormConfig.value?.hidden !== true &&
283
- searchForm &&
284
- searchForm.fields.value.filter(item => !item.hidden)?.length > 0)
337
+ ((computedSearchFormConfig.value as any)?.hidden !== true &&
338
+ props.table?.searchForm &&
339
+ fieldList.filter((item: any) => !item.hidden)?.length > 0)
285
340
  );
286
341
  });
287
342
 
@@ -289,13 +344,13 @@ const computedControl = computed<{
289
344
  sizeControl: boolean;
290
345
  columnControl: boolean | ColumnControlConfig<Path<RecordType>>;
291
346
  }>(() => {
292
- const mergedControl = control ?? injectProps.control;
347
+ const mergedControl = props.control ?? injectProps.value.control;
293
348
  if (typeof mergedControl === 'boolean') {
294
349
  return { sizeControl: mergedControl, columnControl: mergedControl };
295
350
  }
296
351
  const c = mergedControl as Control;
297
- const sizeControl = c.sizeControl ?? true;
298
- const columnControl = c.columnControl ?? true;
352
+ const sizeControl = c?.sizeControl ?? false;
353
+ const columnControl = c?.columnControl ?? false;
299
354
  return { sizeControl: !!sizeControl, columnControl };
300
355
  });
301
356
 
@@ -313,7 +368,7 @@ const resolvedSize = computed(() => {
313
368
  });
314
369
 
315
370
  onMounted(() => {
316
- if (immediateSearch ?? injectProps.immediateSearch) {
371
+ if (props.immediateSearch ?? injectProps.value.immediateSearch) {
317
372
  _search();
318
373
  }
319
374
  });
@@ -327,8 +382,8 @@ onMounted(() => {
327
382
  >
328
383
  <slot name="search-form">
329
384
  <SearchForm
330
- v-if="searchForm"
331
- :form="searchForm"
385
+ v-if="props.table?.searchForm"
386
+ :form="props.table.searchForm"
332
387
  v-bind="computedSearchFormConfig"
333
388
  @search="searchPage1st"
334
389
  @reset="reset"
@@ -358,7 +413,7 @@ onMounted(() => {
358
413
  :columns="
359
414
  normalizedRawColumns.filter(item => item.key !== '_index')
360
415
  "
361
- :table="table"
416
+ :table="props.table"
362
417
  :column-control="columnControlConfig"
363
418
  />
364
419
  </div>