@qin-ui/element-plus-pro 1.1.4 → 1.2.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 (46) hide show
  1. package/AI-CONTEXT.md +19 -8
  2. package/api.json +7 -1
  3. package/es/components/component-provider/components/index.vue.d.ts +6 -0
  4. package/es/components/component-provider/components/index.vue.d.ts.map +1 -1
  5. package/es/components/component-provider/constants/index.d.ts.map +1 -1
  6. package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
  7. package/es/components/form/components/BaseField/utils/index.d.ts +3 -1
  8. package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -1
  9. package/es/components/table/components/ColumnControl.vue.d.ts +2 -1
  10. package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
  11. package/es/components/table/components/ProTable.vue.d.ts +3 -2
  12. package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
  13. package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
  14. package/es/components/table/components/SizeControl.vue.d.ts.map +1 -1
  15. package/es/components/table/types/index.d.ts +22 -0
  16. package/es/components/table/types/index.d.ts.map +1 -1
  17. package/es/components/table/useTable.d.ts.map +1 -1
  18. package/es/element-plus-pro.css +13 -13
  19. package/es/index.d.ts +1 -0
  20. package/es/index.d.ts.map +1 -1
  21. package/es/index.js +285 -125
  22. package/es/index.js.map +1 -1
  23. package/es/locale/en-US.d.ts +26 -0
  24. package/es/locale/en-US.d.ts.map +1 -0
  25. package/es/locale/index.d.ts +31 -0
  26. package/es/locale/index.d.ts.map +1 -0
  27. package/es/locale/zh-CN.d.ts +26 -0
  28. package/es/locale/zh-CN.d.ts.map +1 -0
  29. package/es/locale/zh-TW.d.ts +26 -0
  30. package/es/locale/zh-TW.d.ts.map +1 -0
  31. package/package.json +2 -2
  32. package/src/components/component-provider/components/index.vue +14 -1
  33. package/src/components/component-provider/constants/index.ts +20 -39
  34. package/src/components/form/components/BaseField/index.vue +10 -4
  35. package/src/components/form/components/BaseField/utils/index.ts +66 -3
  36. package/src/components/table/components/ColumnControl.vue +83 -20
  37. package/src/components/table/components/ProTable.vue +36 -10
  38. package/src/components/table/components/SearchForm.vue +6 -3
  39. package/src/components/table/components/SizeControl.vue +9 -5
  40. package/src/components/table/types/index.ts +23 -0
  41. package/src/components/table/useTable.ts +13 -1
  42. package/src/index.ts +1 -0
  43. package/src/locale/en-US.ts +21 -0
  44. package/src/locale/index.ts +68 -0
  45. package/src/locale/zh-CN.ts +21 -0
  46. package/src/locale/zh-TW.ts +21 -0
