@qin-ui/element-plus-pro 1.3.3 → 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 (50) hide show
  1. package/AI-CONTEXT.md +4 -4
  2. package/README.md +3 -2
  3. package/api.json +2 -2
  4. package/es/components/component-provider/constants/index.d.ts.map +1 -1
  5. package/es/components/component-provider/types/index.d.ts +3 -2
  6. package/es/components/component-provider/types/index.d.ts.map +1 -1
  7. package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
  8. package/es/components/form/components/BaseFormItem/index.vue.d.ts +1 -1
  9. package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
  10. package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
  11. package/es/components/form/components/SlotComponent/index.vue.d.ts.map +1 -1
  12. package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
  13. package/es/components/form/types/index.d.ts +2 -2
  14. package/es/components/form/types/index.d.ts.map +1 -1
  15. package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
  16. package/es/components/table/components/ProTable.vue.d.ts +30 -4
  17. package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
  18. package/es/components/table/components/SearchForm.vue.d.ts +1 -1
  19. package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
  20. package/es/components/table/components/SizeControl.vue.d.ts +2 -2
  21. package/es/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -1
  22. package/es/components/table/index.d.ts +4 -28
  23. package/es/components/table/index.d.ts.map +1 -1
  24. package/es/components/table/useTable.d.ts +1 -1
  25. package/es/element-plus-pro.css +12 -12
  26. package/es/index.js +410 -331
  27. package/es/index.js.map +1 -1
  28. package/es/shared/core/index.d.ts +1 -2
  29. package/es/shared/core/index.d.ts.map +1 -1
  30. package/es/shared/ui/index.d.ts +2 -2
  31. package/es/shared/ui/index.d.ts.map +1 -1
  32. package/package.json +5 -4
  33. package/src/components/component-provider/constants/index.ts +12 -0
  34. package/src/components/component-provider/types/index.ts +8 -2
  35. package/src/components/form/components/BaseField/index.vue +25 -9
  36. package/src/components/form/components/BaseField/utils/index.ts +1 -1
  37. package/src/components/form/components/BaseFormItem/index.vue +62 -8
  38. package/src/components/form/components/ProForm/index.vue +24 -12
  39. package/src/components/form/components/SlotComponent/index.vue +4 -3
  40. package/src/components/form/hooks/useGroupFieldAttrs.ts +8 -15
  41. package/src/components/form/types/index.ts +2 -8
  42. package/src/components/table/components/ColumnControl.vue +40 -37
  43. package/src/components/table/components/ProTable.vue +74 -18
  44. package/src/components/table/components/SearchForm.vue +5 -4
  45. package/src/components/table/components/TableColumnRenderer.vue +22 -9
  46. package/src/components/table/index.ts +10 -30
  47. package/src/components/table/useTable.ts +1 -1
  48. package/src/shared/core/index.ts +1 -2
  49. package/src/shared/ui/index.ts +2 -0
  50. package/tsconfig.json +23 -0
@@ -37,6 +37,7 @@ import {
37
37
  type PaginationProps,
38
38
  Table as ElTable,
39
39
  type TableProps,
40
+ type TableEmits,
40
41
  tableProps,
41
42
  paginationProps,
42
43
  } from '../../../shared/ui';
@@ -52,7 +53,7 @@ import {
52
53
  useSlots,
53
54
  } from 'vue';
54
55
  import { INJECT_CONFIG } from '../../component-provider/constants';
55
- import { ContainerFragment } from '../../form';
56
+ import ContainerFragment from '../../form/components/ContainerFragment/index.vue';
56
57
  import type { ContainerComponent } from '../../form/types';
57
58
  import SearchForm from './SearchForm.vue';
58
59
  import type { SearchFormProps } from './SearchForm.vue';
@@ -66,12 +67,10 @@ import {
66
67
  isPlainObject,
67
68
  Path,
68
69
  } from '../../../shared/core';
69
- import {
70
- DefaultSearchFormContainer,
71
- DefaultTableContainer,
72
- SizeControl,
73
- ColumnControl,
74
- } 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';
75
74
  import type { Columns, ColumnControlConfig } from '../types';
76
75
  import TableColumnRenderer from './TableColumnRenderer.vue';
77
76
 
@@ -119,7 +118,11 @@ type Props = {
119
118
  columns?: Columns<RecordType>;
120
119
  data?: RecordType[];
121
120
  pagination?: false | Partial<PaginationProps>;
122
- } & Omit<TableProps, 'data'>;
121
+ } & /* @vue-ignore */ Omit<TableProps, 'data'>;
122
+
123
+ type Emits = {} & /* @vue-ignore */ TableEmits<RecordType>;
124
+
125
+ defineEmits<Emits>();
123
126
 
