@qin-ui/element-plus-pro 1.3.4 → 1.3.5

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 (26) hide show
  1. package/api.json +1 -1
  2. package/es/components/component-provider/types/index.d.ts +2 -2
  3. package/es/components/component-provider/types/index.d.ts.map +1 -1
  4. package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
  5. package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
  6. package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
  7. package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
  8. package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
  9. package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
  10. package/es/components/table/components/SearchForm.vue.d.ts +1 -1
  11. package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
  12. package/es/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -1
  13. package/es/element-plus-pro.css +12 -12
  14. package/es/index.js +336 -274
  15. package/es/index.js.map +1 -1
  16. package/package.json +4 -3
  17. package/src/components/component-provider/types/index.ts +2 -2
  18. package/src/components/form/components/BaseField/index.vue +11 -7
  19. package/src/components/form/components/BaseFormItem/index.vue +58 -8
  20. package/src/components/form/components/ProForm/index.vue +17 -10
  21. package/src/components/form/hooks/useGroupFieldAttrs.ts +6 -14
  22. package/src/components/table/components/ColumnControl.vue +4 -2
  23. package/src/components/table/components/ProTable.vue +24 -16
  24. package/src/components/table/components/SearchForm.vue +4 -3
  25. package/src/components/table/components/TableColumnRenderer.vue +22 -9
  26. package/tsconfig.json +23 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qin-ui/element-plus-pro",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "基于 element-plus 的二次封装组件",
5
5
  "bin": {
6
6
  "element-plus-pro": "./bin/init-ai.mjs"
@@ -23,7 +23,8 @@
23
23
  "README.md",
24
24
  "LICENSE",
25
25
  "api.json",
26
- "AI-CONTEXT.md"
26
+ "AI-CONTEXT.md",
27
+ "tsconfig.json"
27
28
  ],
28
29
  "homepage": "https://dufan3715.github.io/pro-components/",
29
30
  "repository": {
@@ -39,7 +40,7 @@
39
40
  },
