@kine-design/crud 0.0.1-beta.24 → 0.0.1-beta.26

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.
@@ -1 +1 @@
1
- 594292cc-8321-4629-97d9-91ad432e0646
1
+ 4c9b1c27-b707-4b0b-98d7-fde5d511ce47
@@ -9,6 +9,7 @@
9
9
  * 一个配置出一整个页面:标题 + 筛选 + 表格 + 分页 + 状态标签。
10
10
  */
11
11
  import { defineComponent, ref, computed, type PropType } from 'vue';
12
+ import { useLocale } from '@kine-design/core';
12
13
  import KTableColumn from 'kine-ui/components/tableColumn/KTableColumn.tsx';
13
14
  import KTag from 'kine-ui/components/tag/KTag.tsx';
14
15
  import KImage from 'kine-ui/components/image/KImage.tsx';
@@ -31,6 +32,7 @@ export default defineComponent({
31
32
  },
32
33
 
33
34
  setup(props, { slots }) {
35
+ const t = useLocale();
34
36
  const rootRef = ref<HTMLElement>();
35
37
  const tableBodyRef = computed(
36
38
  () => rootRef.value?.querySelector('.k-search-table-body') as HTMLElement | undefined,
@@ -107,7 +109,7 @@ export default defineComponent({
107
109
  const renderFilters = () => {
108
110
  if (!props.config.filters?.length) return null;
109
111
  return props.config.filters.map(f => (
110
- <div class="k-crud-page-filter-item" key={f.param}>
112
+ <div class="k-crud-page-filter-item" data-k={`filter:${f.param}`} key={f.param}>
111
113
  <label class="k-crud-page-filter-label">{f.label}</label>
112
114
  {f.type === 'select' ? (
113
115
  <KSelect
@@ -116,7 +118,7 @@ export default defineComponent({
116
118
  options={f.options ?? []}
117
119
  optionParam="label"
118
120
  valueParam="value"
119
- placeholder={f.placeholder ?? '全部'}
121
+ placeholder={f.placeholder ?? t.value.crudPage.all}
120
122
  onUpdate:modelValue={(v: unknown) => {
121
123
  filters[f.param] = (v as string) || undefined;
122
124
  }}
@@ -124,7 +126,7 @@ export default defineComponent({
124
126
  ) : (
125
127
  <KInput
126
128
  class="k-crud-page-filter-control"
127
- placeholder={f.placeholder ?? `请输入${f.label}`}
129
+ placeholder={f.placeholder ?? t.value.crudPage.fieldPlaceholder(f.label)}
128
130
  modelValue={(filters[f.param] as string) ?? ''}
129
131
  onUpdate:modelValue={(v: string) => {
130
132
  filters[f.param] = v || undefined;
@@ -136,7 +138,7 @@ export default defineComponent({
136
138
  };
137
139
 
138
140
  return () => (
139
- <div class="k-crud-page" ref={rootRef}>
141
+ <div class="k-crud-page" data-k="page:crudList" ref={rootRef}>
140
142
  <KPageHeader title={props.config.title}>
141
143
  {{ extra: slots.headerExtra }}
142
144
  </KPageHeader>
@@ -13,6 +13,7 @@
13
13
  * - 汇总行:底部加粗合计
14
14
  */
15
15
  import { defineComponent, ref, type PropType } from 'vue';
16
+ import { useLocale } from '@kine-design/core';
16
17
  import KButton from 'kine-ui/components/button/KButton.tsx';
17
18
  import KInput from 'kine-ui/components/input/KInput.tsx';
18
19
  import KSelect from 'kine-ui/components/select/KSelect.tsx';
@@ -68,9 +69,9 @@ export default defineComponent({
68
69
  /** 是否显示汇总行 */
69
70
  showSummary: { type: Boolean, default: false },
70
71
  /** 汇总行标签 */
71
- summaryLabel: { type: String, default: '合计' },
72
+ summaryLabel: { type: String, default: undefined },
72
73
  /** 新增行按钮文本 */
73
- addText: { type: String, default: '+ 添加行' },
74
+ addText: { type: String, default: undefined },
74
75
  /** 是否可新增 */
75
76
  addable: { type: Boolean, default: true },
76
77
  /** 币种前缀(用于金额汇总展示) */
@@ -78,6 +79,7 @@ export default defineComponent({
78
79
  },
79
80
  emits: ['update:modelValue', 'add', 'remove'],
80
81
  setup(props, { emit, slots }) {
82
+ const t = useLocale();
81
83
  const deletingId = ref<string | number | null>(null);
82
84
 
83
85
  // ── 更新某行某字段 ──────────────────────────────
@@ -177,7 +179,7 @@ export default defineComponent({
177
179
  <div class="k-et-header-right">
178
180
  {slots.headerExtra?.()}
179
181
  {props.addable && (
180
- <KButton plain text={props.addText} onClick={addRow} />
182
+ <KButton plain text={props.addText ?? t.value.editableTable.addRow} onClick={addRow} />
181
183
  )}
182
184
  </div>
183
185
  </div>
@@ -212,9 +214,9 @@ export default defineComponent({
212
214
  <td class="k-et-td" colspan={cols.length + (props.showActions ? 1 : 0)}>
213
215
  <span class="k-et-delete-confirm">
214
216
  <s class="k-et-deleted-text">{row[cols[0].param] as string ?? ''}</s>
215
- <span class="k-et-delete-ask">确定删除这一行?</span>
216
- <KButton type="danger" size="small" text="删除" onClick={confirmDelete} />
217
- <KButton plain size="small" text="取消" onClick={cancelDelete} />
217
+ <span class="k-et-delete-ask">{t.value.editableTable.confirmDelete}</span>
218
+ <KButton type="danger" size="small" text={t.value.editableTable.delete} onClick={confirmDelete} />
219
+ <KButton plain size="small" text={t.value.editableTable.cancel} onClick={cancelDelete} />
218
220
  </span>
219
221
  </td>
220
222
  </tr>
@@ -246,7 +248,7 @@ export default defineComponent({
246
248
  {props.showIndex && <td class="k-et-td" />}
247
249
  {cols.map((col, ci) => (
248
250
  <td key={col.param} class={['k-et-td k-et-td-summary', col.align && `k-et-align-${col.align}`]}>
249
- {ci === 0 ? props.summaryLabel : ''}
251
+ {ci === 0 ? (props.summaryLabel ?? t.value.editableTable.summary) : ''}
250
252
  {col.summary ? (
251
253
  <span class="k-et-summary-value">
252
254
  {props.currencyPrefix && col.summaryFormat === undefined && ci === cols.length - 1 ? `${props.currencyPrefix} ` : ''}
@@ -266,9 +268,9 @@ export default defineComponent({
266
268
  <div class="k-et-empty">
267
269
  {slots.empty?.() ?? (
268
270
  <>
269
- <span class="k-et-empty-text">暂无数据</span>
271
+ <span class="k-et-empty-text">{t.value.editableTable.noData}</span>
270
272
  {props.addable && (
271
- <KButton type="primary" text="添加第一行" onClick={addRow} />
273
+ <KButton type="primary" text={t.value.editableTable.addFirstRow} onClick={addRow} />
272
274
  )}
273
275
  </>
274
276
  )}
@@ -12,28 +12,18 @@
12
12
  * - reject: 红色确认按钮,备注必填
13
13
  */
14
14
  import { defineComponent, ref, type PropType } from 'vue';
15
+ import { useLocale } from '@kine-design/core';
15
16
  import KButton from 'kine-ui/components/button/KButton.tsx';
16
17
  import KInput from 'kine-ui/components/input/KInput.tsx';
17
18
  import './formPage.css';
18
19
 
19
20
  export type ApprovalType = 'submit' | 'approve' | 'reject';
20
21
 
21
- const CONFIG = {
22
- submit: {
23
- title: '提交审批',
24
- confirmText: '确认提交',
25
- btnType: 'primary' as const,
26
- },
27
- approve: {
28
- title: '审批通过',
29
- confirmText: '确认通过',
30
- btnType: 'success' as const,
31
- },
32
- reject: {
33
- title: '驳回申请',
34
- confirmText: '确认驳回',
35
- btnType: 'danger' as const,
36
- },
22
+ // Button visual style per type. Text is resolved from locale at render time.
23
+ const BTN_TYPE = {
24
+ submit: 'primary' as const,
25
+ approve: 'success' as const,
26
+ reject: 'danger' as const,
37
27
  } as const;
38
28
 
39
29
  export default defineComponent({
@@ -58,6 +48,7 @@ export default defineComponent({
58
48
  },
59
49
  emits: ['update:visible', 'confirm', 'cancel'],
60
50
  setup(props, { emit, slots }) {
51
+ const t = useLocale();
61
52
  const remark = ref('');
62
53
 
63
54
  const close = () => {
@@ -82,10 +73,20 @@ export default defineComponent({
82
73
  return () => {
83
74
  if (!props.visible) return null;
84
75
 
85
- const cfg = CONFIG[props.type];
86
- const displayTitle = props.title || cfg.title;
76
+ const ad = t.value.approvalDialog;
77
+ const titleMap = {
78
+ submit: ad.submitTitle,
79
+ approve: ad.approveTitle,
80
+ reject: ad.rejectTitle,
81
+ };
82
+ const confirmMap = {
83
+ submit: ad.submitConfirm,
84
+ approve: ad.approveConfirm,
85
+ reject: ad.rejectConfirm,
86
+ };
87
+ const displayTitle = props.title || titleMap[props.type];
87
88
  const placeholder = props.remarkPlaceholder
88
- || (props.type === 'reject' ? '请输入驳回原因...' : '添加备注(可选)...');
89
+ || (props.type === 'reject' ? ad.rejectPlaceholder : ad.remarkPlaceholder);
89
90
 
90
91
  return (
91
92
  <div class="k-approval-overlay" onClick={close}>
@@ -119,16 +120,16 @@ export default defineComponent({
119
120
  onUpdate:modelValue={(v: string) => { remark.value = v; }}
120
121
  />
121
122
  {isRemarkRequired() && (
122
- <span class="k-approval-dialog-required-hint">* 必填</span>
123
+ <span class="k-approval-dialog-required-hint">{ad.required}</span>
123
124
  )}
124
125
  </div>
125
126
 
126
127
  {/* 底部 */}
127
128
  <div class="k-approval-dialog-footer">
128
- <KButton text="取消" onClick={close} />
129
+ <KButton text={ad.cancel} onClick={close} />
129
130
  <KButton
130
- type={cfg.btnType}
131
- text={props.loading ? '处理中...' : cfg.confirmText}
131
+ type={BTN_TYPE[props.type]}
132
+ text={props.loading ? ad.processing : confirmMap[props.type]}
132
133
  disabled={!canConfirm()}
133
134
  loading={props.loading}
134
135
  onClick={confirm}
@@ -10,6 +10,7 @@
10
10
  * 业务页面只需 <KFormPage :config="config" /> 即可。
11
11
  */
12
12
  import { defineComponent, type PropType } from 'vue';
13
+ import { useLocale } from '@kine-design/core';
13
14
  import { useFormPage, renderFormField } from '../../composables/form';
14
15
  import type { FormPageConfig } from '../../composables/form/types';
15
16
  import KFormCard from './KFormCard';
@@ -23,6 +24,8 @@ export default defineComponent({
23
24
  config: { type: Object as PropType<FormPageConfig>, required: true },
24
25
  },
25
26
  setup(props, { slots }) {
27
+ const t = useLocale();
28
+
26
29
  const {
27
30
  formData,
28
31
  isEdit,
@@ -46,10 +49,12 @@ export default defineComponent({
46
49
 
47
50
  return () => {
48
51
  if (loading.value) {
49
- return <div class="k-fp-loading">加载中...</div>;
52
+ return <div class="k-fp-loading">{t.value.formPage.loading}</div>;
50
53
  }
51
54
 
52
- const title = isEdit.value ? `编辑${props.config.title}` : `新建${props.config.title}`;
55
+ const title = isEdit.value
56
+ ? t.value.formPage.editTitle(props.config.title)
57
+ : t.value.formPage.createTitle(props.config.title);
53
58
 
54
59
  return (
55
60
  <div class="k-form-page-wrapper">
@@ -102,17 +107,17 @@ export default defineComponent({
102
107
  {{
103
108
  default: () => slots.actions?.({ formData, submit, saveDraft, submitting }) ?? (
104
109
  <>
105
- <KButton text="取消" onClick={goBack} />
110
+ <KButton text={t.value.formPage.cancel} onClick={goBack} />
106
111
  {props.config.showDraft && (
107
112
  <KButton
108
- text="保存草稿"
113
+ text={t.value.formPage.saveDraft}
109
114
  disabled={submitting.value}
110
115
  onClick={() => saveDraft()}
111
116
  />
112
117
  )}
113
118
  <KButton
114
119
  type="primary"
115
- text={submitting.value ? '保存中...' : (props.config.submitText ?? '保存')}
120
+ text={submitting.value ? t.value.formPage.saving : (props.config.submitText ?? t.value.formPage.save)}
116
121
  disabled={submitting.value}
117
122
  loading={submitting.value}
118
123
  onClick={() => submit()}
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import { defineComponent, ref, type PropType } from 'vue';
13
13
  import { useRouter } from 'vue-router';
14
+ import { useLocale } from '@kine-design/core';
14
15
  import { useFormPage, renderFormField } from '../../composables/form';
15
16
  import type { MasterDetailPageConfig, DetailTableConfig } from '../../composables/form/types';
16
17
  import type { EditableTableRow } from '../editableTable';
@@ -27,6 +28,7 @@ export default defineComponent({
27
28
  },
28
29
  setup(props, { slots }) {
29
30
  const router = useRouter();
31
+ const t = useLocale();
30
32
 
31
33
  const {
32
34
  formData,
@@ -78,12 +80,12 @@ export default defineComponent({
78
80
 
79
81
  return () => {
80
82
  if (loading.value) {
81
- return <div class="k-fp-loading">加载中...</div>;
83
+ return <div class="k-fp-loading">{t.value.formPage.loading}</div>;
82
84
  }
83
85
 
84
86
  const title = isEdit.value
85
87
  ? `${props.config.title}`
86
- : `新建${props.config.title}`;
88
+ : t.value.formPage.createTitle(props.config.title);
87
89
 
88
90
  return (
89
91
  <div class="k-form-page k-master-detail-page">
@@ -115,7 +117,7 @@ export default defineComponent({
115
117
  </div>
116
118
 
117
119
  {/* 主表字段区 */}
118
- <KFormCard title="基本信息">
120
+ <KFormCard title={t.value.formPage.basicInfo}>
119
121
  {{
120
122
  default: () => (
121
123
  <div class={`k-fp-grid k-fp-grid--${cols}`}>
@@ -179,17 +181,17 @@ export default defineComponent({
179
181
  {{
180
182
  default: () => slots.actions?.({ formData, submit, saveDraft, submitting }) ?? (
181
183
  <>
182
- <KButton text="取消" onClick={goBack} />
184
+ <KButton text={t.value.formPage.cancel} onClick={goBack} />
183
185
  {props.config.showDraft && (
184
186
  <KButton
185
- text="保存草稿"
187
+ text={t.value.formPage.saveDraft}
186
188
  disabled={submitting.value}
187
189
  onClick={() => saveDraft()}
188
190
  />
189
191
  )}
190
192
  <KButton
191
193
  type="primary"
192
- text={submitting.value ? '保存中...' : (props.config.submitText ?? '提交')}
194
+ text={submitting.value ? t.value.formPage.saving : (props.config.submitText ?? t.value.formPage.submit)}
193
195
  disabled={submitting.value}
194
196
  loading={submitting.value}
195
197
  onClick={() => submit()}
@@ -7,6 +7,7 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  import { computed, defineComponent, inject, ref } from 'vue';
10
+ import { useLocale } from '@kine-design/core';
10
11
  import {
11
12
  LAYOUT_COLLAPSED_KEY,
12
13
  LAYOUT_TOGGLE_KEY,
@@ -18,6 +19,8 @@ import {
18
19
  export default defineComponent({
19
20
  name: 'KSider',
20
21
  setup(_, ctx) {
22
+ const t = useLocale();
23
+
21
24
  // 从 KLayout 注入折叠状态、切换方法和宽度配置
22
25
  const collapsed = inject(LAYOUT_COLLAPSED_KEY);
23
26
  const toggle = inject(LAYOUT_TOGGLE_KEY);
@@ -41,7 +44,7 @@ export default defineComponent({
41
44
  <button
42
45
  class="k-crud-sider-trigger-btn"
43
46
  onClick={handleTriggerClick}
44
- title={isCollapsed.value ? '展开侧边栏' : '折叠侧边栏'}
47
+ title={isCollapsed.value ? t.value.a11y.expandSider : t.value.a11y.collapseSider}
45
48
  >
46
49
  <span class={['k-crud-sider-trigger-icon', isCollapsed.value && 'k-crud-sider-trigger-icon--collapsed']}>
47
50
  {'‹'}
@@ -7,6 +7,7 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  import { defineComponent, ref } from 'vue';
10
+ import { useLocale } from '@kine-design/core';
10
11
  import KInput from 'kine-ui/components/input/KInput.tsx';
11
12
  import KButton from 'kine-ui/components/button/KButton.tsx';
12
13
  import './login.css';
@@ -37,6 +38,7 @@ export default defineComponent({
37
38
  },
38
39
  },
39
40
  setup(props, ctx) {
41
+ const t = useLocale();
40
42
  const username = ref('');
41
43
  const password = ref('');
42
44
  const remember = ref(false);
@@ -68,21 +70,21 @@ export default defineComponent({
68
70
  <form class="k-login-form" onSubmit={handleSubmit}>
69
71
  {/* 用户名 */}
70
72
  <div class="k-login-form-item">
71
- <label>用户名</label>
73
+ <label>{t.value.login.username}</label>
72
74
  <KInput
73
75
  modelValue={username.value}
74
- placeholder="请输入用户名"
76
+ placeholder={t.value.login.usernamePlaceholder}
75
77
  onUpdate:modelValue={(v: string) => { username.value = v; }}
76
78
  />
77
79
  </div>
78
80
 
79
81
  {/* 密码 */}
80
82
  <div class="k-login-form-item">
81
- <label>密码</label>
83
+ <label>{t.value.login.password}</label>
82
84
  <KInput
83
85
  type="password"
84
86
  modelValue={password.value}
85
- placeholder="请输入密码"
87
+ placeholder={t.value.login.passwordPlaceholder}
86
88
  onUpdate:modelValue={(v: string) => { password.value = v; }}
87
89
  />
88
90
  </div>
@@ -96,13 +98,13 @@ export default defineComponent({
96
98
  remember.value = (e.target as HTMLInputElement).checked;
97
99
  }}
98
100
  />
99
- 记住我
101
+ {t.value.login.remember}
100
102
  </label>
101
103
 
102
104
  {/* 提交按钮 */}
103
105
  <KButton
104
106
  type="primary"
105
- text={props.loading ? '登录中...' : '登 录'}
107
+ text={props.loading ? t.value.login.loggingIn : t.value.login.login}
106
108
  disabled={props.loading}
107
109
  loading={props.loading}
108
110
  onClick={handleSubmit}
@@ -7,6 +7,7 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  import { defineComponent, PropType } from 'vue';
10
+ import { useLocale } from '@kine-design/core';
10
11
  import KTable from 'kine-ui/components/table/KTable.tsx';
11
12
  import KPagination from 'kine-ui/components/pagination/KPagination.tsx';
12
13
  import KButton from 'kine-ui/components/button/KButton.tsx';
@@ -68,6 +69,8 @@ export default defineComponent({
68
69
  ],
69
70
 
70
71
  setup(props, ctx) {
72
+ const t = useLocale();
73
+
71
74
  /** 点击搜索按钮 */
72
75
  const onSearch = () => {
73
76
  ctx.emit('search');
@@ -94,9 +97,9 @@ export default defineComponent({
94
97
  {ctx.slots.search?.()}
95
98
  </div>
96
99
  {/* 搜索 / 重置按钮区 */}
97
- <div class="k-search-table-search-actions">
98
- <KButton type="primary" text="搜索" onClick={onSearch} />
99
- <KButton text="重置" onClick={onReset} />
100
+ <div class="k-search-table-search-actions" data-k="action:searchActions">
101
+ <KButton type="primary" text={t.value.searchTable.search} data-k="action:search" onClick={onSearch} />
102
+ <KButton text={t.value.searchTable.reset} data-k="action:reset" onClick={onReset} />
100
103
  </div>
101
104
  </div>
102
105
  )}
@@ -7,17 +7,11 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  import { defineComponent, PropType } from 'vue';
10
+ import { useLocale } from '@kine-design/core';
10
11
  import type { UploadFile } from './types';
11
12
  import KButton from 'kine-ui/components/button/KButton.tsx';
12
13
  import './upload.css';
13
14
 
14
- const STATUS_TEXT: Record<UploadFile['status'], string> = {
15
- pending: '等待中',
16
- uploading: '上传中',
17
- success: '已完成',
18
- error: '上传失败',
19
- };
20
-
21
15
  function formatSize(bytes: number): string {
22
16
  if (bytes < 1024) return `${bytes} B`;
23
17
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -38,6 +32,20 @@ export default defineComponent({
38
32
  },
39
33
  emits: ['remove'],
40
34
  setup(props, ctx) {
35
+ const t = useLocale();
36
+
37
+ // 状态文案映射:在 render 内现取以保持响应式(不能留在模块顶层)
38
+ const statusText = (status: UploadFile['status']): string => {
39
+ const u = t.value.upload;
40
+ const map: Record<UploadFile['status'], string> = {
41
+ pending: u.statusWaiting,
42
+ uploading: u.statusUploading,
43
+ success: u.statusDone,
44
+ error: u.statusFailed,
45
+ };
46
+ return map[status];
47
+ };
48
+
41
49
  return () => {
42
50
  if (props.files.length === 0) return null;
43
51
 
@@ -62,7 +70,7 @@ export default defineComponent({
62
70
  }
63
71
  </span>
64
72
  <span class="k-file-list-meta">
65
- {formatSize(file.size)} · {STATUS_TEXT[file.status]}
73
+ {formatSize(file.size)} · {statusText(file.status)}
66
74
  </span>
67
75
  </div>
68
76
 
@@ -7,6 +7,7 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  import { defineComponent, ref, computed, onBeforeUnmount, PropType } from 'vue';
10
+ import { useLocale } from '@kine-design/core';
10
11
  import type { UploadFile, UploadRequest } from './types';
11
12
  import KButton from 'kine-ui/components/button/KButton.tsx';
12
13
  import './upload.css';
@@ -58,6 +59,7 @@ export default defineComponent({
58
59
  },
59
60
  emits: ['update:fileList', 'change', 'error'],
60
61
  setup(props, ctx) {
62
+ const t = useLocale();
61
63
  const inputRef = ref<HTMLInputElement | null>(null);
62
64
  const isDragOver = ref(false);
63
65
  // 本地 objectURL 缓存,组件销毁时统一释放
@@ -101,7 +103,7 @@ export default defineComponent({
101
103
 
102
104
  for (const raw of filesToProcess) {
103
105
  if (props.maxSize !== Infinity && raw.size > props.maxSize) {
104
- ctx.emit('error', { file: raw, message: `文件 "${raw.name}" 超过大小限制` });
106
+ ctx.emit('error', { file: raw, message: t.value.upload.oversize(raw.name) });
105
107
  continue;
106
108
  }
107
109
 
@@ -230,7 +232,7 @@ export default defineComponent({
230
232
  )}
231
233
  {file.status === 'error' && (
232
234
  <div class="k-image-upload-overlay k-image-upload-overlay--error">
233
- <span>上传失败</span>
235
+ <span>{t.value.upload.uploadFailed}</span>
234
236
  </div>
235
237
  )}
236
238
  {!props.disabled && (
@@ -7,6 +7,7 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
  import { defineComponent, ref, PropType } from 'vue';
10
+ import { useLocale } from '@kine-design/core';
10
11
  import type { UploadFile, UploadRequest } from './types';
11
12
  import './upload.css';
12
13
 
@@ -59,6 +60,7 @@ export default defineComponent({
59
60
  },
60
61
  emits: ['update:fileList', 'change', 'error'],
61
62
  setup(props, ctx) {
63
+ const t = useLocale();
62
64
  const inputRef = ref<HTMLInputElement | null>(null);
63
65
  const isDragOver = ref(false);
64
66
 
@@ -69,7 +71,7 @@ export default defineComponent({
69
71
 
70
72
  const validateFile = (file: File): string | null => {
71
73
  if (props.maxSize !== Infinity && file.size > props.maxSize) {
72
- return `文件 "${file.name}" 超过大小限制(最大 ${formatSize(props.maxSize)})`;
74
+ return t.value.upload.oversize(file.name, formatSize(props.maxSize));
73
75
  }
74
76
  return null;
75
77
  };
@@ -195,7 +197,7 @@ export default defineComponent({
195
197
  <div class="k-upload-placeholder">
196
198
  <span class="k-upload-icon">↑</span>
197
199
  <span class="k-upload-text">
198
- {props.drag ? '点击或拖拽文件到此处上传' : '点击上传文件'}
200
+ {props.drag ? t.value.upload.trigger : t.value.upload.triggerSimple}
199
201
  </span>
200
202
  </div>
201
203
  )}
@@ -7,6 +7,7 @@
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
9
 
10
+ import { getActiveLocaleMessages } from '@kine-design/core';
10
11
  import { BusinessError, NetworkRequestError } from '../request/types';
11
12
  import type { ErrorHandlerOptions } from './types';
12
13
  import { defaultFeedbackHandler } from './defaultFeedbackHandler';
@@ -20,6 +21,7 @@ import { defaultFeedbackHandler } from './defaultFeedbackHandler';
20
21
  * createErrorHandler 和 useErrorHandler 共用此函数。
21
22
  */
22
23
  export function dispatchError(error: unknown, options: ErrorHandlerOptions): void {
24
+ const t = getActiveLocaleMessages();
23
25
  const feedback = options.feedbackHandler ?? defaultFeedbackHandler;
24
26
 
25
27
  // 业务错误:后端返回 success=false
@@ -41,30 +43,30 @@ export function dispatchError(error: unknown, options: ErrorHandlerOptions): voi
41
43
  return;
42
44
  }
43
45
  if (detail.status === 403) {
44
- feedback.showWarning('权限不足,无法执行此操作');
46
+ feedback.showWarning(t.request.permissionDenied);
45
47
  return;
46
48
  }
47
- feedback.showError(`请求失败 (${detail.status})`);
49
+ feedback.showError(t.request.failedWithStatus(detail.status));
48
50
  return;
49
51
  case 'timeout':
50
- feedback.showWarning('请求超时,请稍后重试');
52
+ feedback.showWarning(t.request.timeoutRetry);
51
53
  return;
52
54
  case 'aborted':
53
55
  // 用户主动取消,静默处理
54
56
  return;
55
57
  case 'invalidURL':
56
- feedback.showError(`无效的请求地址: ${detail.url}`);
58
+ feedback.showError(t.request.invalidUrl(detail.url));
57
59
  return;
58
60
  case 'decodingFailed':
59
- feedback.showError('数据解析失败');
61
+ feedback.showError(t.request.parseFailed);
60
62
  return;
61
63
  case 'unknown':
62
- feedback.showError('网络异常,请检查网络连接');
64
+ feedback.showError(t.request.networkException);
63
65
  return;
64
66
  }
65
67
  }
66
68
 
67
69
  // 其他未知错误
68
70
  const message = error instanceof Error ? error.message : String(error);
69
- feedback.showError(message || '发生了未知错误');
71
+ feedback.showError(message || t.request.unknown);
70
72
  }
@@ -9,6 +9,7 @@
9
9
  * 抽取自 KFormPage / KMasterDetailPage 的 renderField,消除重复。
10
10
  * KFormPage 和 KMasterDetailPage 均通过此函数渲染主表单字段。
11
11
  */
12
+ import { getActiveLocaleMessages } from '@kine-design/core';
12
13
  import type { FormFieldConfig } from './types';
13
14
  import KInput from 'kine-ui/components/input/KInput.tsx';
14
15
  import KSelect from 'kine-ui/components/select/KSelect.tsx';
@@ -29,6 +30,7 @@ export function renderFormField(
29
30
  validateField: (param: string) => void,
30
31
  slots: Record<string, ((...args: unknown[]) => unknown) | undefined>,
31
32
  ) {
33
+ const t = getActiveLocaleMessages();
32
34
  // 优先使用自定义插槽
33
35
  const customSlot = slots[`field-${field.param}`];
34
36
  if (customSlot) {
@@ -46,7 +48,7 @@ export function renderFormField(
46
48
  type="textarea"
47
49
  class={['k-fp-input--error-wrap', error && 'k-fp-input--error', field.disabled && 'k-fp-input--disabled'].filter(Boolean)}
48
50
  modelValue={val as string}
49
- placeholder={field.placeholder ?? `请输入${field.label}`}
51
+ placeholder={field.placeholder ?? t.form.inputPlaceholder(field.label)}
50
52
  disabled={field.disabled}
51
53
  readonly={field.readonly}
52
54
  onUpdate:modelValue={(v: string) => { formData[field.param] = v; }}
@@ -62,7 +64,7 @@ export function renderFormField(
62
64
  type="number"
63
65
  class={['k-fp-input', error && 'k-fp-input--error', field.disabled && 'k-fp-input--disabled'].filter(Boolean)}
64
66
  modelValue={val as string | number}
65
- placeholder={field.placeholder ?? `请输入${field.label}`}
67
+ placeholder={field.placeholder ?? t.form.inputPlaceholder(field.label)}
66
68
  disabled={field.disabled}
67
69
  readonly={field.readonly}
68
70
  onUpdate:modelValue={(v: string) => { formData[field.param] = v; }}
@@ -80,7 +82,7 @@ export function renderFormField(
80
82
  options={field.options ?? []}
81
83
  optionParam="label"
82
84
  valueParam="value"
83
- placeholder={field.placeholder ?? `请选择${field.label}`}
85
+ placeholder={field.placeholder ?? t.form.selectPlaceholder(field.label)}
84
86
  disabled={field.disabled}
85
87
  readonly={field.readonly}
86
88
  onUpdate:modelValue={(v: unknown) => {
@@ -108,7 +110,7 @@ export function renderFormField(
108
110
  <KInput
109
111
  class={['k-fp-input--error-wrap', error && 'k-fp-input--error', field.disabled && 'k-fp-input--disabled'].filter(Boolean)}
110
112
  modelValue={val as string}
111
- placeholder={field.placeholder ?? `请输入${field.label}`}
113
+ placeholder={field.placeholder ?? t.form.inputPlaceholder(field.label)}
112
114
  disabled={field.disabled}
113
115
  readonly={field.readonly}
114
116
  onUpdate:modelValue={(v: string) => { formData[field.param] = v; }}