124
127
  const props = withDefaults(defineProps<Props>(), {
125
128
  table: undefined,
@@ -142,8 +145,36 @@ const injectProps = computed(() => toValue(injectPropsRaw) || {});
142
145
 
143
146
  const attrs = useAttrs() as Record<string, any>;
144
147
 
148
+ const PRO_TABLE_EXCLUDED_ATTR_KEYS = new Set([
149
+ 'class',
150
+ 'style',
151
+ 'onSearch',
152
+ 'onChange',
153
+ 'onUpdate:size',
154
+ 'onUpdate:loading',
155
+ 'data',
156
+ 'columns',
157
+ 'size',
158
+ 'pagination',
159
+ ]);
160
+
161
+ const PRO_TABLE_PROVIDER_KEYS = new Set([
162
+ 'pagination',
163
+ 'searchFormConfig',
164
+ 'control',
165
+ 'immediateSearch',
166
+ 'addIndexColumn',
167
+ 'tableContainer',
168
+ ]);
169
+
145
170
  const injectTableAttrs = computed(() => {
146
- return pick(injectProps.value, tableProperties) as Partial<TableProps>;
171
+ const tableConfig: Record<string, any> = {};
172
+ for (const [key, value] of Object.entries(injectProps.value)) {
173
+ if (!PRO_TABLE_PROVIDER_KEYS.has(key)) {
174
+ tableConfig[key] = value;
175
+ }
176
+ }
177
+ return tableConfig as Partial<TableProps>;
147
178
  });
148
179
 
149
180
  const attrsTableProps = computed(() => {
@@ -153,15 +184,41 @@ const attrsTableProps = computed(() => {
153
184
  ) as Partial<TableProps>;
154
185
  });
155
186
 
187
+ const restTableAttrs = computed(() => {
188
+ const rest: Record<string, any> = {};
189
+ for (const [key, value] of Object.entries(attrs)) {
190
+ if (!PRO_TABLE_EXCLUDED_ATTR_KEYS.has(key)) {
191
+ rest[key] = value;
192
+ }
193
+ }
194
+ return rest;
195
+ });
196
+
156
197
  const size = defineModel<TableProps['size']>('size');
157
198
  const loading = defineModel<boolean>('loading', { default: false });
158
199
 
159
200
  // 递增序号做请求竞态守卫,仅最新一次请求可变更 loading
160
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
+ };
161
208
  const _search = async () => {
162
- const seq = ++searchSequence;
209
+ if (props.search && attrs.onSearch) {
210
+ warnSearchConfig(
211
+ '同时配置了 :search prop 与 @search 监听,@search 监听不会被执行,请仅保留其一。'
212
+ );
213
+ }
163
214
  const searchPromise = (props.search || attrs.onSearch)?.();
164
- if (!isPromise(searchPromise)) return;
215
+ if (!isPromise(searchPromise)) {
216
+ warnSearchConfig(
217
+ 'search 未返回 Promise,本次查询不参与 loading 竞态管理(在途请求的 loading 状态保持不变)。'
218
+ );
219
+ return;
220
+ }
221
+ const seq = ++searchSequence;
165
222
  loading.value = true;
166
223
  try {
167
224
  await searchPromise;
@@ -251,21 +308,19 @@ const normalizedRawColumns = computed(() => {
251
308
 
252
309
  const computedDataSource = computed<any[]>(() => {
253
310
  const attrsData = attrsTableProps.value.data as any[] | undefined;
254
- return (
255
- props.data ??
256
- attrsData ??
257
- toValue((props.table as any)?.dataSource) ??
258
- toValue(props.table?.data) ??
259
- []
260
- );
311
+ return props.data ?? attrsData ?? toValue(props.table?.data) ?? [];
261
312
  });
262
313
 
263
314
  const computedTableProps = computed<TableProps>(() => {
264
315
  const merged = {
265
316
  ...injectTableAttrs.value,
317
+ ...restTableAttrs.value,
266
318
  ...attrsTableProps.value,
267
319
  } as TableProps;
268
320
  delete (merged as any).data;
321
+ delete (merged as any).columns;
322
+ delete (merged as any).size;
323
+ delete (merged as any).pagination;
269
324
  return merged;
270
325
  });
271
326
 
@@ -313,6 +368,7 @@ const onCurrentChange = (current: number) => {
313
368
  _search();
314
369
  };
315
370
 
371
+ // 改变 pageSize 时重置回第 1 页并重新查询
316
372
  const onSizeChange = (pageSize: number) => {
317
373
  props.table?.setPageParam?.({ pageSize, current: 1 });
318
374
  _search();
@@ -46,7 +46,7 @@ export type SearchFormProps = {
46
46
  *
47
47
  * 1. 内部渲染 ProForm,使用 table.searchForm 管理搜索条件
48
48
  * 2. 支持 grid(网格)和 inline(行内)两种布局
49
- * 3. 网格布局下支持展开/折叠:通过 IntersectionObserver 自动检测哪些字段溢出
49
+ * 3. 网格布局下支持展开/折叠(溢出检测与初值自动展开见下方机制说明)
50
50
  * 4. 提供搜索、重置、展开/折叠按钮(支持自定义)
51
51
  *
52
52
  * ## 展开/折叠机制
@@ -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"
@@ -1,38 +1,18 @@
1
- import type { ComponentPublicInstance } from 'vue';
2
- import type { PaginationProps, TableProps } from '../../shared/ui';
3
- import type { ContainerComponent } from '../form/types';
4
- import type { Columns } from './types';
5
- import type { SearchFormProps } from './components/SearchForm.vue';
6
- import type { Table } from './useTable';
1
+ import type {
2
+ ComponentExposed,
3
+ ComponentProps,
4
+ } from 'vue-component-type-helpers';
7
5
  import ProTable from './components/ProTable.vue';
8
6
 
9
- type Control = { sizeControl: boolean; columnControl: boolean };
10
- type SearchFormConfig = Omit<SearchFormProps, 'form'> & {
11
- hidden?: boolean;
12
- container?: ContainerComponent | false;
13
- };
7
+ // 仅导出组件 Props 与实例类型,不导出裸组件
8
+ export type ProTableProps = ComponentProps<typeof ProTable>;
9
+ export type ProTableInstance = ComponentExposed<typeof ProTable>;
14
10
 
15
- type ReturnTableRecord<V> = V extends Table<any, infer R> ? R : any;
16
-
17
- // instance
18
- export type ProTableProps<T extends Table<any> = Table> = {
19
- table?: T;
20
- search?: () => Promise<unknown>;
21
- addIndexColumn?: boolean;
22
- immediateSearch?: boolean;
23
- control?: boolean | Partial<Control>;
24
- searchFormConfig?: SearchFormConfig;
25
- tableContainer?: ContainerComponent | false;
26
- columns?: Columns<ReturnTableRecord<T>>;
27
- data?: ReturnTableRecord<T>[];
28
- pagination?: false | Partial<PaginationProps>;
29
- } & Omit<TableProps, 'data'>;
30
- export type ProTableInstance = ComponentPublicInstance;
31
-
32
- // types
11
+ // 仅导出核心列配置类型
33
12
  export { type Column, type Columns } from './types';
13
+ export { type TableEmits } from '../../shared/ui';
34
14
 
35
- // hooks
15
+ // 仅导出核心表格 Hook 与类型
36
16
  export { useTable, type Table } from './useTable';
37
17
 
38
18
  export default ProTable;
@@ -31,7 +31,7 @@ import type { Ref } from 'vue';
31
31
  * @property appendColumn - 在指定列后追加新列,传 undefined 在末尾追加
32
32
  * @property prependColumn - 在指定列前插入新列,传 undefined 在开头插入
33
33
  * @property setPageParam - 设置分页参数,支持部分更新和函数式更新
34
- * @property resetQueryParams - 重置分页到第一页并恢复搜索条件到初始值
34
+ * @property resetQueryParams - 恢复分页与搜索条件到初始化时的快照(含自定义分页键,非固定回到第一页)
35
35
  *
36
36
  * @example
37
37
  * ```ts
@@ -1,6 +1,5 @@
1
1
  /**
2
- * 统一 re-export @qin-ui/pro-components-core,包内所有文件通过此文件引入 core 内容,
3
- * 避免直接散列依赖 @qin-ui/pro-components-core,便于将来统一维护。
2
+ * 统一 re-export @qin-ui/pro-components-core,包内所有文件通过此文件引入 core 内容。
4
3
  */
5
4
  export {
6
5
  // hooks
@@ -32,6 +32,7 @@ import {
32
32
  type FormProps,
33
33
  type FormItemProps,
34
34
  type TableProps as _TableProps,
35
+ type TableEmits,
35
36
  type TableColumnCtx as ColumnType,
36
37
  type PaginationProps,
37
38
  type RowProps as GridProps,
@@ -75,6 +76,7 @@ export {
75
76
  type GridProps,
76
77
  type GridItemProps,
77
78
  type ColumnType,
79
+ type TableEmits,
78
80
  };
79
81
 
80
82
  export type TableProps = _TableProps<any>;
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
+ }