40
41
  "dependencies": {
41
42
  "vue-component-type-helpers": "^3.2.5",
42
- "@qin-ui/pro-components-core": "^2.0.3"
43
+ "@qin-ui/pro-components-core": "^2.0.4"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@vue/test-utils": "^2.4.11",
@@ -7,11 +7,11 @@ import {
7
7
  } from '../../../shared/ui';
8
8
  import { ComponentName, GetComponentType } from '../../form/constants';
9
9
  import { Base, Grid, ContainerComponent } from '../../form/types';
10
- import { ProFormProps } from '../../form';
10
+ import { ProFormProps } from '../../form/components';
11
11
  import type { SearchFormProps } from '../../table/components/SearchForm.vue';
12
12
  import { AllowedComponentProps } from 'vue';
13
13
  import type { ComponentProps } from 'vue-component-type-helpers';
14
- import { ColumnControlConfig } from 'src/components/table/types';
14
+ import { ColumnControlConfig } from '../../table/types';
15
15
 
16
16
  type PP<T extends Record<string, any>> = Partial<T & AllowedComponentProps>;
17
17
 
@@ -39,7 +39,8 @@ import {
39
39
  import { cloneDeep } from '../../../../shared/core';
40
40
  import { useDisabledContext } from '../../../../shared/ui';
41
41
  import type { Field } from '../../types';
42
- import { ContainerFragment, SlotComponent } from '..';
42
+ import ContainerFragment from '../ContainerFragment/index.vue';
43
+ import SlotComponent from '../SlotComponent/index.vue';
43
44
  import { componentMap, InjectionFormSlotsKey } from '../../constants';
44
45
  import {
45
46
  INJECT_COMPONENTS,
@@ -49,6 +50,10 @@ import { useForm } from '../../hooks/useForm';
49
50
  import { getInitProps, warnUnknownComponent } from './utils';
50
51
  import { useLocale } from '../../../../locale';
51
52
 
53
+ const cloneDeepIfObject = (val: unknown) =>
54
+ // 原始值直接传递,引用值克隆快照
55
+ typeof val === 'object' && val !== null ? cloneDeep(val) : val;
56
+
52
57
  defineOptions({ name: 'BaseField', inheritAttrs: false });
53
58
 
54
59
  type Props = {
@@ -113,7 +118,7 @@ const value = computed({
113
118
  // 仅当 set 转换函数需要旧值参数时,才获取旧值上下文
114
119
  const oldVal =
115
120
  setFormatter.length > 1
116
- ? cloneDeep(form.getFormData?.(props.path))
121
+ ? cloneDeepIfObject(form.getFormData?.(props.path))
117
122
  : undefined;
118
123
  newVal = setFormatter(val, oldVal);
119
124
  }
@@ -158,7 +163,6 @@ const groupedAttrs = computed(() => {
158
163
  ) as Required<Field>;
159
164
 
160
165
  // prettier-ignore
161
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
162
166
  const { valueFormatter, modelProp, slots, componentClass, componentStyle, componentContainer, [modelPropName]: _modelVal, [`onUpdate:${modelPropName}`]: onUpdateValue, ...bindAttrs } = mergedProps as any;
163
167
 
164
168
  return {
@@ -171,9 +175,9 @@ const groupedAttrs = computed(() => {
171
175
  };
172
176
  });
173
177
 
174
- // 从 ProForm 插槽注入的 slots 集合(优先级最高)
175
- const formSlots = inject(InjectionFormSlotsKey, undefined) as
176
- | Record<string, any>
178
+ // 从 ProForm 插槽注入的 slots 集合(优先级最高;shallowRef 形态,computed 内经 .value 读取建立依赖)
179
+ const injectedFormSlots = inject(InjectionFormSlotsKey, undefined) as
180
+ | { value: Record<string, any> | undefined }
177
181
  | undefined;
178
182
 
179
183
  // 从 ProComponentProvider 注入的自定义组件映射
@@ -184,7 +188,7 @@ const customComponents = inject(INJECT_COMPONENTS, {});
184
188
  * ProForm 插槽 → 自定义组件映射 → 内置组件映射 → 原始 component 值
185
189
  */
186
190
  const is = computed(() => {
187
- const teleportSlot = props.path && formSlots?.[props.path];
191
+ const teleportSlot = props.path && injectedFormSlots?.value?.[props.path];
188
192
  if (teleportSlot) return teleportSlot;
189
193
  if (typeof props.component === 'string') {
190
194
  const resolved =
@@ -31,10 +31,28 @@ import {
31
31
  } from '../../../../shared/ui';
32
32
  import { computed, inject, toRaw, toValue } from 'vue';
33
33
  import type { Fields, Grid } from '../../types';
34
- import { BaseField, SlotComponent, ContainerFragment } from '..';
34
+ import BaseField from '../BaseField/index.vue';
35
+ import SlotComponent from '../SlotComponent/index.vue';
36
+ import ContainerFragment from '../ContainerFragment/index.vue';
35
37
  import { useGroupFieldAttrs } from '../../hooks/useGroupFieldAttrs';
36
38
  import { INJECT_CONFIG } from '../../../component-provider/constants';
37
39
 
40
+ /*
41
+ * 无 path 字段的稳定渲染 key:以字段原始对象为 key 分配自增 id,
42
+ * 字段插删/重排/过滤时保持 key 与对象绑定,不随数组位置漂移
43
+ */
44
+ let fieldKeySeed = 0;
45
+ const fieldKeyMap = new WeakMap<object, number>();
46
+ const getFieldKey = (field: unknown): number => {
47
+ const raw = toRaw(field as object);
48
+ let key = fieldKeyMap.get(raw);
49
+ if (key === undefined) {
50
+ key = ++fieldKeySeed;
51
+ fieldKeyMap.set(raw, key);
52
+ }
53
+ return key;
54
+ };
55
+
38
56
  defineOptions({ name: 'BaseFormItem', inheritAttrs: false });
39
57
 
40
58
  type Props = {
@@ -73,13 +91,41 @@ const computedGridProps = computed<GridProps>(() => {
73
91
  const formItemInstanceMap = new WeakMap<object, any>();
74
92
  const componentInstanceMap = new WeakMap<object, any>();
75
93
 
94
+ /** 已告警过的字段对象(不可写场景去重) */
95
+ const warnedFrozenFields = new WeakSet<object>();
96
+ const defineFieldAccessors = (
97
+ raw: object,
98
+ accessors: Record<string, () => any>
99
+ ) => {
100
+ try {
101
+ for (const [key, getter] of Object.entries(accessors)) {
102
+ // 不可枚举:不进入 Object.keys / spread,不随字段配置透传
103
+ Object.defineProperty(raw, key, {
104
+ value: getter,
105
+ enumerable: false,
106
+ configurable: true,
107
+ writable: true,
108
+ });
109
+ }
110
+ } catch {
111
+ if (!warnedFrozenFields.has(raw)) {
112
+ warnedFrozenFields.add(raw);
113
+ console.warn(
114
+ '[pro-components] 字段配置对象不可写(如被 Object.freeze),getFormItemRef / getComponentRef 等实例访问器挂载失败。'
115
+ );
116
+ }
117
+ }
118
+ };
119
+
76
120
  const setFormItemRef = (field: any, el: any, formItemProps?: any) => {
77
121
  if (!field || typeof field !== 'object') return;
78
122
  const raw = toRaw(field);
79
123
  if (el) {
80
124
  formItemInstanceMap.set(raw, el);
81
- raw.getFormItemRef = () => formItemInstanceMap.get(raw);
82
- raw.getFormItemComputedProps = () => formItemProps;
125
+ defineFieldAccessors(raw, {
126
+ getFormItemRef: () => formItemInstanceMap.get(raw),
127
+ getFormItemComputedProps: () => formItemProps,
128
+ });
83
129
  } else {
84
130
  formItemInstanceMap.delete(raw);
85
131
  delete raw.getFormItemRef;
@@ -92,10 +138,11 @@ const setComponentRef = (field: any, el: any) => {
92
138
  const raw = toRaw(field);
93
139
  if (el) {
94
140
  componentInstanceMap.set(raw, el);
95
- raw.getComponentRef = () =>
96
- componentInstanceMap.get(raw)?.getComponentRef();
97
- raw.getComponentComputedProps = () =>
98
- componentInstanceMap.get(raw)?.getComponentComputedProps();
141
+ defineFieldAccessors(raw, {
142
+ getComponentRef: () => componentInstanceMap.get(raw)?.getComponentRef(),
143
+ getComponentComputedProps: () =>
144
+ componentInstanceMap.get(raw)?.getComponentComputedProps(),
145
+ });
99
146
  } else {
100
147
  componentInstanceMap.delete(raw);
101
148
  delete raw.getComponentRef;
@@ -112,7 +159,10 @@ const groupedFields = useGroupFieldAttrs(validFields);
112
159
  v-bind="computedGridProps"
113
160
  style="width: 100%"
114
161
  >
115
- <template v-for="(item, index) of groupedFields" :key="item.path || index">
162
+ <template
163
+ v-for="item of groupedFields"
164
+ :key="item.path || getFieldKey(item.rawField)"
165
+ >
116
166
  <ContainerFragment
117
167
  :component="enableGrid ? ElGridItem : undefined"
118
168
  v-bind="item.gridItemProps"
@@ -29,16 +29,19 @@ import {
29
29
  FormProps as ElFormProps,
30
30
  } from '../../../../shared/ui';
31
31
  import {
32
+ computed,
32
33
  inject,
33
34
  mergeProps,
35
+ onUpdated,
34
36
  provide,
37
+ shallowRef,
35
38
  toValue,
36
- type Slot,
37
- computed,
39
+ triggerRef,
38
40
  useAttrs,
41
+ type Slot,
39
42
  } from 'vue';
40
43
  import { INJECT_CONFIG } from '../../../component-provider/constants';
41
- import { BaseFormItem } from '..';
44
+ import BaseFormItem from '../BaseFormItem/index.vue';
42
45
  import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
43
46
  import type { Grid, VModelProps, PathProps } from '../../types';
44
47
  import {
@@ -63,12 +66,12 @@ const props = withDefaults(defineProps<Props>(), {
63
66
 
64
67
  /**
65
68
  * 依赖注入中心:
66
- * 若外部未传 form 实例,则自动初始化内部表单实例;
69
+ * 未传 form 实例(含运行期置空)时惰性初始化内部表单实例;
67
70
  * 将 Form 实例注入到 Vue Context 中,供深层子组件跨层级直接访问数据和方法。
68
71
  */
69
- const fallbackForm = props.form ? undefined : useForm();
72
+ let fallbackForm: F | undefined;
70
73
  const currentForm = computed(
71
- () => props.form ?? (fallbackForm as unknown as F)
74
+ () => props.form ?? (fallbackForm ??= useForm() as unknown as F)
72
75
  );
73
76
 
74
77
  /**
@@ -93,6 +96,7 @@ const setFormRef = (el: any) => currentForm.value?.setFormRef?.(el);
93
96
  let formInstanceCounter = 0;
94
97
  const formInstanceKeyMap = new WeakMap<object, number>();
95
98
  const getFormKey = (f: object) => {
99
+ if (f == null) return 0;
96
100
  if (!formInstanceKeyMap.has(f)) {
97
101
  formInstanceKeyMap.set(f, ++formInstanceCounter);
98
102
  }
@@ -109,7 +113,6 @@ const attrs = useAttrs();
109
113
  */
110
114
  const injectAttrs = computed(() => {
111
115
  const raw = toValue(injectFormConfig);
112
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
113
116
  const { grid: _injectGrid, ...rest } = raw || {};
114
117
  return rest;
115
118
  });
@@ -125,11 +128,15 @@ type FieldSlots = Record<ExtractPath<F>, Slot<FieldSlotProps>>;
125
128
 
126
129
  const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
127
130
 
128
- /**
131
+ /*
129
132
  * 动态插槽映射中心 (Slot Teleporting):
130
- * 在 setup 同步期直接提供响应式 slots 上下文容器,供深层 BaseField 按需动态获取插槽
133
+ * slots 以 shallowRef 注入,组件每次更新后触发通知,深层 BaseField 可感知父级运行期增删的具名插槽
131
134
  */
132
- provide(InjectionFormSlotsKey, slots);
135
+ const formSlotsRef = shallowRef<Record<string, any>>(slots);
136
+ onUpdated(() => {
137
+ triggerRef(formSlotsRef);
138
+ });
139
+ provide(InjectionFormSlotsKey, formSlotsRef);
133
140
  </script>
134
141
 
135
142
  <template>
@@ -46,22 +46,11 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
46
46
 
47
47
  if (isPlainObject(field)) {
48
48
  // prettier-ignore
49
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
49
  const { path, name, fields, formItemClass, formItemStyle, formItemContainer, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
51
50
  field as any;
52
51
 
53
- const {
54
- class: injectClassName,
55
- style: injectStyle,
56
- span: injectSpan,
57
- formItemContainer: injectFormItemContainer,
58
- xs,
59
- sm,
60
- md,
61
- lg,
62
- xl,
63
- ...injectRest
64
- } = rawInjectProps;
52
+ // prettier-ignore
53
+ const { class: injectClassName, style: injectStyle, span: injectSpan, formItemContainer: injectFormItemContainer, xs, sm, md, lg, xl, ...injectRest } = rawInjectProps;
65
54
 
66
55
  // 字段显式 span 优先,否则回退注入配置的响应式断点
67
56
  const initGridItemProps =
@@ -69,7 +58,10 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
69
58
  ? { span }
70
59
  : { xs, sm, md, lg, xl, span: injectSpan };
71
60
 
72
- gridItemProps = fields ? { span: span ?? 24 } : initGridItemProps;
61
+ // 分组字段默认整行(span 兜底 24),注入断点同样生效
62
+ gridItemProps = fields
63
+ ? { ...initGridItemProps, span: span ?? 24 }
64
+ : initGridItemProps;
73
65
  formItemProps = mergeProps(
74
66
  injectRest as any,
75
67
  { class: injectClassName, style: injectStyle },
@@ -91,10 +91,12 @@ const checkedColumnKeys = computed({
91
91
  );
92
92
  },
93
93
  set(val: string[]) {
94
+ // 仅写 hidden 发生翻转的列
95
+ const checkedSet = new Set(val);
94
96
  checkedColumnsOptions.value.forEach(item => {
95
- if (item.disabled) return;
97
+ if (item.disabled || item.checked === checkedSet.has(item.value)) return;
96
98
  props.table?.setColumn(item.value as any, {
97
- hidden: !val.includes(item.value),
99
+ hidden: !checkedSet.has(item.value),
98
100
  });
99
101
  });
100
102
  },
@@ -53,7 +53,7 @@ import {
53
53
  useSlots,
54
54
  } from 'vue';
55
55
  import { INJECT_CONFIG } from '../../component-provider/constants';
56
- import { ContainerFragment } from '../../form';
56
+ import ContainerFragment from '../../form/components/ContainerFragment/index.vue';
57
57
  import type { ContainerComponent } from '../../form/types';
58
58
  import SearchForm from './SearchForm.vue';
59
59
  import type { SearchFormProps } from './SearchForm.vue';
@@ -67,12 +67,10 @@ import {
67
67
  isPlainObject,
68
68
  Path,
69
69
  } from '../../../shared/core';
70
- import {
71
- DefaultSearchFormContainer,
72
- DefaultTableContainer,
73
- SizeControl,
74
- ColumnControl,
75
- } from '.';
70
+ import DefaultSearchFormContainer from './DefaultSearchFormContainer.vue';
71
+ import DefaultTableContainer from './DefaultTableContainer.vue';
72
+ import SizeControl from './SizeControl.vue';
73
+ import ColumnControl from './ColumnControl.vue';
76
74
  import type { Columns, ColumnControlConfig } from '../types';
77
75
  import TableColumnRenderer from './TableColumnRenderer.vue';
78
76
 
@@ -201,10 +199,26 @@ const loading = defineModel<boolean>('loading', { default: false });
201
199
 
202
200
  // 递增序号做请求竞态守卫,仅最新一次请求可变更 loading
203
201
  let searchSequence = 0;
202
+ let warnedSearchConfig = false;
203
+ const warnSearchConfig = (reason: string) => {
204
+ if (warnedSearchConfig) return;
205
+ warnedSearchConfig = true;
206
+ console.warn(`[ProTable] ${reason}`);
207
+ };
204
208
  const _search = async () => {
205
- const seq = ++searchSequence;
209
+ if (props.search && attrs.onSearch) {
210
+ warnSearchConfig(
211
+ '同时配置了 :search prop 与 @search 监听,@search 监听不会被执行,请仅保留其一。'
212
+ );
213
+ }
206
214
  const searchPromise = (props.search || attrs.onSearch)?.();
207
- if (!isPromise(searchPromise)) return;
215
+ if (!isPromise(searchPromise)) {
216
+ warnSearchConfig(
217
+ 'search 未返回 Promise,本次查询不参与 loading 竞态管理(在途请求的 loading 状态保持不变)。'
218
+ );
219
+ return;
220
+ }
221
+ const seq = ++searchSequence;
208
222
  loading.value = true;
209
223
  try {
210
224
  await searchPromise;
@@ -294,13 +308,7 @@ const normalizedRawColumns = computed(() => {
294
308
 
295
309
  const computedDataSource = computed<any[]>(() => {
296
310
  const attrsData = attrsTableProps.value.data as any[] | undefined;
297
- return (
298
- props.data ??
299
- attrsData ??
300
- toValue((props.table as any)?.dataSource) ??
301
- toValue(props.table?.data) ??
302
- []
303
- );
311
+ return props.data ?? attrsData ?? toValue(props.table?.data) ?? [];
304
312
  });
305
313
 
306
314
  const computedTableProps = computed<TableProps>(() => {
@@ -63,8 +63,8 @@ import {
63
63
  type Component,
64
64
  type DefineComponent,
65
65
  } from 'vue';
66
- import ProForm from '../../form';
67
- import { type Form } from '../../form';
66
+ import ProForm from '../../form/components/ProForm/index.vue';
67
+ import { type Form } from '../../form/hooks/useForm';
68
68
  import DownOutlined from './icons/DownOutlined.vue';
69
69
  import { useLocale } from '../../../locale';
70
70
  import { useSearchFormExpand } from '../../../shared/core';
@@ -122,6 +122,7 @@ const onReset = () => {
122
122
  emit('reset');
123
123
  };
124
124
 
125
+ // 查询按钮为 native-type=submit:点击与回车统一经表单 submit 事件触发
125
126
  const onSearch = () => {
126
127
  emit('search');
127
128
  };
@@ -132,6 +133,7 @@ const onSearch = () => {
132
133
  v-bind="layoutProps"
133
134
  :form="props.form"
134
135
  class="pro-table_search-form transition"
136
+ @submit.prevent="onSearch"
135
137
  >
136
138
  <Space alignment="start" class="pro-table_search-form_button-group">
137
139
  <slot name="reset-button" :on-reset="onReset">
@@ -162,7 +164,6 @@ const onSearch = () => {
162
164
  class="pro-table_search-form_search-button"
163
165
  type="primary"
164
166
  native-type="submit"
165
- @click="onSearch"
166
167
  >
167
168
  {{ t('table.query') }}
168
169
  </Button>
@@ -5,9 +5,9 @@
5
5
  >
6
6
  import { get, normalizePathKey } from '../../../shared/core';
7
7
  import { TableColumn } from '../../../shared/ui';
8
- import { SlotComponent } from '../../form';
8
+ import SlotComponent from '../../form/components/SlotComponent/index.vue';
9
9
  import type { Column, Columns } from '../types';
10
- import { computed } from 'vue';
10
+ import { computed, toRaw } from 'vue';
11
11
 
12
12
  defineOptions({ name: 'TableColumnRenderer' });
13
13
 
@@ -18,9 +18,26 @@ const props = withDefaults(defineProps<Props>(), {
18
18
  columns: () => [],
19
19
  });
20
20
 
21
- const getColumnKey = (column: Column<D>, index: number) => {
21
+ /*
22
+ * 无 key/prop 列的稳定渲染 key:以列原始对象为 key 分配自增 id,
23
+ * 列插删/重排/过滤时保持 key 与对象绑定,不随数组位置漂移
24
+ */
25
+ let columnKeySeed = 0;
26
+ const columnKeyMap = new WeakMap<object, number>();
27
+ const getStableColumnKey = (column: unknown): number => {
28
+ const raw = toRaw(column as object);
29
+ let key = columnKeyMap.get(raw);
30
+ if (key === undefined) {
31
+ key = ++columnKeySeed;
32
+ columnKeyMap.set(raw, key);
33
+ }
34
+ return key;
35
+ };
36
+
37
+ const getColumnKey = (column: Column<D>) => {
22
38
  const rawKey = column.key ?? column.prop;
23
- if (rawKey === undefined || rawKey === null) return `__col_${index}`;
39
+ if (rawKey === undefined || rawKey === null)
40
+ return getStableColumnKey(column);
24
41
  return normalizePathKey(rawKey);
25
42
  };
26
43
 
@@ -35,7 +52,6 @@ const normalizedColumns = computed(() => {
35
52
  });
36
53
 
37
54
  const getColumnProps = (column: Column<D>) => {
38
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
39
55
  const { hidden, render, key, children, ...rest } = column;
40
56
  const prop = column.prop ? normalizePathKey(column.prop) : undefined;
41
57
  const columnKey = key ? normalizePathKey(key) : prop;
@@ -49,10 +65,7 @@ const getColumnProps = (column: Column<D>) => {
49
65
  </script>
50
66
 
51
67
  <template>
52
- <template
53
- v-for="(column, index) in normalizedColumns"
54
- :key="getColumnKey(column, index)"
55
- >
68
+ <template v-for="column in normalizedColumns" :key="getColumnKey(column)">
56
69
  <!-- 分组列(递归子列) -->
57
70
  <TableColumn
58
71
  v-if="column.children && column.children.length > 0"
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "strict": true,
8
+ "jsx": "preserve",
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "esModuleInterop": true,
12
+ "lib": ["ESNext", "ES2022", "DOM"],
13
+ "baseUrl": ".",
14
+ "types": ["element-plus/global"],
15
+ "declaration": true,
16
+ "declarationMap": true,
17
+ "skipLibCheck": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "noEmit": true
20
+ },
21
+ "include": ["src", "__tests__"],
22
+ "exclude": ["node_modules", "es"]
23
+ }