@qin-ui/antd-vue-pro 2.2.4 → 2.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 (35) hide show
  1. package/AI-CONTEXT.md +22 -11
  2. package/api.json +7 -1
  3. package/es/antd-vue-pro.css +13 -13
  4. package/es/components/component-provider/components/index.vue.d.ts +8 -0
  5. package/es/components/component-provider/components/index.vue.d.ts.map +1 -1
  6. package/es/components/component-provider/constants/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/BaseField/utils/index.d.ts +3 -1
  9. package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -1
  10. package/es/components/table/components/ColumnControl.vue.d.ts +2 -1
  11. package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
  12. package/es/components/table/components/ProTable.vue.d.ts +3 -2
  13. package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
  14. package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
  15. package/es/components/table/components/SizeControl.vue.d.ts.map +1 -1
  16. package/es/components/table/types/index.d.ts +25 -0
  17. package/es/components/table/types/index.d.ts.map +1 -1
  18. package/es/index.d.ts +1 -0
  19. package/es/index.d.ts.map +1 -1
  20. package/es/index.js +200 -103
  21. package/es/index.js.map +1 -1
  22. package/es/locale/index.d.ts +28 -0
  23. package/es/locale/index.d.ts.map +1 -0
  24. package/package.json +2 -2
  25. package/src/components/component-provider/components/index.vue +16 -1
  26. package/src/components/component-provider/constants/index.ts +4 -7
  27. package/src/components/form/components/BaseField/index.vue +10 -4
  28. package/src/components/form/components/BaseField/utils/index.ts +33 -3
  29. package/src/components/table/components/ColumnControl.vue +91 -38
  30. package/src/components/table/components/ProTable.vue +47 -13
  31. package/src/components/table/components/SearchForm.vue +6 -3
  32. package/src/components/table/components/SizeControl.vue +9 -5
  33. package/src/components/table/types/index.ts +26 -0
  34. package/src/index.ts +1 -0
  35. package/src/locale/index.ts +71 -0
@@ -9,6 +9,7 @@
9
9
  *
10
10
  * @param {ComponentVars} [componentVars] - 组件全局配置变量
11
11
  * @param {Record<ComponentName, any>} [componentMap] - 自定义组件映射,用于替换或扩展内置组件
12
+ * @param {Partial<ProLocale>} [locale] - 内置文案覆盖,可覆盖任意 locale 文案(如查询/重置/序号/占位符等)
12
13
  *
13
14
  * @slot default - 需要应用全局配置的子组件内容
14
15
  *
@@ -29,9 +30,10 @@
29
30
  */
30
31
  import { ComponentVars } from '../types';
31
32
  import { INJECT_COMPONENTS, ensureInjectConfig } from '../constants';
32
- import { provide } from 'vue';
33
+ import { computed, provide } from 'vue';
33
34
  import { getObject } from '../../../shared/core';
34
35
  import { ComponentName } from '../../form/constants';
36
+ import { LOCALE_INJECT_KEY, type ProLocale } from '../../../locale';
35
37
 
36
38
  type ComponentMap = Partial<Record<ComponentName, any>>;
37
39
 
@@ -53,6 +55,13 @@ type Props = {
53
55
  * @example { 'input': MyCustomInput, 'select': MyCustomSelect }
54
56
  */
55
57
  componentMap?: ComponentMap;
58
+ /**
59
+ * @description 内置文案覆盖
60
+ * 可覆盖任意 locale 文案(如查询/重置/序号/占位符等),
61
+ * 未覆盖的文案默认跟随宿主组件库语言自动切换
62
+ * @example { 'table.query': '搜索' }
63
+ */
64
+ locale?: Partial<ProLocale>;
56
65
  };
57
66
 
58
67
  const props = defineProps<Props>();
