@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.
- package/.vlaude/last-session-id +1 -1
- package/components/crudPage/KCrudPage.tsx +6 -4
- package/components/editableTable/KEditableTable.tsx +11 -9
- package/components/formPage/KApprovalDialog.tsx +24 -23
- package/components/formPage/KFormPage.tsx +10 -5
- package/components/formPage/KMasterDetailPage.tsx +8 -6
- package/components/layout/KSider.tsx +4 -1
- package/components/login/KLoginPage.tsx +8 -6
- package/components/searchTable/KSearchTable.tsx +6 -3
- package/components/upload/KFileList.tsx +16 -8
- package/components/upload/KImageUpload.tsx +4 -2
- package/components/upload/KUpload.tsx +4 -2
- package/composables/error/dispatchError.ts +9 -7
- package/composables/form/renderFormField.tsx +6 -4
- package/composables/form/useFormPage.ts +3 -1
- package/composables/page/types.ts +2 -0
- package/composables/page/useCrudPage.ts +1 -0
- package/composables/request/requestBuilder.ts +8 -5
- package/composables/request/transport/xhrTransport.ts +3 -1
- package/composables/request/types.ts +8 -5
- package/composables/request/upload.ts +4 -2
- package/composables/router/defineCrudRoutes.ts +5 -3
- package/dist/components/editableTable/KEditableTable.d.ts +5 -5
- package/dist/composables/page/types.d.ts +2 -0
- package/dist/crud.css +57 -57
- package/dist/crud.js +1742 -1164
- package/package.json +3 -3
- package/tsconfig.json +12 -12
package/.vlaude/last-session-id
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
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 ??
|
|
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"
|
|
216
|
-
<KButton type="danger" size="small" text=
|
|
217
|
-
<KButton plain size="small" text=
|
|
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"
|
|
271
|
+
<span class="k-et-empty-text">{t.value.editableTable.noData}</span>
|
|
270
272
|
{props.addable && (
|
|
271
|
-
<KButton type="primary" text=
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
86
|
-
const
|
|
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"
|
|
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=
|
|
129
|
+
<KButton text={ad.cancel} onClick={close} />
|
|
129
130
|
<KButton
|
|
130
|
-
type={
|
|
131
|
-
text={props.loading ?
|
|
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"
|
|
52
|
+
return <div class="k-fp-loading">{t.value.formPage.loading}</div>;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
const title = isEdit.value
|
|
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=
|
|
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 ?
|
|
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"
|
|
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
|
-
:
|
|
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=
|
|
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 ?
|
|
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
|
|
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
|
|
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="
|
|
99
|
-
<KButton text="
|
|
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)} · {
|
|
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:
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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 ??
|
|
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; }}
|