@@ -0,0 +1,26 @@
1
+ declare const _default: {
2
+ readonly 'date-picker.placeholder': "Please select";
3
+ readonly 'date-picker.start': "Start date";
4
+ readonly 'date-picker.end': "End date";
5
+ readonly 'date-picker.startMonth': "Start month";
6
+ readonly 'date-picker.endMonth': "End month";
7
+ readonly 'date-picker.startYear': "Start year";
8
+ readonly 'date-picker.endYear': "End year";
9
+ readonly 'date-picker.startTime': "Start time";
10
+ readonly 'date-picker.endTime': "End time";
11
+ readonly 'table.query': "Search";
12
+ readonly 'table.reset': "Reset";
13
+ readonly 'table.expand': "Expand";
14
+ readonly 'table.collapse': "Collapse";
15
+ readonly 'table.selectAll': "Select All";
16
+ readonly 'table.index': "No.";
17
+ readonly 'table.sizeDefault': "Default";
18
+ readonly 'table.sizeMiddle': "Middle";
19
+ readonly 'table.sizeCompact': "Compact";
20
+ readonly 'table.columnFallback': "Column {index}";
21
+ readonly 'pagination.total': "Total {total} items";
22
+ readonly 'input.placeholder': "Please input";
23
+ readonly 'select.placeholder': "Please select";
24
+ };
25
+ export default _default;
26
+ //# sourceMappingURL=en-US.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en-US.d.ts","sourceRoot":"","sources":["../../src/locale/en-US.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAQA,wBAYW"}
@@ -0,0 +1,31 @@
1
+ import { InjectionKey, MaybeRef } from 'vue';
2
+ import { ProLocaleName } from '@qin-ui/pro-components-core';
3
+ import { default as zhCN } from './zh-CN';
4
+ import { default as zhTW } from './zh-TW';
5
+ import { default as enUS } from './en-US';
6
+ export { zhCN, zhTW, enUS };
7
+ export type ProLocale = typeof zhCN;
8
+ /** ProComponentProvider 通过该 key 注入局部覆盖文案 */
9
+ export declare const LOCALE_INJECT_KEY: InjectionKey<MaybeRef<Partial<ProLocale> | undefined>>;
10
+ /**
11
+ * 获取响应式 locale 文案
12
+ *
13
+ * @description 返回 `t` 翻译函数与当前语言名。
14
+ * `t` 在 computed / 模板中调用时会随宿主语言切换自动更新。
15
+ *
16
+ * @returns
17
+ * - `t(key, params?)` - 翻译函数,支持 `{placeholder}` 参数
18
+ * - `lang` - 当前解析出的语言名('zh-CN' | 'zh-TW' | 'en-US')
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const { t } = useLocale();
23
+ * t('table.query') // '查询' / '查詢' / 'Search'
24
+ * t('pagination.total', { total: 100 }) // '共 100 条'
25
+ * ```
26
+ */
27
+ export declare const useLocale: () => {
28
+ t: (key: string, params?: Record<string, string | number>) => string;
29
+ lang: import('vue').ComputedRef<ProLocaleName>;
30
+ };
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/locale/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,KAAK,CAAC;AAEb,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,IAAI,MAAM,SAAS,CAAC;AAE3B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAE5B,MAAM,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC;AAEpC,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,EAAE,YAAY,CAC1C,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,CAClB,CAAC;AAEzB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,SAAS;aASJ,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAG,MAAM;;CAQ1E,CAAC"}
@@ -0,0 +1,26 @@
1
+ declare const _default: {
2
+ readonly 'date-picker.placeholder': "请选择";
3
+ readonly 'date-picker.start': "开始日期";
4
+ readonly 'date-picker.end': "结束日期";
5
+ readonly 'date-picker.startMonth': "开始月份";
6
+ readonly 'date-picker.endMonth': "结束月份";
7
+ readonly 'date-picker.startYear': "开始年份";
8
+ readonly 'date-picker.endYear': "结束年份";
9
+ readonly 'date-picker.startTime': "开始时间";
10
+ readonly 'date-picker.endTime': "结束时间";
11
+ readonly 'table.query': "\u67E5\u8BE2";
12
+ readonly 'table.reset': "\u91CD\u7F6E";
13
+ readonly 'table.expand': "\u5C55\u5F00";
14
+ readonly 'table.collapse': "\u6536\u8D77";
15
+ readonly 'table.selectAll': "\u5168\u9009";
16
+ readonly 'table.index': "\u5E8F\u53F7";
17
+ readonly 'table.sizeDefault': "\u9ED8\u8BA4";
18
+ readonly 'table.sizeMiddle': "\u4E2D\u7B49";
19
+ readonly 'table.sizeCompact': "\u7D27\u51D1";
20
+ readonly 'table.columnFallback': "\u5217{index}";
21
+ readonly 'pagination.total': "\u5171 {total} \u6761";
22
+ readonly 'input.placeholder': "\u8BF7\u8F93\u5165";
23
+ readonly 'select.placeholder': "\u8BF7\u9009\u62E9";
24
+ };
25
+ export default _default;
26
+ //# sourceMappingURL=zh-CN.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zh-CN.d.ts","sourceRoot":"","sources":["../../src/locale/zh-CN.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAQA,wBAYW"}
@@ -0,0 +1,26 @@
1
+ declare const _default: {
2
+ readonly 'date-picker.placeholder': "請選擇";
3
+ readonly 'date-picker.start': "開始日期";
4
+ readonly 'date-picker.end': "結束日期";
5
+ readonly 'date-picker.startMonth': "開始月份";
6
+ readonly 'date-picker.endMonth': "結束月份";
7
+ readonly 'date-picker.startYear': "開始年份";
8
+ readonly 'date-picker.endYear': "結束年份";
9
+ readonly 'date-picker.startTime': "開始時間";
10
+ readonly 'date-picker.endTime': "結束時間";
11
+ readonly 'table.query': "\u67E5\u8A62";
12
+ readonly 'table.reset': "\u91CD\u8A2D";
13
+ readonly 'table.expand': "\u5C55\u958B";
14
+ readonly 'table.collapse': "\u6536\u8D77";
15
+ readonly 'table.selectAll': "\u5168\u9078";
16
+ readonly 'table.index': "\u5E8F\u865F";
17
+ readonly 'table.sizeDefault': "\u9810\u8A2D";
18
+ readonly 'table.sizeMiddle': "\u4E2D\u7B49";
19
+ readonly 'table.sizeCompact': "\u7DCA\u6E4A";
20
+ readonly 'table.columnFallback': "\u6B04{index}";
21
+ readonly 'pagination.total': "\u5171 {total} \u7B46";
22
+ readonly 'input.placeholder': "\u8ACB\u8F38\u5165";
23
+ readonly 'select.placeholder': "\u8ACB\u9078\u64C7";
24
+ };
25
+ export default _default;
26
+ //# sourceMappingURL=zh-TW.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zh-TW.d.ts","sourceRoot":"","sources":["../../src/locale/zh-TW.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAQA,wBAYW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qin-ui/element-plus-pro",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "description": "基于 element-plus 的二次封装组件",
5
5
  "bin": {
6
6
  "element-plus-pro": "./bin/init-ai.mjs"
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "vue-component-type-helpers": "^3.2.5",
42
- "@qin-ui/pro-components-core": "^1.0.4"
42
+ "@qin-ui/pro-components-core": "^1.1.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "element-plus": "^2.13.6"
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * @param {ComponentVars} [componentVars] - 组件全局配置变量
9
9
  * @param {Record<ComponentName, any>} [componentMap] - 自定义组件映射
10
+ * @param {Partial<ProLocale>} [locale] - 内置文案覆盖,可覆盖任意 locale 文案(如查询/重置/序号/占位符等)
10
11
  *
11
12
  * @slot default - 子组件内容
12
13
  *
@@ -23,9 +24,10 @@
23
24
  */
24
25
  import { ComponentVars } from '../types';
25
26
  import { INJECT_COMPONENTS, ensureInjectConfig } from '../constants';
26
- import { provide } from 'vue';
27
+ import { computed, provide } from 'vue';
27
28
  import { getObject } from '../../../shared/core';
28
29
  import { ComponentName } from '../../form/constants';
30
+ import { LOCALE_INJECT_KEY, type ProLocale } from '../../../locale';
29
31
 
30
32
  type ComponentMap = Partial<Record<ComponentName, any>>;
31
33
 
@@ -39,6 +41,11 @@ type Props = {
39
41
  componentVars?: ComponentVars;
40
42
  /** 自定义组件映射,用于替换或扩展内置组件 */
41
43
  componentMap?: ComponentMap;
44
+ /**
45
+ * 内置文案覆盖,可覆盖任意 locale 文案(如查询/重置/序号/占位符等),
46
+ * 未覆盖的文案默认跟随宿主组件库语言自动切换
47
+ */
48
+ locale?: Partial<ProLocale>;
42
49
  };
43
50
 
44
51
  const props = defineProps<Props>();
@@ -60,6 +67,12 @@ if (props.componentVars) {
60
67
  if (props.componentMap) {
61
68
  provide(INJECT_COMPONENTS, props.componentMap);
62
69
  }
70
+
71
+ // 注入内置文案覆盖,useLocale 在解析文案时会优先使用
72
+ provide(
73
+ LOCALE_INJECT_KEY,
74
+ computed(() => props.locale)
75
+ );
63
76
  </script>
64
77
 
65
78
  <template>
@@ -38,7 +38,7 @@ export const INJECT_CONFIG: {
38
38
  },
39
39
  input: {
40
40
  injectionKey: Symbol(''),
41
- default: { maxlength: 100, clearable: true, placeholder: '请输入' },
41
+ default: { maxlength: 100, clearable: true },
42
42
  },
43
43
  'input.textarea': {
44
44
  injectionKey: Symbol(''),
@@ -47,7 +47,6 @@ export const INJECT_CONFIG: {
47
47
  autosize: { minRows: 3, maxRows: 6 },
48
48
  showWordLimit: true,
49
49
  clearable: true,
50
- placeholder: '请输入',
51
50
  },
52
51
  },
53
52
  'input.password': {
@@ -56,7 +55,6 @@ export const INJECT_CONFIG: {
56
55
  showPassword: true,
57
56
  maxlength: 100,
58
57
  clearable: true,
59
- placeholder: '请输入',
60
58
  },
61
59
  },
62
60
  'input-number': {
@@ -65,97 +63,80 @@ export const INJECT_CONFIG: {
65
63
  max: 10 ** 15 - 1,
66
64
  min: -(10 ** 15 + 1),
67
65
  controls: false,
68
- placeholder: '请输入',
69
66
  style: { width: '100%' },
70
67
  },
71
68
  },
72
69
  autocomplete: {
73
70
  injectionKey: Symbol(''),
74
- default: { clearable: true, placeholder: '请输入' },
71
+ default: { clearable: true },
75
72
  },
76
73
  select: {
77
74
  injectionKey: Symbol(''),
78
- default: { clearable: true, placeholder: '请选择' },
75
+ default: { clearable: true },
79
76
  },
80
77
  cascader: {
81
78
  injectionKey: Symbol(''),
82
- default: { clearable: true, placeholder: '请选择' },
79
+ default: { clearable: true },
83
80
  },
84
81
  'date-picker': {
85
82
  injectionKey: Symbol(''),
86
- default: { style: { width: '100%' }, placeholder: '请选择' },
83
+ default: { style: { width: '100%' } },
87
84
  },
88
85
  'date-picker.date': {
89
86
  injectionKey: Symbol(''),
90
- default: { style: { width: '100%' }, placeholder: '请选择' },
87
+ default: { style: { width: '100%' } },
91
88
  },
92
89
  'date-picker.dates': {
93
90
  injectionKey: Symbol(''),
94
- default: { style: { width: '100%' }, placeholder: '请选择' },
91
+ default: { style: { width: '100%' } },
95
92
  },
96
93
  'date-picker.daterange': {
97
94
  injectionKey: Symbol(''),
98
- default: {
99
- style: { width: '100%' },
100
- startPlaceholder: '开始日期',
101
- endPlaceholder: '结束日期',
102
- },
95
+ default: { style: { width: '100%' } },
103
96
  },
104
97
  'date-picker.week': {
105
98
  injectionKey: Symbol(''),
106
- default: { style: { width: '100%' }, placeholder: '请选择' },
99
+ default: { style: { width: '100%' } },
107
100
  },
108
101
  'date-picker.month': {
109
102
  injectionKey: Symbol(''),
110
- default: { style: { width: '100%' }, placeholder: '请选择' },
103
+ default: { style: { width: '100%' } },
111
104
  },
112
105
  'date-picker.monthrange': {
113
106
  injectionKey: Symbol(''),
114
- default: {
115
- style: { width: '100%' },
116
- startPlaceholder: '开始月份',
117
- endPlaceholder: '结束月份',
118
- },
107
+ default: { style: { width: '100%' } },
119
108
  },
120
109
  'date-picker.months': {
121
110
  injectionKey: Symbol(''),
122
- default: { style: { width: '100%' }, placeholder: '请选择' },
111
+ default: { style: { width: '100%' } },
123
112
  },
124
113
  'date-picker.yearrange': {
125
114
  injectionKey: Symbol(''),
126
- default: {
127
- style: { width: '100%' },
128
- startPlaceholder: '开始年份',
129
- endPlaceholder: '结束年份',
130
- },
115
+ default: { style: { width: '100%' } },
131
116
  },
132
117
  'date-picker.year': {
133
118
  injectionKey: Symbol(''),
134
- default: { style: { width: '100%' }, placeholder: '请选择' },
119
+ default: { style: { width: '100%' } },
135
120
  },
136
121
  'date-picker.years': {
137
122
  injectionKey: Symbol(''),
138
- default: { style: { width: '100%' }, placeholder: '请选择' },
123
+ default: { style: { width: '100%' } },
139
124
  },
140
125
  'date-picker.datetime': {
141
126
  injectionKey: Symbol(''),
142
- default: { style: { width: '100%' }, placeholder: '请选择' },
127
+ default: { style: { width: '100%' } },
143
128
  },
144
129
  'date-picker.datetimerange': {
145
130
  injectionKey: Symbol(''),
146
- default: {
147
- style: { width: '100%' },
148
- startPlaceholder: '开始时间',
149
- endPlaceholder: '结束时间',
150
- },
131
+ default: { style: { width: '100%' } },
151
132
  },
152
133
  'time-picker': {
153
134
  injectionKey: Symbol(''),
154
- default: { style: { width: '100%' }, placeholder: '请选择' },
135
+ default: { style: { width: '100%' } },
155
136
  },
156
137
  'time-select': {
157
138
  injectionKey: Symbol(''),
158
- default: { style: { width: '100%' }, placeholder: '请选择' },
139
+ default: { style: { width: '100%' } },
159
140
  },
160
141
  'checkbox-group': {
161
142
  injectionKey: Symbol(''),
@@ -175,7 +156,7 @@ export const INJECT_CONFIG: {
175
156
  },
176
157
  'tree-select': {
177
158
  injectionKey: Symbol(''),
178
- default: { clearable: true, placeholder: '请选择' },
159
+ default: { clearable: true },
179
160
  },
180
161
  transfer: {
181
162
  injectionKey: Symbol(''),
@@ -43,6 +43,7 @@ import { componentMap, TeleportComponentNamePrefix } from '../../constants';
43
43
  import { INJECT_COMPONENTS } from '../../../component-provider/constants';
44
44
  import { useForm } from '../../hooks/useForm';
45
45
  import { getInitProps } from './utils';
46
+ import { useLocale } from '../../../../locale';
46
47
 
47
48
  defineOptions({ name: 'BaseField', inheritAttrs: false });
48
49
 
@@ -52,6 +53,8 @@ type Props = {
52
53
  };
53
54
  const { component = undefined, path = '' } = defineProps<Props>();
54
55
 
56
+ const { t } = useLocale();
57
+
55
58
  const form = useForm(false);
56
59
  const { getFormData, setFormData } = form;
57
60
 
@@ -97,10 +100,13 @@ const parentDisabled = useDisabledContext();
97
100
  * 属性分为三类:绑定属性(v-model)、组件属性(传递给组件)、插槽(动态渲染)
98
101
  */
99
102
  const groupedAttrs = computed(() => {
100
- const initProps = getInitProps({
101
- component: component,
102
- type: attrs.type,
103
- } as unknown as Field);
103
+ const initProps = getInitProps(
104
+ {
105
+ component: component,
106
+ type: attrs.type,
107
+ } as unknown as Field,
108
+ t
109
+ );
104
110
  const modelPropName = attrs.modelProp ?? initProps.modelProp ?? 'modelValue';
105
111
  const mergedProps = mergeProps(
106
112
  initProps,
@@ -2,14 +2,77 @@ 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
+ 'input.textarea': 'input.placeholder',
14
+ 'input.password': 'input.placeholder',
15
+ 'input-number': 'input.placeholder',
16
+ autocomplete: 'input.placeholder',
17
+ select: 'select.placeholder',
18
+ cascader: 'select.placeholder',
19
+ 'tree-select': 'select.placeholder',
20
+ 'date-picker': 'date-picker.placeholder',
21
+ 'date-picker.date': 'date-picker.placeholder',
22
+ 'date-picker.dates': 'date-picker.placeholder',
23
+ 'date-picker.week': 'date-picker.placeholder',
24
+ 'date-picker.month': 'date-picker.placeholder',
25
+ 'date-picker.months': 'date-picker.placeholder',
26
+ 'date-picker.year': 'date-picker.placeholder',
27
+ 'date-picker.years': 'date-picker.placeholder',
28
+ 'date-picker.datetime': 'date-picker.placeholder',
29
+ 'time-picker': 'date-picker.placeholder',
30
+ 'time-select': 'date-picker.placeholder',
31
+ };
32
+
33
+ /** 区间组件 start/end 占位符对应的 locale key */
34
+ const RANGE_PLACEHOLDER_KEYS: Record<string, [string, string]> = {
35
+ 'date-picker.daterange': ['date-picker.start', 'date-picker.end'],
36
+ 'date-picker.monthrange': ['date-picker.startMonth', 'date-picker.endMonth'],
37
+ 'date-picker.yearrange': ['date-picker.startYear', 'date-picker.endYear'],
38
+ 'date-picker.datetimerange': ['date-picker.startTime', 'date-picker.endTime'],
39
+ };
40
+
41
+ /**
42
+ * 根据组件 key 兜底生成 locale 占位符
43
+ *
44
+ * @description 用户通过 componentVars 或字段属性显式设置了占位符时不再兜底。
45
+ */
46
+ const getPlaceholderProps = (
47
+ key: string,
48
+ injectProps: Record<string, any>,
49
+ t: LocaleT
50
+ ): Record<string, string> => {
51
+ const rangeKeys = RANGE_PLACEHOLDER_KEYS[key];
52
+ if (rangeKeys) {
53
+ if (
54
+ injectProps.startPlaceholder !== undefined ||
55
+ injectProps.placeholder !== undefined
56
+ )
57
+ return {};
58
+ return {
59
+ startPlaceholder: t(rangeKeys[0]),
60
+ endPlaceholder: t(rangeKeys[1]),
61
+ };
62
+ }
63
+ const placeholderKey = PLACEHOLDER_KEYS[key];
64
+ if (!placeholderKey || injectProps.placeholder !== undefined) return {};
65
+ return { placeholder: t(placeholderKey) };
66
+ };
67
+
68
+ export const getInitProps = (field: Field, t: LocaleT): Record<string, any> => {
6
69
  const { component, type } = field as any;
7
70
  if (typeof component === 'string') {
8
71
  const k = [component, type].filter(Boolean).join('.');
9
72
  const config = getInjectConfig(k);
10
- if (!config) return {};
73
+ if (!config) return getPlaceholderProps(k, {}, t);
11
74
  const injectProps = inject(config.injectionKey, config.default);
12
- return injectProps;
75
+ return { ...injectProps, ...getPlaceholderProps(k, injectProps, t) };
13
76
  }
14
77
  return {};
15
78
  };
@@ -6,16 +6,24 @@ import {
6
6
  DropdownItem,
7
7
  Checkbox,
8
8
  } from '../../../shared/ui';
9
- import { computed, ref, watch } from 'vue';
10
- import type { Column, Columns } from '../types';
9
+ import { computed, onMounted, ref, watch } from 'vue';
10
+ import type { Column, Columns, ColumnControlConfig } from '../types';
11
11
  import { type Table } from '../useTable';
12
12
  import FilterOutlined from './icons/FilterOutlined.vue';
13
+ import { useLocale } from '../../../locale';
13
14
 
14
15
  type Props = {
15
16
  columns: Columns;
16
17
  table?: Table;
18
+ columnControl?: ColumnControlConfig;
17
19
  };
18
- const { columns, table = undefined } = defineProps<Props>();
20
+ const {
21
+ columns,
22
+ table = undefined,
23
+ columnControl = undefined,
24
+ } = defineProps<Props>();
25
+
26
+ const { t } = useLocale();
19
27
 
20
28
  const open = ref(false);
21
29
  const checkAll = ref(false);
@@ -28,14 +36,43 @@ const getColumnKey = (column: Column, index: number) => {
28
36
  };
29
37
 
30
38
  const checkedColumnsOptions = computed<
31
- Array<{ label: string; value: string; checked: boolean; column: Column }>
39
+ Array<{
40
+ label: string;
41
+ value: string;
42
+ checked: boolean;
43
+ disabled: boolean;
44
+ column: Column;
45
+ }>
32
46
  >(() =>
33
- columns.map((item, index) => ({
34
- label: String(item.label ?? item.prop ?? `列${index + 1}`),
35
- value: getColumnKey(item, index),
36
- checked: !item.hidden,
37
- column: item,
38
- }))
47
+ columns
48
+ .map((item, index) => {
49
+ const key = getColumnKey(item, index);
50
+ const excluded = columnControl?.excludedKeys?.some(
51
+ ek => String(ek) === key
52
+ );
53
+ const disabled = columnControl?.disabledKeys?.some(
54
+ dk => String(dk) === key
55
+ );
56
+ const label = String(
57
+ item.label ??
58
+ item.prop ??
59
+ t('table.columnFallback', { index: index + 1 })
60
+ );
61
+ return {
62
+ label,
63
+ value: key,
64
+ checked: !item.hidden || !!disabled,
65
+ disabled: !!disabled,
66
+ column: item,
67
+ excluded,
68
+ };
69
+ })
70
+ .filter(item => !item.excluded)
71
+ );
72
+
73
+ /** 可被用户勾选/取消的列(非 disabled) */
74
+ const controllableOptions = computed(() =>
75
+ checkedColumnsOptions.value.filter(item => !item.disabled)
39
76
  );
40
77
 
41
78
  const checkedColumnKeys = computed({
@@ -46,6 +83,7 @@ const checkedColumnKeys = computed({
46
83
  },
47
84
  set(val: string[]) {
48
85
  checkedColumnsOptions.value.forEach(item => {
86
+ if (item.disabled) return;
49
87
  table?.setColumn(item.value as any, {
50
88
  hidden: !val.includes(item.value),
51
89
  });
@@ -56,12 +94,14 @@ const checkedColumnKeys = computed({
56
94
  const onCheckAllChange = (value: any) => {
57
95
  const checked = !!(value?.target?.checked ?? value);
58
96
  checkedColumnKeys.value = checked
59
- ? checkedColumnsOptions.value.map(item => item.value)
97
+ ? controllableOptions.value.map(item => item.value)
60
98
  : [];
61
99
  indeterminate.value = false;
62
100
  };
63
101
 
64
102
  const toggleColumnsItem = (val: string) => {
103
+ const item = checkedColumnsOptions.value.find(o => o.value === val);
104
+ if (item?.disabled) return;
65
105
  if (checkedColumnKeys.value.includes(val)) {
66
106
  checkedColumnKeys.value = checkedColumnKeys.value.filter(
67
107
  item => item !== val
@@ -73,14 +113,36 @@ const toggleColumnsItem = (val: string) => {
73
113
 
74
114
  watch(
75
115
  checkedColumnKeys,
76
- val => {
116
+ () => {
117
+ const controllableChecked = controllableOptions.value.filter(
118
+ item => item.checked
119
+ );
77
120
  indeterminate.value =
78
- !!val.length && val.length < checkedColumnsOptions.value.length;
121
+ !!controllableChecked.length &&
122
+ controllableChecked.length < controllableOptions.value.length;
79
123
  checkAll.value =
80
- val.length > 0 && val.length === checkedColumnsOptions.value.length;
124
+ controllableChecked.length === controllableOptions.value.length &&
125
+ controllableOptions.value.length > 0;
81
126
  },
82
127
  { immediate: true }
83
128
  );
129
+
130
+ // 强制 disabledKeys 列始终展示
131
+ onMounted(() => {
132
+ columnControl?.disabledKeys?.forEach(key => {
133
+ table?.setColumn(String(key), { hidden: false });
134
+ });
135
+ });
136
+
137
+ watch(
138
+ () => columnControl?.disabledKeys,
139
+ keys => {
140
+ keys?.forEach(key => {
141
+ table?.setColumn(String(key), { hidden: false });
142
+ });
143
+ },
144
+ { deep: true }
145
+ );
84
146
  </script>
85
147
 
86
148
  <template>
@@ -95,13 +157,17 @@ watch(
95
157
  </Button>
96
158
  <template #dropdown>
97
159
  <DropdownMenu>
98
- <DropdownItem :disabled="true" :divided="false">
160
+ <DropdownItem
161
+ v-if="!columnControl?.disableCheckAll"
162
+ :disabled="true"
163
+ :divided="false"
164
+ >
99
165
  <Checkbox
100
166
  :model-value="checkAll"
101
167
  :indeterminate="indeterminate"
102
168
  @change="onCheckAllChange"
103
169
  >
104
- 全选
170
+ {{ t('table.selectAll') }}
105
171
  </Checkbox>
106
172
  </DropdownItem>
107
173
  <DropdownItem
@@ -110,10 +176,7 @@ watch(
110
176
  :command="item.value"
111
177
  @click.stop="toggleColumnsItem(item.value)"
112
178
  >
113
- <Checkbox
114
- :model-value="item.checked"
115
- @change="() => toggleColumnsItem(item.value)"
116
- >
179
+ <Checkbox :model-value="item.checked" :disabled="item.disabled">
117
180
  {{ item.label }}
118
181
  </Checkbox>
119
182
  </DropdownItem>