@@ -74,6 +83,12 @@ if (props.componentVars) {
74
83
  if (props.componentMap) {
75
84
  provide(INJECT_COMPONENTS, props.componentMap);
76
85
  }
86
+
87
+ // 注入内置文案覆盖,useLocale 在解析文案时会优先使用
88
+ provide(
89
+ LOCALE_INJECT_KEY,
90
+ computed(() => props.locale)
91
+ );
77
92
  </script>
78
93
 
79
94
  <template>
@@ -23,7 +23,6 @@ export const INJECT_CONFIG: {
23
23
  injectionKey: Symbol(''),
24
24
  default: {
25
25
  pagination: {
26
- showTotal: total => `共 ${total} 条`,
27
26
  showSizeChanger: true,
28
27
  pageSizeOptions: ['10', '20', '30', '40', '50', '100'],
29
28
  showQuickJumper: true,
@@ -47,7 +46,7 @@ export const INJECT_CONFIG: {
47
46
  // field
48
47
  input: {
49
48
  injectionKey: Symbol(''),
50
- default: { maxlength: 100, allowClear: true, placeholder: '请输入' },
49
+ default: { maxlength: 100, allowClear: true },
51
50
  },
52
51
  textarea: {
53
52
  injectionKey: Symbol(''),
@@ -56,12 +55,11 @@ export const INJECT_CONFIG: {
56
55
  autoSize: { minRows: 3, maxRows: 6 },
57
56
  showCount: true,
58
57
  allowClear: true,
59
- placeholder: '请输入',
60
58
  },
61
59
  },
62
60
  'input-password': {
63
61
  injectionKey: Symbol(''),
64
- default: { maxlength: 100, allowClear: true, placeholder: '请输入' },
62
+ default: { maxlength: 100, allowClear: true },
65
63
  },
66
64
  'input-search': {
67
65
  injectionKey: Symbol(''),
@@ -73,17 +71,16 @@ export const INJECT_CONFIG: {
73
71
  max: 10 ** 15 - 1,
74
72
  min: -(10 ** 15 + 1),
75
73
  controls: false,
76
- placeholder: '请输入',
77
74
  style: { width: '100%' },
78
75
  },
79
76
  },
80
77
  select: {
81
78
  injectionKey: Symbol(''),
82
- default: { allowClear: true, placeholder: '请选择', getPopupContainer },
79
+ default: { allowClear: true, getPopupContainer },
83
80
  },
84
81
  cascader: {
85
82
  injectionKey: Symbol(''),
86
- default: { allowClear: true, placeholder: '请选择', getPopupContainer },
83
+ default: { allowClear: true, getPopupContainer },
87
84
  },
88
85
  'date-picker': {
89
86
  injectionKey: Symbol(''),
@@ -46,6 +46,7 @@ import { componentMap, TeleportComponentNamePrefix } from '../../constants';
46
46
  import { INJECT_COMPONENTS } from '../../../component-provider/constants';
47
47
  import { useForm } from '../../hooks/useForm';
48
48
  import { getInitProps } from './utils';
49
+ import { useLocale } from '../../../../locale';
49
50
 
50
51
  defineOptions({ name: 'BaseField', inheritAttrs: false });
51
52
 
@@ -55,6 +56,8 @@ type Props = {
55
56
  };
56
57
  const { component = undefined, path = '' } = defineProps<Props>();
57
58
 
59
+ const { t } = useLocale();
60
+
58
61
  const form = useForm(false);
59
62
  const { getFormData, setFormData } = form;
60
63
 
@@ -106,10 +109,13 @@ const parentDisabled = useInjectDisabled();
106
109
  * 属性分为三类:绑定属性(v-model)、组件属性(传递给组件)、插槽(动态渲染)
107
110
  */
108
111
  const groupedAttrs = computed(() => {
109
- const initProps = getInitProps({
110
- component: component,
111
- picker: attrs.picker,
112
- } as Field);
112
+ const initProps = getInitProps(
113
+ {
114
+ component: component,
115
+ picker: attrs.picker,
116
+ } as Field,
117
+ t
118
+ );
113
119
  const mergedProps = mergeProps(
114
120
  initProps,
115
121
  attrs,
@@ -2,15 +2,45 @@ import { Field } from '../../../types';
2
2
  import { getInjectConfig } from '../../../../component-provider/constants';
3
3
  import { inject } from 'vue';
4
4
 
5
- export const getInitProps = (field: Field): Record<string, any> => {
5
+ type LocaleT = (
6
+ key: string,
7
+ params?: Record<string, string | number>
8
+ ) => string;
9
+
10
+ /** 组件的占位符对应的 locale key */
11
+ const PLACEHOLDER_KEYS: Record<string, string> = {
12
+ input: 'input.placeholder',
13
+ textarea: 'input.placeholder',
14
+ 'input-password': 'input.placeholder',
15
+ 'input-number': 'input.placeholder',
16
+ select: 'select.placeholder',
17
+ cascader: 'select.placeholder',
18
+ };
19
+
20
+ /**
21
+ * 根据组件 key 兜底生成 locale 占位符
22
+ *
23
+ * @description 用户通过 componentVars 或字段属性显式设置了占位符时不再兜底。
24
+ */
25
+ const getPlaceholderProps = (
26
+ key: string,
27
+ injectProps: Record<string, any>,
28
+ t: LocaleT
29
+ ): Record<string, string> => {
30
+ const placeholderKey = PLACEHOLDER_KEYS[key];
31
+ if (!placeholderKey || injectProps.placeholder !== undefined) return {};
32
+ return { placeholder: t(placeholderKey) };
33
+ };
34
+
35
+ export const getInitProps = (field: Field, t: LocaleT): Record<string, any> => {
6
36
  const { component } = field as any;
7
37
  const picker = component === 'date-picker' ? '' : (field as any).picker;
8
38
  if (typeof component === 'string') {
9
39
  const k = [component, picker].filter(Boolean).join('.');
10
40
  const config = getInjectConfig(k);
11
- if (!config) return {};
41
+ if (!config) return getPlaceholderProps(k, {}, t);
12
42
  const injectProps = inject(config.injectionKey, config.default);
13
- return injectProps;
43
+ return { ...injectProps, ...getPlaceholderProps(k, injectProps, t) };
14
44
  }
15
45
  return {};
16
46
  };
@@ -7,16 +7,24 @@ import {
7
7
  MenuItem,
8
8
  Checkbox,
9
9
  } from '../../../shared/ui';
10
- import { computed, ref, watch } from 'vue';
11
- import type { Columns } from '../types';
10
+ import { computed, onMounted, ref, watch } from 'vue';
11
+ import type { Columns, ColumnControlConfig } from '../types';
12
12
  import { type Table } from '../hooks/useTable';
13
13
  import FilterOutlined from './icons/FilterOutlined.vue';
14
+ import { useLocale } from '../../../locale';
14
15
 
15
16
  type Props = {
16
17
  columns: Columns;
17
18
  table?: Table;
19
+ columnControl?: ColumnControlConfig;
18
20
  };
19
- const { columns, table = undefined } = defineProps<Props>();
21
+ const {
22
+ columns,
23
+ table = undefined,
24
+ columnControl = undefined,
25
+ } = defineProps<Props>();
26
+
27
+ const { t } = useLocale();
20
28
 
21
29
  const open = ref(false);
22
30
 
@@ -24,29 +32,46 @@ const checkAll = ref(false);
24
32
  const indeterminate = ref(false);
25
33
 
26
34
  const checkedColumnsOptions = computed<
27
- Array<{ label: string; value: string; checked: boolean }>
35
+ Array<{
36
+ label: string;
37
+ value: string;
38
+ checked: boolean;
39
+ disabled: boolean;
40
+ }>
28
41
  >(() =>
29
- columns.flatMap(item =>
30
- item.key && item.title
31
- ? {
32
- label: item.title as string,
33
- value: item.key,
34
- checked: !item.hidden,
35
- }
36
- : []
37
- )
42
+ columns.flatMap(item => {
43
+ if (!item.key || !item.title) return [];
44
+ const key = item.key as string;
45
+ if (columnControl?.excludedKeys?.some(ek => String(ek) === key)) return [];
46
+ const disabled =
47
+ columnControl?.disabledKeys?.some(dk => String(dk) === key) ?? false;
48
+ // labels 优先;title 为字符串时直接使用;vnode 等其他类型降级为 key,避免渲染异常
49
+ const label =
50
+ (columnControl?.labels?.[key] as string) ??
51
+ (typeof item.title === 'string' ? item.title : key);
52
+ return {
53
+ label,
54
+ value: key,
55
+ checked: !item.hidden || disabled,
56
+ disabled,
57
+ };
58
+ })
59
+ );
60
+
61
+ /** 可被用户勾选/取消的列(非 disabled) */
62
+ const controllableOptions = computed(() =>
63
+ checkedColumnsOptions.value.filter(item => !item.disabled)
38
64
  );
39
65
 
40
66
  const checkedColumnKeys = computed({
41
67
  get() {
42
- return (
43
- checkedColumnsOptions.value.flatMap(item =>
44
- item.checked ? item.value : []
45
- ) || []
68
+ return checkedColumnsOptions.value.flatMap(item =>
69
+ item.checked ? item.value : []
46
70
  );
47
71
  },
48
72
  set(val) {
49
- checkedColumnsOptions.value?.forEach(item => {
73
+ checkedColumnsOptions.value.forEach(item => {
74
+ if (item.disabled) return;
50
75
  table?.setColumn?.(item.value, {
51
76
  hidden: !val.includes(item.value),
52
77
  });
@@ -54,14 +79,17 @@ const checkedColumnKeys = computed({
54
79
  },
55
80
  });
56
81
 
57
- const onCheckAllChange = (e: any) => {
58
- checkedColumnKeys.value = e.target.checked
59
- ? checkedColumnsOptions.value.map(item => item.value)
60
- : [];
82
+ const onCheckAllChange = () => {
83
+ // 整行点击触发:当前全选则取消全选,否则全选可控列
84
+ checkedColumnKeys.value = checkAll.value
85
+ ? []
86
+ : controllableOptions.value.map(item => item.value);
61
87
  indeterminate.value = false;
62
88
  };
63
89
 
64
90
  const checkColumnsMenuItemClick = (val: string) => {
91
+ const item = checkedColumnsOptions.value.find(o => o.value === val);
92
+ if (item?.disabled) return;
65
93
  if (checkedColumnKeys.value.includes(val)) {
66
94
  checkedColumnKeys.value = checkedColumnKeys.value.filter(
67
95
  item => item !== val
@@ -73,13 +101,36 @@ const checkColumnsMenuItemClick = (val: string) => {
73
101
 
74
102
  watch(
75
103
  checkedColumnKeys,
76
- val => {
104
+ () => {
105
+ const controllableChecked = controllableOptions.value.filter(
106
+ item => item.checked
107
+ );
77
108
  indeterminate.value =
78
- !!val.length && val.length < checkedColumnsOptions.value.length;
79
- checkAll.value = val.length === checkedColumnsOptions.value.length;
109
+ !!controllableChecked.length &&
110
+ controllableChecked.length < controllableOptions.value.length;
111
+ checkAll.value =
112
+ controllableChecked.length === controllableOptions.value.length &&
113
+ controllableOptions.value.length > 0;
80
114
  },
81
115
  { immediate: true }
82
116
  );
117
+
118
+ // 强制 disabledKeys 列始终展示
119
+ onMounted(() => {
120
+ columnControl?.disabledKeys?.forEach(key => {
121
+ table?.setColumn?.(String(key), { hidden: false });
122
+ });
123
+ });
124
+
125
+ watch(
126
+ () => columnControl?.disabledKeys,
127
+ keys => {
128
+ keys?.forEach(key => {
129
+ table?.setColumn?.(String(key), { hidden: false });
130
+ });
131
+ },
132
+ { deep: true }
133
+ );
83
134
  </script>
84
135
 
85
136
  <template>
@@ -92,25 +143,27 @@ watch(
92
143
  style="min-width: 100px; max-height: 500px; overflow-y: scroll"
93
144
  :selectable="false"
94
145
  >
95
- <MenuItem :key="0">
96
- <div>
97
- <Checkbox
98
- :checked="checkAll"
99
- :indeterminate="indeterminate"
100
- @change="onCheckAllChange"
101
- >
102
- 全选
103
- </Checkbox>
104
- </div>
146
+ <MenuItem
147
+ v-if="!columnControl?.disableCheckAll"
148
+ :key="0"
149
+ @click="onCheckAllChange"
150
+ >
151
+ <Checkbox
152
+ :checked="checkAll"
153
+ :indeterminate="indeterminate"
154
+ style="width: 100%"
155
+ >
156
+ {{ t('table.selectAll') }}
157
+ </Checkbox>
105
158
  </MenuItem>
106
- <MenuDivider />
159
+ <MenuDivider v-if="!columnControl?.disableCheckAll" />
107
160
  <MenuItem
108
161
  v-for="item of checkedColumnsOptions"
109
162
  :key="item.value"
110
163
  @click="checkColumnsMenuItemClick(item.value)"
111
164
  >
112
- <Checkbox :checked="checkedColumnKeys.includes(item.value)">
113
- <div @click.stop>{{ item.label }}</div>
165
+ <Checkbox :checked="item.checked" :disabled="item.disabled">
166
+ <div>{{ item.label }}</div>
114
167
  </Checkbox>
115
168
  </MenuItem>
116
169
  </Menu>
@@ -53,7 +53,13 @@ import type { ContainerComponent } from '../../form/types';
53
53
  import SearchForm from './SearchForm.vue';
54
54
  import { SearchFormProps } from './SearchForm.vue';
55
55
  import type { Table } from '../hooks/useTable';
56
- import { camelizeProperties, getObject, pick } from '../../../shared/core';
56
+ import { useLocale } from '../../../locale';
57
+ import {
58
+ camelizeProperties,
59
+ getObject,
60
+ pick,
61
+ Path,
62
+ } from '../../../shared/core';
57
63
  import { ComponentSlots } from 'vue-component-type-helpers';
58
64
  import {
59
65
  DefaultSearchFormContainer,
@@ -61,11 +67,14 @@ import {
61
67
  SizeControl,
62
68
  ColumnControl,
63
69
  } from '.';
64
- import { Column, Columns } from '../types';
70
+ import { Column, Columns, ColumnControlConfig } from '../types';
65
71
 
66
72
  defineOptions({ name: 'ProTable', inheritAttrs: false });
67
73
 
68
- type Control = { sizeControl: boolean; columnControl: boolean };
74
+ type Control = {
75
+ sizeControl: boolean;
76
+ columnControl: boolean | ColumnControlConfig<Path<RecordType>>;
77
+ };
69
78
 
70
79
  type SearchFormConfig = Omit<SearchFormProps, 'form'> & {
71
80
  // 是否隐藏搜索表单
@@ -101,6 +110,8 @@ const {
101
110
  searchFormConfig = undefined,
102
111
  } = defineProps<Props>();
103
112
 
113
+ const { t } = useLocale();
114
+
104
115
  const config = INJECT_CONFIG['pro-table'];
105
116
  const injectProps = inject(config.injectionKey, config.default);
106
117
 
@@ -197,17 +208,19 @@ const computedTableContainer = computed(() => {
197
208
  type ReturnGenericParameterTypes<V> = V extends Table<any, infer R> ? R : never;
198
209
  type RecordType = ReturnGenericParameterTypes<T>;
199
210
 
200
- const indexColumn: ColumnType = {
201
- title: '序号',
211
+ const indexColumn = (): ColumnType => ({
212
+ title: t('table.index'),
202
213
  dataIndex: '_index',
203
214
  width: 80,
204
215
  customRender: ({ index }) => index + 1,
205
- };
216
+ });
206
217
 
207
218
  const computedColumns = computed(
208
219
  () =>
209
220
  [
210
- ...((addIndexColumn ?? injectProps.addIndexColumn) ? [indexColumn] : []),
221
+ ...((addIndexColumn ?? injectProps.addIndexColumn)
222
+ ? [indexColumn()]
223
+ : []),
211
224
  ...(tableAttrs.value.columns ?? columns?.value ?? ([] as any)).flatMap(
212
225
  (item: ColumnType<RecordType>, index: number) => {
213
226
  if (item.key) return [{ ...item, key: item.key }];
@@ -237,6 +250,12 @@ const computedPagination = computed<PaginationProps | false>(() => {
237
250
  ...injectAttrs.pagination,
238
251
  ...pageParam,
239
252
  ...tableAttrs.value.pagination,
253
+ showTotal:
254
+ tableAttrs.value.pagination?.showTotal ??
255
+ (injectAttrs.pagination === false
256
+ ? undefined
257
+ : injectAttrs.pagination?.showTotal) ??
258
+ ((total: number) => t('pagination.total', { total })),
240
259
  };
241
260
  });
242
261
 
@@ -255,12 +274,23 @@ const showSearch = computed(() => {
255
274
  );
256
275
  });
257
276
 
258
- const computedControl = computed<Control>(() => {
277
+ const computedControl = computed<{
278
+ sizeControl: boolean;
279
+ columnControl: boolean | ColumnControlConfig<Path<RecordType>>;
280
+ }>(() => {
259
281
  const mergedControl = control ?? injectProps.control;
260
- const sizeControl = (mergedControl as Control)?.sizeControl ?? mergedControl;
261
- const columnControl =
262
- (mergedControl as Control)?.columnControl ?? mergedControl;
263
- return { sizeControl, columnControl };
282
+ if (typeof mergedControl === 'boolean') {
283
+ return { sizeControl: mergedControl, columnControl: mergedControl };
284
+ }
285
+ const c = mergedControl as Control;
286
+ const sizeControl = c.sizeControl ?? true;
287
+ const columnControl = c.columnControl ?? true;
288
+ return { sizeControl: !!sizeControl, columnControl };
289
+ });
290
+
291
+ const columnControlConfig = computed(() => {
292
+ const cc = computedControl.value.columnControl;
293
+ return typeof cc === 'object' ? cc : undefined;
264
294
  });
265
295
 
266
296
  onMounted(() => {
@@ -305,7 +335,11 @@ onMounted(() => {
305
335
  v-if="computedControl.columnControl"
306
336
  class="pro-table_header_column-control"
307
337
  >
308
- <ColumnControl :columns="computedColumns as any" :table="table" />
338
+ <ColumnControl
339
+ :columns="computedColumns as any"
340
+ :table="table"
341
+ :column-control="columnControlConfig"
342
+ />
309
343
  </div>
310
344
  </div>
311
345
 
@@ -69,9 +69,12 @@ import {
69
69
  import ProForm from '../../form';
70
70
  import { type Form } from '../../form';
71
71
  import DownOutlined from './icons/DownOutlined.vue';
72
+ import { useLocale } from '../../../locale';
72
73
 
73
74
  defineOptions({ name: 'SearchForm' });
74
75
 
76
+ const { t } = useLocale();
77
+
75
78
  const {
76
79
  layout = 'grid',
77
80
  expand = true,
@@ -230,7 +233,7 @@ const onSearch = () => {
230
233
  v-else
231
234
  class="pro-table_search-form_reset-button"
232
235
  @click="onReset"
233
- >重置</Button
236
+ >{{ t('table.reset') }}</Button
234
237
  >
235
238
  </template>
236
239
  </slot>
@@ -243,7 +246,7 @@ const onSearch = () => {
243
246
  type="primary"
244
247
  html-type="submit"
245
248
  @click="onSearch"
246
- >查询
249
+ >{{ t('table.query') }}
247
250
  </Button>
248
251
  </template>
249
252
  </slot>
@@ -261,7 +264,7 @@ const onSearch = () => {
261
264
  class="pro-table_search-form_expand-toggle-button"
262
265
  @click="changeExpandStatus"
263
266
  >
264
- {{ expandStatus ? '收起' : '展开' }}
267
+ {{ expandStatus ? t('table.collapse') : t('table.expand') }}
265
268
  <DownOutlined
266
269
  class="transition"
267
270
  style="margin-left: 4px"
@@ -8,9 +8,13 @@ import {
8
8
  useConfigContextInject,
9
9
  } from '../../../shared/ui';
10
10
  import ColumnHeightOutlined from './icons/ColumnHeightOutlined.vue';
11
+ import { computed } from 'vue';
12
+ import { useLocale } from '../../../locale';
11
13
 
12
14
  type Size = TableProps['size'];
13
15
 
16
+ const { t } = useLocale();
17
+
14
18
  const configContext = useConfigContextInject();
15
19
 
16
20
  const size = defineModel<Size>('size');
@@ -21,11 +25,11 @@ const onSizeChange = (val: any) => {
21
25
  size.value = val.key;
22
26
  };
23
27
 
24
- const sizeOptions: Array<MenuItemType> = [
25
- { label: '默认', key: 'large' },
26
- { label: '中等', key: 'middle' },
27
- { label: '紧凑', key: 'small' },
28
- ];
28
+ const sizeOptions = computed<Array<MenuItemType>>(() => [
29
+ { label: t('table.sizeDefault'), key: 'large' },
30
+ { label: t('table.sizeMiddle'), key: 'middle' },
31
+ { label: t('table.sizeCompact'), key: 'small' },
32
+ ]);
29
33
  </script>
30
34
 
31
35
  <template>
@@ -72,3 +72,29 @@ export type Column<D extends Data = Data> = Omit<
72
72
  * ```
73
73
  */
74
74
  export type Columns<D extends Data = Data> = Array<Column<D>>;
75
+
76
+ /**
77
+ * 列控制配置(用于 control.columnControl)
78
+ *
79
+ * @template K - 列 key 类型,ProTable 中传入 Path<RecordType> 获得自动补全
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const config: ColumnControlConfig<'name' | 'age'> = {
84
+ * labels: { name: '姓名', age: '年龄' },
85
+ * excludedKeys: [],
86
+ * disabledKeys: ['name'],
87
+ * disableCheckAll: false,
88
+ * }
89
+ * ```
90
+ */
91
+ export type ColumnControlConfig<K extends string | number = string | number> = {
92
+ /** 列在下拉中的显示名称映射,key 为列 key/dataIndex,用于 title 为 vnode 时无法正常渲染 */
93
+ labels?: Partial<Record<K, string>>;
94
+ /** 不在下拉中出现的列 keys(始终展示,不参与勾选) */
95
+ excludedKeys?: K[];
96
+ /** 在下拉中显示但不可取消勾选的列 keys(始终展示,始终勾选) */
97
+ disabledKeys?: K[];
98
+ /** 禁用全选/取消全选 */
99
+ disableCheckAll?: boolean;
100
+ };
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import ProComponentProvider from './components/component-provider';
6
6
  export * from './components/form';
7
7
  export * from './components/table';
8
8
  export * from './components/component-provider';
9
+ export * from './locale';
9
10
 
10
11
  /**
11
12
  * Schema 驱动的表单组件,基于 antd-vue Form 封装
@@ -0,0 +1,71 @@
1
+ /**
2
+ * antd-vue-pro locale 模块
3
+ *
4
+ * @description 提供 useLocale 组合式函数与语言包。
5
+ * 语言自动跟随宿主应用的 ant-design-vue `a-config-provider` locale 切换,
6
+ * 也可通过 ProComponentProvider 的 `locale` prop 覆盖任意文案。
7
+ */
8
+ import {
9
+ computed,
10
+ inject,
11
+ toValue,
12
+ type InjectionKey,
13
+ type MaybeRef,
14
+ } from 'vue';
15
+ import {
16
+ zhCN,
17
+ zhTW,
18
+ enUS,
19
+ format,
20
+ resolveLocaleName,
21
+ type ProLocale,
22
+ type ProLocaleName,
23
+ } from '@qin-ui/pro-components-core';
24
+ import { useConfigContextInject } from '../shared/ui';
25
+
26
+ export { zhCN, zhTW, enUS };
27
+
28
+ export type { ProLocale };
29
+
30
+ /** ProComponentProvider 通过该 key 注入局部覆盖文案 */
31
+ export const LOCALE_INJECT_KEY: InjectionKey<
32
+ MaybeRef<Partial<ProLocale> | undefined>
33
+ > = Symbol('pro-locale');
34
+
35
+ /**
36
+ * 获取响应式 locale 文案
37
+ *
38
+ * @description 返回 `t` 翻译函数与当前语言名。
39
+ * `t` 在 computed / 模板中调用时会随宿主语言切换自动更新。
40
+ *
41
+ * @returns
42
+ * - `t(key, params?)` - 翻译函数,支持 `{placeholder}` 参数
43
+ * - `lang` - 当前解析出的语言名('zh-CN' | 'zh-TW' | 'en-US')
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const { t } = useLocale();
48
+ * t('table.query') // '查询' / '查詢' / 'Search'
49
+ * t('pagination.total', { total: 100 }) // '共 100 条'
50
+ * ```
51
+ */
52
+ export const useLocale = () => {
53
+ const config = useConfigContextInject();
54
+ const override = inject(LOCALE_INJECT_KEY, undefined);
55
+
56
+ const lang = computed<ProLocaleName>(() =>
57
+ resolveLocaleName(config.locale?.value?.locale)
58
+ );
59
+ const pack = computed<Record<string, string>>(() =>
60
+ lang.value === 'zh-TW' ? zhTW : lang.value === 'en-US' ? enUS : zhCN
61
+ );
62
+
63
+ const t = (key: string, params?: Record<string, string | number>): string => {
64
+ const overrides = toValue(override) as Record<string, string> | undefined;
65
+ const text =
66
+ overrides?.[key] ?? (pack.value as Record<string, string>)[key] ?? key;
67
+ return format(text, params);
68
+ };
69
+
70
+ return { t, lang };
71
+ };