@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/dist/crud.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Comment, Fragment, Teleport, cloneVNode, computed, createApp, createTextVNode, createVNode, defineComponent, h, inject, isRef, mergeProps, nextTick, onBeforeUnmount, onMounted, onUnmounted, provide, reactive, ref, resolveComponent, shallowRef, toRef, triggerRef, watch } from "vue";
2
- import { QueryClient, VueQueryPlugin, useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
3
2
  import { createPinia, defineStore } from "pinia";
3
+ import { QueryClient, VueQueryPlugin, useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
4
4
  import { useRoute, useRouter } from "vue-router";
5
5
  //#region \0rolldown/runtime.js
6
6
  var __create = Object.create;
@@ -88,979 +88,594 @@ var KLayout_default = /* @__PURE__ */ defineComponent({
88
88
  }
89
89
  });
90
90
  //#endregion
91
- //#region components/layout/KSider.tsx
92
- /**
93
- * @description KSider 侧边栏组件,支持折叠动画和自定义 trigger
94
- * @author 阿怪
95
- * @date 2026/2/26
96
- * @version v0.0.1
97
- *
98
- * 江湖的业务千篇一律,复杂的代码好几百行。
99
- */
100
- var KSider_default = /* @__PURE__ */ defineComponent({
101
- name: "KSider",
102
- setup(_, ctx) {
103
- const collapsed = inject(LAYOUT_COLLAPSED_KEY);
104
- const toggle = inject(LAYOUT_TOGGLE_KEY);
105
- const drawerOpen = inject(LAYOUT_DRAWER_OPEN_KEY, ref(false));
106
- const siderWidth = inject(LAYOUT_SIDER_WIDTH_KEY, "240px");
107
- const collapsedWidth = inject(LAYOUT_COLLAPSED_WIDTH_KEY, "64px");
108
- const currentWidth = computed(() => {
109
- if (!collapsed) return siderWidth;
110
- return collapsed.value ? collapsedWidth : siderWidth;
111
- });
112
- const isCollapsed = computed(() => collapsed?.value ?? false);
113
- const handleTriggerClick = () => {
114
- toggle?.();
115
- };
116
- const defaultTrigger = () => createVNode("button", {
117
- "class": "k-crud-sider-trigger-btn",
118
- "onClick": handleTriggerClick,
119
- "title": isCollapsed.value ? "展开侧边栏" : "折叠侧边栏"
120
- }, [createVNode("span", { "class": ["k-crud-sider-trigger-icon", isCollapsed.value && "k-crud-sider-trigger-icon--collapsed"] }, ["‹"])]);
121
- return () => createVNode("aside", {
122
- "class": [
123
- "k-crud-sider",
124
- isCollapsed.value && "k-crud-sider--collapsed",
125
- drawerOpen.value && "k-crud-sider--drawer-open"
126
- ],
127
- "style": { width: currentWidth.value }
128
- }, [
129
- ctx.slots.logo && createVNode("div", { "class": "k-crud-sider-logo" }, [ctx.slots.logo()]),
130
- createVNode("div", { "class": "k-crud-sider-body" }, [ctx.slots.default?.()]),
131
- createVNode("div", { "class": "k-crud-sider-trigger" }, [ctx.slots.trigger ? ctx.slots.trigger({
132
- collapsed: isCollapsed.value,
133
- toggle
134
- }) : defaultTrigger()])
135
- ]);
91
+ //#region ../core/components/base/input/api.ts
92
+ var props$10 = {
93
+ type: {
94
+ type: String,
95
+ default: "text"
96
+ },
97
+ placeholder: {
98
+ type: String,
99
+ default: ""
100
+ },
101
+ modelValue: {
102
+ type: [String, Number],
103
+ default: ""
104
+ },
105
+ readonly: {
106
+ type: Boolean,
107
+ default: false
108
+ },
109
+ disabled: {
110
+ type: Boolean,
111
+ default: false
112
+ },
113
+ autofocus: {
114
+ type: Boolean,
115
+ default: false
116
+ },
117
+ clearable: {
118
+ type: Boolean,
119
+ default: false
120
+ },
121
+ size: {
122
+ type: String,
123
+ default: void 0
124
+ },
125
+ maxlength: {
126
+ type: Number,
127
+ default: void 0
128
+ },
129
+ showWordLimit: {
130
+ type: Boolean,
131
+ default: false
136
132
  }
137
- });
133
+ };
138
134
  //#endregion
139
- //#region components/layout/KHeader.tsx
140
- /**
141
- * @description KHeader 顶部栏组件
142
- * @author 阿怪
143
- * @date 2026/2/26
144
- * @version v0.0.1
145
- *
146
- * 江湖的业务千篇一律,复杂的代码好几百行。
147
- */
148
- var KHeader_default = /* @__PURE__ */ defineComponent({
149
- name: "KHeader",
150
- setup(_, ctx) {
151
- const toggleDrawer = inject(LAYOUT_TOGGLE_DRAWER_KEY, () => {});
152
- return () => createVNode("header", { "class": "k-crud-header" }, [
153
- createVNode("button", {
154
- "class": "k-crud-header-hamburger",
155
- "onClick": toggleDrawer
156
- }, [createVNode("span", { "class": "k-crud-header-hamburger-icon" }, null)]),
157
- createVNode("div", { "class": "k-crud-header-main" }, [ctx.slots.default?.()]),
158
- ctx.slots.extra && createVNode("div", { "class": "k-crud-header-extra" }, [ctx.slots.extra()])
159
- ]);
160
- }
161
- });
135
+ //#region ../core/components/base/input/useInput.ts
136
+ function useInput(props, ctx) {
137
+ const isInput = props.type !== "textarea";
138
+ const inputType = isInput ? "input" : "textarea";
139
+ const inputClass = isInput ? "m-input-inner" : "m-textarea-inner";
140
+ const rowInfo = isInput ? {} : { rows: 10 };
141
+ const baseProps = {
142
+ autofocus: props.autofocus,
143
+ value: props.modelValue,
144
+ placeholder: props.placeholder,
145
+ disabled: props.disabled,
146
+ type: props.type,
147
+ readOnly: props.readonly,
148
+ maxLength: props.maxlength
149
+ };
150
+ const onInput = (e) => {
151
+ ctx.emit("update:modelValue", e.target.value);
152
+ ctx.emit("input", e.target.value);
153
+ };
154
+ const onFocus = (e) => {
155
+ ctx.emit("focus", e);
156
+ };
157
+ const onBlur = (e) => {
158
+ ctx.emit("blur", e);
159
+ };
160
+ const onClear = () => {
161
+ ctx.emit("update:modelValue", "");
162
+ ctx.emit("input", "");
163
+ ctx.emit("clear");
164
+ };
165
+ return {
166
+ baseProps,
167
+ inputType,
168
+ inputClass,
169
+ rowInfo,
170
+ onInput,
171
+ onFocus,
172
+ onBlur,
173
+ onClear
174
+ };
175
+ }
162
176
  //#endregion
163
- //#region components/layout/KContent.tsx
177
+ //#region ../core/components/base/input/index.ts
164
178
  /**
165
- * @description KContent 主内容区组件
179
+ * @description
166
180
  * @author 阿怪
167
- * @date 2026/2/26
168
- * @version v0.0.1
181
+ * @date 2024/11/29 10:24
182
+ * @version v1.0.0
169
183
  *
170
184
  * 江湖的业务千篇一律,复杂的代码好几百行。
171
185
  */
172
- var KContent_default = /* @__PURE__ */ defineComponent({
173
- name: "KContent",
174
- setup(_, ctx) {
175
- return () => createVNode("main", { "class": "k-crud-content" }, [ctx.slots.default?.()]);
176
- }
177
- });
186
+ var InputCore = {
187
+ props: props$10,
188
+ useInput
189
+ };
178
190
  //#endregion
179
- //#region ../core/components/template/table/api.ts
180
- var props$10 = {
181
- data: {
182
- type: Array,
183
- default: () => []
184
- },
185
- height: {
191
+ //#region ../core/components/base/button/api.ts
192
+ var props$9 = {
193
+ text: {
186
194
  type: String,
187
- default: null
195
+ default: ""
188
196
  },
189
- paramClass: {
197
+ link: {
198
+ type: Boolean,
199
+ default: false
200
+ },
201
+ disabled: {
190
202
  type: Boolean,
191
203
  default: false
192
204
  },
193
205
  loading: {
194
206
  type: Boolean,
195
207
  default: false
208
+ },
209
+ plain: {
210
+ type: Boolean,
211
+ default: false
212
+ },
213
+ type: {
214
+ type: String,
215
+ default: "default",
216
+ enum: [
217
+ "default",
218
+ "primary",
219
+ "success",
220
+ "warning",
221
+ "danger",
222
+ "info"
223
+ ]
224
+ },
225
+ size: {
226
+ type: String,
227
+ default: void 0,
228
+ enum: [
229
+ "large",
230
+ "medium",
231
+ "small"
232
+ ]
196
233
  }
197
234
  };
198
235
  //#endregion
199
- //#region ../core/components/template/table/useTable.ts
236
+ //#region ../core/components/base/button/useButton.ts
237
+ function useButton(props, { slots }) {
238
+ const domType = props.link ? "a" : "button";
239
+ const slot = slots.default?.() ?? props.text;
240
+ const isDisabled = props.disabled || props.loading;
241
+ return {
242
+ domType,
243
+ slot,
244
+ domProps: {
245
+ class: [
246
+ "m-button",
247
+ { "m-button-disabled": isDisabled },
248
+ { "m-button-loading": props.loading },
249
+ { "m-button-plain": props.plain },
250
+ `m-button-${props.type ?? "default"}`,
251
+ `m-button-${props.size ?? "medium"}`
252
+ ],
253
+ disabled: isDisabled
254
+ },
255
+ isDisabled
256
+ };
257
+ }
258
+ //#endregion
259
+ //#region ../core/components/base/button/index.ts
200
260
  /**
201
- * @description core table hook
261
+ * @description
202
262
  * @author 阿怪
203
- * @date 2026/2/25
263
+ * @date 2024/12/16 09:44
204
264
  * @version v1.0.0
205
265
  *
206
266
  * 江湖的业务千篇一律,复杂的代码好几百行。
207
267
  */
208
- function useTable() {
209
- const error = (msg) => console.warn(`[水墨UI表格组件] ${msg}`);
210
- const initTable = (renders, columns, data) => {
211
- const tbodyTrList = [];
212
- data.forEach(() => {
213
- tbodyTrList.push([]);
214
- });
215
- /** 从 data[i] 中安全取值 */
216
- const getData = (i, param) => {
217
- if (data[i] == null) return "";
218
- return data[i][param] ?? "";
219
- };
220
- /** 向每行的 td 列表中追加一列 */
221
- const pushTd = (param, bodySlot, style) => {
222
- if (!param && !bodySlot) {
223
- error("param is undefined, column without param or slot will be ignored!");
224
- return;
268
+ var ButtonCore = {
269
+ props: props$9,
270
+ useButton
271
+ };
272
+ //#endregion
273
+ //#region ../core/components/base/select/api.ts
274
+ var props$8 = {
275
+ modelValue: {
276
+ type: void 0,
277
+ default: ""
278
+ },
279
+ options: {
280
+ type: Array,
281
+ default: () => []
282
+ },
283
+ inputParam: {
284
+ type: String,
285
+ default: void 0
286
+ },
287
+ optionParam: {
288
+ type: String,
289
+ default: void 0
290
+ },
291
+ valueParam: {
292
+ type: String,
293
+ default: void 0
294
+ },
295
+ readonly: {
296
+ type: Boolean,
297
+ default: true
298
+ },
299
+ disabled: {
300
+ type: Boolean,
301
+ default: false
302
+ },
303
+ placeholder: {
304
+ type: String,
305
+ default: void 0
306
+ },
307
+ toMatch: {
308
+ type: Function,
309
+ default: void 0
310
+ },
311
+ multiple: {
312
+ type: Boolean,
313
+ default: false
314
+ },
315
+ checkbox: {
316
+ type: Boolean,
317
+ default: true
318
+ },
319
+ filterable: {
320
+ type: Boolean,
321
+ default: false
322
+ },
323
+ filter: {
324
+ type: Function,
325
+ default: void 0
326
+ },
327
+ optionsH: {
328
+ type: [Number, String],
329
+ default: void 0
330
+ },
331
+ needFetch: {
332
+ type: Boolean,
333
+ default: false
334
+ },
335
+ fetch: {
336
+ type: Function,
337
+ default: void 0
338
+ },
339
+ clearable: {
340
+ type: Boolean,
341
+ default: false
342
+ },
343
+ size: {
344
+ type: String,
345
+ default: void 0
346
+ },
347
+ loading: {
348
+ type: Boolean,
349
+ default: false
350
+ }
351
+ };
352
+ //#endregion
353
+ //#region ../core/components/base/select/useSelectTools.ts
354
+ function useSelectTools(props) {
355
+ /** 从 option 中取指定 key 的值 */
356
+ const getInfoWithKey = (option, key) => {
357
+ const paramKey = props[key];
358
+ if (!paramKey) {
359
+ if (option && typeof option === "object") {
360
+ if ("label" in option) return option.label;
361
+ if ("name" in option) return option.name;
362
+ if ("text" in option) return option.text;
225
363
  }
226
- tbodyTrList.forEach((t, i) => {
227
- t.push(renders.tbodyTr({
228
- data: param ? getData(i, param) : "",
229
- style,
230
- param: param ?? "",
231
- slot: bodySlot,
232
- slotInfo: {
233
- data: data[i],
234
- index: i
235
- }
236
- }));
237
- });
238
- };
239
- /** 将列宽 prop 转换为 style 对象 */
240
- const getStyle = (options) => {
241
- if (!options || !options.width) return {};
242
- const numberWidth = Number(options.width);
243
- if (!isNaN(numberWidth)) return { width: numberWidth + "px" };
244
- return { width: options.width };
245
- };
246
- /**
247
- * 初始化表头,同时将每列对应的 td 推入 tbodyTrList
248
- */
249
- const initTHead = () => {
250
- const ths = (columns ?? []).filter((column) => {
251
- if (!column.props) {
252
- error("column.props is undefined, column without param will be ignored!");
253
- return false;
254
- }
255
- return true;
256
- }).map((column) => {
257
- const slots = renders.initSlot(column);
258
- let bodySlot;
259
- let headSlot;
260
- const style = getStyle(column.props);
261
- if (slots) {
262
- bodySlot = slots.body;
263
- headSlot = slots.head;
264
- }
265
- pushTd(column.props.param, bodySlot, style);
266
- return renders.theadTh({
267
- label: column.props.label,
268
- param: column.props.param,
269
- slot: headSlot,
270
- style
271
- });
272
- });
273
- return renders.thead(ths);
274
- };
275
- return {
276
- thead: initTHead(),
277
- tbody: tbodyTrList.length > 0 ? renders.tbody(tbodyTrList.map((tds, i) => renders.tbodyTrs(tds, i))) : renders.empty
278
- };
364
+ return option;
365
+ }
366
+ try {
367
+ return option[paramKey];
368
+ } catch {
369
+ return option;
370
+ }
371
+ };
372
+ /** 输入框显示值 */
373
+ const getInputValue = (option) => {
374
+ return String(getInfoWithKey(option, "inputParam") ?? "");
375
+ };
376
+ /** 下拉列表显示值 */
377
+ const getOptionValue = (option) => {
378
+ return String(getInfoWithKey(option, "optionParam") ?? "");
379
+ };
380
+ /** modelValue 绑定值 */
381
+ const getModelValue = (option) => {
382
+ if (props.valueParam) return getInfoWithKey(option, "valueParam");
383
+ if (option && typeof option === "object" && "value" in option) return option.value;
384
+ return option;
279
385
  };
280
386
  return {
281
- initTable,
282
- error
387
+ getInfoWithKey,
388
+ getInputValue,
389
+ getOptionValue,
390
+ getModelValue
283
391
  };
284
392
  }
285
393
  //#endregion
286
- //#region ../core/components/template/table/index.ts
394
+ //#region ../core/components/base/select/useSelect.ts
287
395
  /**
288
- * @description table core 导出
396
+ * @description select 核心 composable
289
397
  * @author 阿怪
290
- * @date 2026/2/25
291
- * @version v1.0.0
398
+ * @date 2026/2/25 14:40
399
+ * @version v1.0.1
292
400
  *
293
401
  * 江湖的业务千篇一律,复杂的代码好几百行。
294
- */
295
- var TableCore = {
296
- props: props$10,
297
- useTable
298
- };
299
- //#endregion
300
- //#region ../core/compositions/common/testAnchor.ts
301
- /**
302
- * @description Test anchor system: provide/inject keys, types, and kDefineComponent wrapper
303
- * @author kine-design
304
- * @date 2026/4/28
305
- * @version v1.0.0
306
402
  *
307
- * Renders business-semantic `data-k` attributes on DOM elements when testAnchor is enabled,
308
- * allowing E2E tests to target components by stable identifiers.
309
- */
310
- /** Global switch provided by KConfigProvider to enable data-k rendering */
311
- var K_TEST_ANCHOR_KEY = Symbol("k-test-anchor");
312
- /** Field identity provided by KFormItem so child inputs inherit field name */
313
- var K_FIELD_KEY = Symbol("k-field");
314
- /**
315
- * Extract text content from a VNode tree (for slotText mode).
316
- * Walks children recursively, concatenating string segments.
317
- */
318
- function extractSlotText(nodes) {
319
- if (!nodes) return "";
320
- let text = "";
321
- for (const node of nodes) if (typeof node.children === "string") text += node.children;
322
- else if (Array.isArray(node.children)) text += extractSlotText(node.children);
323
- return text.trim();
324
- }
325
- /**
326
- * Resolve the data-k value for a component instance.
327
- * Returns undefined when testAnchor is off or no identity can be derived.
328
- */
329
- function resolveDataK(config, props, attrs, fieldFromParent, slotNodes) {
330
- const override = attrs[`k-${config.type}`];
331
- if (typeof override === "string" && override) return `${config.type}:${override}`;
332
- if (config.prop) {
333
- const val = props[config.prop];
334
- if (typeof val === "string" && val) return `${config.type}:${val}`;
335
- }
336
- if (config.slotText && slotNodes) {
337
- const text = extractSlotText(slotNodes);
338
- if (text) return `${config.type}:${text}`;
339
- }
340
- if (config.slotText && typeof props.text === "string" && props.text) return `${config.type}:${props.text}`;
341
- if (config.type === "field" && !config.prop && fieldFromParent) return `${config.type}:${fieldFromParent}`;
342
- }
343
- /**
344
- * Inject data-k attribute into a VNode.
345
- * Uses cloneVNode to avoid mutating the original.
403
+ * computed + ref 替代原 class 继承方案
404
+ * 单选/多选通过 props.multiple 分支处理
405
+ * fetch 通过 IntersectionObserver 观察最后一个选项实现
406
+ * v1.0.1 新增 filterable 支持:开启后 inputReadonly false,可输入过滤 阿怪 2026/2/27
346
407
  */
347
- function injectDataK(vnode, dataK) {
348
- return cloneVNode(vnode, { "data-k": dataK });
349
- }
350
- /**
351
- * Wrapper around Vue's defineComponent that automatically injects `data-k`
352
- * test anchor attributes when testAnchor is enabled via KConfigProvider.
353
- *
354
- * Usage:
355
- * ```tsx
356
- * export default kDefineComponent((_props, ctx) => {
357
- * return () => <div class="k-form-item">...</div>;
358
- * }, {
359
- * name: 'KFormItem',
360
- * props: FormCore.formItemProps,
361
- * kAnchor: { type: 'field', prop: 'prop' },
362
- * });
363
- * ```
364
- */
365
- function kDefineComponent(setup, options) {
366
- const { kAnchor, ...defineOptions } = options;
367
- return defineComponent((rawProps, ctx) => {
368
- const render = setup(rawProps, ctx);
369
- if (!kAnchor) return render;
370
- const testAnchor = inject(K_TEST_ANCHOR_KEY, void 0);
371
- const parentField = kAnchor.type === "field" && !kAnchor.prop ? inject(K_FIELD_KEY, void 0) : void 0;
372
- return () => {
373
- const vnode = render();
374
- if (!testAnchor?.value) return vnode;
375
- const props = rawProps;
376
- const slotNodes = kAnchor.slotText ? ctx.slots.default?.() ?? void 0 : void 0;
377
- const dataK = resolveDataK(kAnchor, props, ctx.attrs, parentField?.value, slotNodes);
378
- if (!dataK) return vnode;
379
- if (vnode === null || vnode === void 0) return vnode;
380
- if (Array.isArray(vnode)) {
381
- if (vnode.length === 0) return vnode;
382
- const first = vnode[0];
383
- if (first && typeof first === "object") return [injectDataK(first, dataK), ...vnode.slice(1)];
384
- return vnode;
408
+ function useSelect$1(props, ctx) {
409
+ const tools = useSelectTools(props);
410
+ const optionsRef = toRef(() => props.options ?? []);
411
+ const modelValueRef = toRef(() => props.modelValue);
412
+ const multipleRef = toRef(() => props.multiple ?? false);
413
+ /**
414
+ * 输入框是否只读:
415
+ * - filterable=true 时强制可编辑(忽略 readonly prop)
416
+ * - 否则沿用 props.readonly(默认 true)
417
+ */
418
+ const inputReadonly = toRef(() => props.filterable ? false : props.readonly ?? true);
419
+ const inputValue = ref("");
420
+ const isOpen = ref(false);
421
+ const fetchLoading = ref(false);
422
+ /** 多选时记录选中的索引集合 */
423
+ const selectedIndices = ref(/* @__PURE__ */ new Set());
424
+ /** 判断某个 option 是否选中 */
425
+ const isSelected = (option, index) => {
426
+ if (multipleRef.value) return selectedIndices.value.has(index);
427
+ if (props.toMatch) return props.toMatch(option, modelValueRef.value);
428
+ return modelValueRef.value === tools.getModelValue(option);
429
+ };
430
+ /** 全量选项(带 isSelected 标记) */
431
+ const allOptions = computed(() => {
432
+ return optionsRef.value.map((o, i) => ({
433
+ value: o,
434
+ index: i,
435
+ isSelected: isSelected(o, i)
436
+ }));
437
+ });
438
+ /** 过滤后的显示选项 */
439
+ const displayOptions = computed(() => {
440
+ return allOptions.value.filter((o) => {
441
+ if (inputReadonly.value) return true;
442
+ if (!inputValue.value) return true;
443
+ if (props.filter) return props.filter(o.value, inputValue.value);
444
+ return tools.getOptionValue(o.value).toLowerCase().includes(inputValue.value.toLowerCase());
445
+ });
446
+ });
447
+ /** 多选已选标签 */
448
+ const selectedTags = computed(() => {
449
+ if (!multipleRef.value) return [];
450
+ return allOptions.value.filter((o) => o.isSelected);
451
+ });
452
+ const syncInputFromModelValue = () => {
453
+ if (multipleRef.value) {
454
+ inputValue.value = "";
455
+ return;
456
+ }
457
+ const mv = modelValueRef.value;
458
+ if (mv == null || mv === "") {
459
+ inputValue.value = "";
460
+ return;
461
+ }
462
+ const found = optionsRef.value.find((o) => {
463
+ if (props.toMatch) return props.toMatch(o, mv);
464
+ return tools.getModelValue(o) === mv;
465
+ });
466
+ inputValue.value = found ? tools.getInputValue(found) : "";
467
+ };
468
+ /** 初始化多选选中状态 */
469
+ const syncSelectedFromModelValue = () => {
470
+ if (!multipleRef.value) return;
471
+ const mv = modelValueRef.value;
472
+ const newSet = /* @__PURE__ */ new Set();
473
+ if (Array.isArray(mv)) optionsRef.value.forEach((o, i) => {
474
+ const val = tools.getModelValue(o);
475
+ if (mv.includes(val)) newSet.add(i);
476
+ if (props.toMatch && mv.some((v) => props.toMatch(o, v))) newSet.add(i);
477
+ });
478
+ selectedIndices.value = newSet;
479
+ };
480
+ syncSelectedFromModelValue();
481
+ syncInputFromModelValue();
482
+ /** 点击选项 */
483
+ const onSelect = (index) => {
484
+ const option = optionsRef.value[index];
485
+ if (!option) return;
486
+ if (option.disabled) return;
487
+ if (multipleRef.value) {
488
+ const newSet = new Set(selectedIndices.value);
489
+ if (newSet.has(index)) newSet.delete(index);
490
+ else newSet.add(index);
491
+ selectedIndices.value = newSet;
492
+ inputValue.value = "";
493
+ const newModelValue = optionsRef.value.filter((_, i) => newSet.has(i)).map((o) => tools.getModelValue(o));
494
+ ctx.emit("update:modelValue", newModelValue);
495
+ ctx.emit("select", option);
496
+ } else {
497
+ inputValue.value = tools.getInputValue(option);
498
+ ctx.emit("update:modelValue", tools.getModelValue(option));
499
+ ctx.emit("select", option);
500
+ isOpen.value = false;
501
+ }
502
+ };
503
+ /** 删除多选标签 */
504
+ const onDeleteTag = (index) => {
505
+ const newSet = new Set(selectedIndices.value);
506
+ newSet.delete(index);
507
+ selectedIndices.value = newSet;
508
+ const newModelValue = optionsRef.value.filter((_, i) => newSet.has(i)).map((o) => tools.getModelValue(o));
509
+ ctx.emit("update:modelValue", newModelValue);
510
+ };
511
+ /** 输入事件 */
512
+ const onInput = () => {
513
+ ctx.emit("input", inputValue.value);
514
+ };
515
+ /** 聚焦 */
516
+ const onFocus = (e) => {
517
+ if (inputReadonly.value) return;
518
+ ctx.emit("focus", e, inputValue.value);
519
+ };
520
+ /** 失焦 */
521
+ const onBlur = (e) => {
522
+ if (inputReadonly.value) return;
523
+ ctx.emit("blur", e, inputValue.value);
524
+ if (!multipleRef.value) {
525
+ const matched = displayOptions.value.find((o) => o.isSelected);
526
+ if (matched && inputValue.value === tools.getInputValue(matched.value)) return;
527
+ if (!inputValue.value) ctx.emit("update:modelValue", void 0);
528
+ }
529
+ };
530
+ const lastOptionRef = ref(null);
531
+ const optionsContainerRef = ref(null);
532
+ let fetchObserver;
533
+ const cleanupFetchObserver = () => {
534
+ if (fetchObserver) {
535
+ fetchObserver.disconnect();
536
+ fetchObserver = void 0;
537
+ }
538
+ };
539
+ /** 更新 fetch observer:观察最后一个选项元素 */
540
+ const updateFetchObserver = () => {
541
+ cleanupFetchObserver();
542
+ if (!props.needFetch || !lastOptionRef.value || !optionsContainerRef.value) return;
543
+ fetchObserver = new IntersectionObserver(async (entries) => {
544
+ if (entries[0]?.isIntersecting && props.needFetch) {
545
+ fetchLoading.value = true;
546
+ await props.fetch?.();
547
+ fetchLoading.value = false;
385
548
  }
386
- return injectDataK(vnode, dataK);
387
- };
388
- }, defineOptions);
549
+ }, {
550
+ root: optionsContainerRef.value,
551
+ threshold: 1
552
+ });
553
+ fetchObserver.observe(lastOptionRef.value);
554
+ };
555
+ watch(modelValueRef, () => {
556
+ syncSelectedFromModelValue();
557
+ syncInputFromModelValue();
558
+ });
559
+ watch(optionsRef, () => {
560
+ syncSelectedFromModelValue();
561
+ syncInputFromModelValue();
562
+ }, { deep: true });
563
+ watch(lastOptionRef, () => {
564
+ updateFetchObserver();
565
+ });
566
+ onBeforeUnmount(cleanupFetchObserver);
567
+ return {
568
+ inputValue,
569
+ isOpen,
570
+ fetchLoading,
571
+ inputReadonly,
572
+ displayOptions,
573
+ selectedTags,
574
+ lastOptionRef,
575
+ optionsContainerRef,
576
+ onSelect,
577
+ onDeleteTag,
578
+ onInput,
579
+ onFocus,
580
+ onBlur,
581
+ tools,
582
+ updateFetchObserver
583
+ };
389
584
  }
390
585
  //#endregion
391
- //#region ../ui/components/table/KTable.tsx
586
+ //#region ../core/components/base/select/index.ts
392
587
  /**
393
- * @description kine-ui table 组件
588
+ * @description select core 导出
394
589
  * @author 阿怪
395
- * @date 2026/2/26
396
- * @version v1.1.0
590
+ * @date 2026/2/25 14:50
591
+ * @version v2.0.0
397
592
  *
398
593
  * 江湖的业务千篇一律,复杂的代码好几百行。
594
+ */
595
+ var SelectCore = {
596
+ props: props$8,
597
+ useSelect: useSelect$1
598
+ };
599
+ //#endregion
600
+ //#region ../core/components/base/tag/index.ts
601
+ /**
602
+ * @description tag core
603
+ * @author 阿怪
604
+ * @date 2025/02/25 00:00
605
+ * @version v1.0.0
399
606
  *
400
- * v1.1.0 migrate to kDefineComponent for data-k test anchor 2026/4/28
607
+ * 江湖的业务千篇一律,复杂的代码好几百行。
401
608
  */
402
- var { props: props$9 } = TableCore;
403
- var KTable_default = kDefineComponent((_props, { slots }) => {
404
- const p = _props;
405
- const { initTable } = useTable();
406
- const testAnchor = inject(K_TEST_ANCHOR_KEY, void 0);
407
- return () => {
408
- const columns = [];
409
- (slots.default?.() ?? []).forEach((s) => {
410
- if (s.type === Fragment) {
411
- s.children?.forEach((c) => columns.push(c));
412
- return;
413
- }
414
- if (s.type === Comment) return;
415
- if (typeof s.type === "object" && s.type.name === "KTableColumn") columns.push(s);
416
- });
417
- const style = p.height ? { height: p.height } : void 0;
418
- const { thead, tbody } = initTable({
419
- empty: createVNode("tbody", { "class": "m-table-empty k-table-empty" }, [createVNode("tr", null, [createVNode("th", { "colspan": columns.length }, [slots.empty?.() ?? "暂无数据"])])]),
420
- tbodyTr: ({ data, param, slot, style: cellStyle, slotInfo }) => createVNode("td", {
421
- "style": cellStyle,
422
- "data-k": testAnchor?.value && param ? `col:${param}` : void 0,
423
- "class": [
424
- "m-td",
425
- "k-td",
426
- p.paramClass ? `m-td-${param}` : null,
427
- p.paramClass ? `k-td-${param}` : null
428
- ]
429
- }, [slot ? slot({
430
- data: slotInfo?.data,
431
- row: slotInfo?.data,
432
- index: slotInfo?.index
433
- }) : data]),
434
- theadTh: ({ label, param, slot, style: cellStyle }) => createVNode("th", {
435
- "class": [
436
- "m-th",
437
- "k-th",
438
- p.paramClass ? `m-th-${param}` : null,
439
- p.paramClass ? `k-th-${param}` : null
440
- ],
441
- "style": cellStyle
442
- }, [slot ? slot() : label]),
443
- thead: (ths) => createVNode("thead", { "class": "m-thead k-thead" }, [createVNode("tr", { "class": "m-tr k-tr" }, [ths])]),
444
- tbody: (trs) => createVNode("tbody", { "class": "m-tbody k-tbody" }, [trs]),
445
- tbodyTrs: (tds, i) => createVNode("tr", {
446
- "class": "m-tr k-tr",
447
- "key": i
448
- }, [...tds]),
449
- initSlot: (tableColumn) => {
450
- const children = tableColumn.children;
451
- if (!children || Array.isArray(children) || typeof children !== "object") return;
452
- return {
453
- body: children.default,
454
- head: children.head
455
- };
456
- }
457
- }, columns, p.data);
458
- return createVNode("div", {
459
- "class": [
460
- "m-table",
461
- "k-table",
462
- p.loading && "k-table-loading-wrap"
463
- ],
464
- "style": style
465
- }, [p.loading && createVNode("div", { "class": "k-table-loading-overlay" }, [createVNode("div", { "class": "k-table-loading-spinner" }, null)]), createVNode("table", { "class": "m-table-inner k-table-inner" }, [thead, tbody])]);
466
- };
467
- }, {
468
- name: "KTable",
469
- props: props$9,
470
- kAnchor: {
471
- type: "table",
472
- prop: "name"
473
- }
474
- });
475
- //#endregion
476
- //#region ../core/components/base/input/api.ts
477
- var props$8 = {
609
+ var TagCore = { props: {
478
610
  type: {
479
611
  type: String,
480
- default: "text"
481
- },
482
- placeholder: {
483
- type: String,
484
- default: ""
485
- },
486
- modelValue: {
487
- type: [String, Number],
488
- default: ""
489
- },
490
- readonly: {
491
- type: Boolean,
492
- default: false
493
- },
494
- disabled: {
495
- type: Boolean,
496
- default: false
497
- },
498
- autofocus: {
499
- type: Boolean,
500
- default: false
501
- },
502
- clearable: {
503
- type: Boolean,
504
- default: false
612
+ default: "default"
505
613
  },
506
614
  size: {
507
615
  type: String,
508
616
  default: void 0
509
617
  },
510
- maxlength: {
511
- type: Number,
512
- default: void 0
513
- },
514
- showWordLimit: {
515
- type: Boolean,
516
- default: false
517
- }
518
- };
519
- //#endregion
520
- //#region ../core/components/base/input/useInput.ts
521
- function useInput(props, ctx) {
522
- const isInput = props.type !== "textarea";
523
- const inputType = isInput ? "input" : "textarea";
524
- const inputClass = isInput ? "m-input-inner" : "m-textarea-inner";
525
- const rowInfo = isInput ? {} : { rows: 10 };
526
- const baseProps = {
527
- autofocus: props.autofocus,
528
- value: props.modelValue,
529
- placeholder: props.placeholder,
530
- disabled: props.disabled,
531
- type: props.type,
532
- readOnly: props.readonly,
533
- maxLength: props.maxlength
534
- };
535
- const onInput = (e) => {
536
- ctx.emit("update:modelValue", e.target.value);
537
- ctx.emit("input", e.target.value);
538
- };
539
- const onFocus = (e) => {
540
- ctx.emit("focus", e);
541
- };
542
- const onBlur = (e) => {
543
- ctx.emit("blur", e);
544
- };
545
- const onClear = () => {
546
- ctx.emit("update:modelValue", "");
547
- ctx.emit("input", "");
548
- ctx.emit("clear");
549
- };
550
- return {
551
- baseProps,
552
- inputType,
553
- inputClass,
554
- rowInfo,
555
- onInput,
556
- onFocus,
557
- onBlur,
558
- onClear
559
- };
560
- }
561
- //#endregion
562
- //#region ../core/components/base/input/index.ts
563
- /**
564
- * @description
565
- * @author 阿怪
566
- * @date 2024/11/29 10:24
567
- * @version v1.0.0
568
- *
569
- * 江湖的业务千篇一律,复杂的代码好几百行。
570
- */
571
- var InputCore = {
572
- props: props$8,
573
- useInput
574
- };
575
- //#endregion
576
- //#region ../core/components/base/button/api.ts
577
- var props$7 = {
578
- text: {
579
- type: String,
580
- default: ""
581
- },
582
- link: {
618
+ closable: {
583
619
  type: Boolean,
584
620
  default: false
585
621
  },
586
622
  disabled: {
587
623
  type: Boolean,
588
624
  default: false
589
- },
590
- loading: {
591
- type: Boolean,
592
- default: false
593
- },
594
- plain: {
595
- type: Boolean,
596
- default: false
597
- },
598
- type: {
599
- type: String,
600
- default: "default",
601
- enum: [
602
- "default",
603
- "primary",
604
- "success",
605
- "warning",
606
- "danger",
607
- "info"
608
- ]
609
- },
610
- size: {
611
- type: String,
612
- default: void 0,
613
- enum: [
614
- "large",
615
- "medium",
616
- "small"
617
- ]
618
625
  }
619
- };
620
- //#endregion
621
- //#region ../core/components/base/button/useButton.ts
622
- function useButton(props, { slots }) {
623
- const domType = props.link ? "a" : "button";
624
- const slot = slots.default?.() ?? props.text;
625
- const isDisabled = props.disabled || props.loading;
626
- return {
627
- domType,
628
- slot,
629
- domProps: {
630
- class: [
631
- "m-button",
632
- { "m-button-disabled": isDisabled },
633
- { "m-button-loading": props.loading },
634
- { "m-button-plain": props.plain },
635
- `m-button-${props.type ?? "default"}`,
636
- `m-button-${props.size ?? "medium"}`
637
- ],
638
- disabled: isDisabled
639
- },
640
- isDisabled
641
- };
642
- }
643
- //#endregion
644
- //#region ../core/components/base/button/index.ts
645
- /**
646
- * @description
647
- * @author 阿怪
648
- * @date 2024/12/16 09:44
649
- * @version v1.0.0
650
- *
651
- * 江湖的业务千篇一律,复杂的代码好几百行。
652
- */
653
- var ButtonCore = {
654
- props: props$7,
655
- useButton
656
- };
657
- //#endregion
658
- //#region ../core/components/base/select/api.ts
659
- var props$6 = {
660
- modelValue: {
661
- type: void 0,
662
- default: ""
663
- },
664
- options: {
665
- type: Array,
666
- default: () => []
667
- },
668
- inputParam: {
669
- type: String,
670
- default: void 0
671
- },
672
- optionParam: {
673
- type: String,
674
- default: void 0
675
- },
676
- valueParam: {
677
- type: String,
678
- default: void 0
679
- },
680
- readonly: {
681
- type: Boolean,
682
- default: true
683
- },
684
- disabled: {
685
- type: Boolean,
686
- default: false
687
- },
688
- placeholder: {
689
- type: String,
690
- default: "请选择..."
691
- },
692
- toMatch: {
693
- type: Function,
694
- default: void 0
695
- },
696
- multiple: {
697
- type: Boolean,
698
- default: false
699
- },
700
- checkbox: {
701
- type: Boolean,
702
- default: true
703
- },
704
- filterable: {
705
- type: Boolean,
706
- default: false
707
- },
708
- filter: {
709
- type: Function,
710
- default: void 0
711
- },
712
- optionsH: {
713
- type: [Number, String],
714
- default: void 0
715
- },
716
- needFetch: {
717
- type: Boolean,
718
- default: false
719
- },
720
- fetch: {
721
- type: Function,
722
- default: void 0
723
- },
724
- clearable: {
725
- type: Boolean,
726
- default: false
727
- },
728
- size: {
729
- type: String,
730
- default: void 0
731
- },
732
- loading: {
733
- type: Boolean,
734
- default: false
735
- }
736
- };
737
- //#endregion
738
- //#region ../core/components/base/select/useSelectTools.ts
739
- function useSelectTools(props) {
740
- /** 从 option 中取指定 key 的值 */
741
- const getInfoWithKey = (option, key) => {
742
- const paramKey = props[key];
743
- if (!paramKey) {
744
- if (option && typeof option === "object") {
745
- if ("label" in option) return option.label;
746
- if ("name" in option) return option.name;
747
- if ("text" in option) return option.text;
626
+ } };
627
+ (/* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
628
+ (function(t, e) {
629
+ "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).interact = e();
630
+ })(exports, (function() {
631
+ "use strict";
632
+ function t(t, e) {
633
+ var n = Object.keys(t);
634
+ if (Object.getOwnPropertySymbols) {
635
+ var r = Object.getOwnPropertySymbols(t);
636
+ e && (r = r.filter((function(e) {
637
+ return Object.getOwnPropertyDescriptor(t, e).enumerable;
638
+ }))), n.push.apply(n, r);
748
639
  }
749
- return option;
640
+ return n;
750
641
  }
751
- try {
752
- return option[paramKey];
753
- } catch {
754
- return option;
642
+ function e(e) {
643
+ for (var n = 1; n < arguments.length; n++) {
644
+ var r = null != arguments[n] ? arguments[n] : {};
645
+ n % 2 ? t(Object(r), !0).forEach((function(t) {
646
+ a(e, t, r[t]);
647
+ })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(r)) : t(Object(r)).forEach((function(t) {
648
+ Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(r, t));
649
+ }));
650
+ }
651
+ return e;
755
652
  }
756
- };
757
- /** 输入框显示值 */
758
- const getInputValue = (option) => {
759
- return String(getInfoWithKey(option, "inputParam") ?? "");
760
- };
761
- /** 下拉列表显示值 */
762
- const getOptionValue = (option) => {
763
- return String(getInfoWithKey(option, "optionParam") ?? "");
764
- };
765
- /** modelValue 绑定值 */
766
- const getModelValue = (option) => {
767
- if (props.valueParam) return getInfoWithKey(option, "valueParam");
768
- if (option && typeof option === "object" && "value" in option) return option.value;
769
- return option;
770
- };
771
- return {
772
- getInfoWithKey,
773
- getInputValue,
774
- getOptionValue,
775
- getModelValue
776
- };
777
- }
778
- //#endregion
779
- //#region ../core/components/base/select/useSelect.ts
780
- /**
781
- * @description select 核心 composable
782
- * @author 阿怪
783
- * @date 2026/2/25 14:40
784
- * @version v1.0.1
785
- *
786
- * 江湖的业务千篇一律,复杂的代码好几百行。
787
- *
788
- * 用 computed + ref 替代原 class 继承方案
789
- * 单选/多选通过 props.multiple 分支处理
790
- * fetch 通过 IntersectionObserver 观察最后一个选项实现
791
- * v1.0.1 新增 filterable 支持:开启后 inputReadonly 为 false,可输入过滤 阿怪 2026/2/27
792
- */
793
- function useSelect$1(props, ctx) {
794
- const tools = useSelectTools(props);
795
- const optionsRef = toRef(() => props.options ?? []);
796
- const modelValueRef = toRef(() => props.modelValue);
797
- const multipleRef = toRef(() => props.multiple ?? false);
798
- /**
799
- * 输入框是否只读:
800
- * - filterable=true 时强制可编辑(忽略 readonly prop)
801
- * - 否则沿用 props.readonly(默认 true)
802
- */
803
- const inputReadonly = toRef(() => props.filterable ? false : props.readonly ?? true);
804
- const inputValue = ref("");
805
- const isOpen = ref(false);
806
- const fetchLoading = ref(false);
807
- /** 多选时记录选中的索引集合 */
808
- const selectedIndices = ref(/* @__PURE__ */ new Set());
809
- /** 判断某个 option 是否选中 */
810
- const isSelected = (option, index) => {
811
- if (multipleRef.value) return selectedIndices.value.has(index);
812
- if (props.toMatch) return props.toMatch(option, modelValueRef.value);
813
- return modelValueRef.value === tools.getModelValue(option);
814
- };
815
- /** 全量选项(带 isSelected 标记) */
816
- const allOptions = computed(() => {
817
- return optionsRef.value.map((o, i) => ({
818
- value: o,
819
- index: i,
820
- isSelected: isSelected(o, i)
821
- }));
822
- });
823
- /** 过滤后的显示选项 */
824
- const displayOptions = computed(() => {
825
- return allOptions.value.filter((o) => {
826
- if (inputReadonly.value) return true;
827
- if (!inputValue.value) return true;
828
- if (props.filter) return props.filter(o.value, inputValue.value);
829
- return tools.getOptionValue(o.value).toLowerCase().includes(inputValue.value.toLowerCase());
830
- });
831
- });
832
- /** 多选已选标签 */
833
- const selectedTags = computed(() => {
834
- if (!multipleRef.value) return [];
835
- return allOptions.value.filter((o) => o.isSelected);
836
- });
837
- const syncInputFromModelValue = () => {
838
- if (multipleRef.value) {
839
- inputValue.value = "";
840
- return;
653
+ function n(t) {
654
+ return n = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(t) {
655
+ return typeof t;
656
+ } : function(t) {
657
+ return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
658
+ }, n(t);
841
659
  }
842
- const mv = modelValueRef.value;
843
- if (mv == null || mv === "") {
844
- inputValue.value = "";
845
- return;
660
+ function r(t, e) {
661
+ if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function");
846
662
  }
847
- const found = optionsRef.value.find((o) => {
848
- if (props.toMatch) return props.toMatch(o, mv);
849
- return tools.getModelValue(o) === mv;
850
- });
851
- inputValue.value = found ? tools.getInputValue(found) : "";
852
- };
853
- /** 初始化多选选中状态 */
854
- const syncSelectedFromModelValue = () => {
855
- if (!multipleRef.value) return;
856
- const mv = modelValueRef.value;
857
- const newSet = /* @__PURE__ */ new Set();
858
- if (Array.isArray(mv)) optionsRef.value.forEach((o, i) => {
859
- const val = tools.getModelValue(o);
860
- if (mv.includes(val)) newSet.add(i);
861
- if (props.toMatch && mv.some((v) => props.toMatch(o, v))) newSet.add(i);
862
- });
863
- selectedIndices.value = newSet;
864
- };
865
- syncSelectedFromModelValue();
866
- syncInputFromModelValue();
867
- /** 点击选项 */
868
- const onSelect = (index) => {
869
- const option = optionsRef.value[index];
870
- if (!option) return;
871
- if (option.disabled) return;
872
- if (multipleRef.value) {
873
- const newSet = new Set(selectedIndices.value);
874
- if (newSet.has(index)) newSet.delete(index);
875
- else newSet.add(index);
876
- selectedIndices.value = newSet;
877
- inputValue.value = "";
878
- const newModelValue = optionsRef.value.filter((_, i) => newSet.has(i)).map((o) => tools.getModelValue(o));
879
- ctx.emit("update:modelValue", newModelValue);
880
- ctx.emit("select", option);
881
- } else {
882
- inputValue.value = tools.getInputValue(option);
883
- ctx.emit("update:modelValue", tools.getModelValue(option));
884
- ctx.emit("select", option);
885
- isOpen.value = false;
663
+ function i(t, e) {
664
+ for (var n = 0; n < e.length; n++) {
665
+ var r = e[n];
666
+ r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(t, d(r.key), r);
667
+ }
886
668
  }
887
- };
888
- /** 删除多选标签 */
889
- const onDeleteTag = (index) => {
890
- const newSet = new Set(selectedIndices.value);
891
- newSet.delete(index);
892
- selectedIndices.value = newSet;
893
- const newModelValue = optionsRef.value.filter((_, i) => newSet.has(i)).map((o) => tools.getModelValue(o));
894
- ctx.emit("update:modelValue", newModelValue);
895
- };
896
- /** 输入事件 */
897
- const onInput = () => {
898
- ctx.emit("input", inputValue.value);
899
- };
900
- /** 聚焦 */
901
- const onFocus = (e) => {
902
- if (inputReadonly.value) return;
903
- ctx.emit("focus", e, inputValue.value);
904
- };
905
- /** 失焦 */
906
- const onBlur = (e) => {
907
- if (inputReadonly.value) return;
908
- ctx.emit("blur", e, inputValue.value);
909
- if (!multipleRef.value) {
910
- const matched = displayOptions.value.find((o) => o.isSelected);
911
- if (matched && inputValue.value === tools.getInputValue(matched.value)) return;
912
- if (!inputValue.value) ctx.emit("update:modelValue", void 0);
669
+ function o(t, e, n) {
670
+ return e && i(t.prototype, e), n && i(t, n), Object.defineProperty(t, "prototype", { writable: !1 }), t;
913
671
  }
914
- };
915
- const lastOptionRef = ref(null);
916
- const optionsContainerRef = ref(null);
917
- let fetchObserver;
918
- const cleanupFetchObserver = () => {
919
- if (fetchObserver) {
920
- fetchObserver.disconnect();
921
- fetchObserver = void 0;
922
- }
923
- };
924
- /** 更新 fetch observer:观察最后一个选项元素 */
925
- const updateFetchObserver = () => {
926
- cleanupFetchObserver();
927
- if (!props.needFetch || !lastOptionRef.value || !optionsContainerRef.value) return;
928
- fetchObserver = new IntersectionObserver(async (entries) => {
929
- if (entries[0]?.isIntersecting && props.needFetch) {
930
- fetchLoading.value = true;
931
- await props.fetch?.();
932
- fetchLoading.value = false;
933
- }
934
- }, {
935
- root: optionsContainerRef.value,
936
- threshold: 1
937
- });
938
- fetchObserver.observe(lastOptionRef.value);
939
- };
940
- watch(modelValueRef, () => {
941
- syncSelectedFromModelValue();
942
- syncInputFromModelValue();
943
- });
944
- watch(optionsRef, () => {
945
- syncSelectedFromModelValue();
946
- syncInputFromModelValue();
947
- }, { deep: true });
948
- watch(lastOptionRef, () => {
949
- updateFetchObserver();
950
- });
951
- onBeforeUnmount(cleanupFetchObserver);
952
- return {
953
- inputValue,
954
- isOpen,
955
- fetchLoading,
956
- inputReadonly,
957
- displayOptions,
958
- selectedTags,
959
- lastOptionRef,
960
- optionsContainerRef,
961
- onSelect,
962
- onDeleteTag,
963
- onInput,
964
- onFocus,
965
- onBlur,
966
- tools,
967
- updateFetchObserver
968
- };
969
- }
970
- //#endregion
971
- //#region ../core/components/base/select/index.ts
972
- /**
973
- * @description select core 导出
974
- * @author 阿怪
975
- * @date 2026/2/25 14:50
976
- * @version v2.0.0
977
- *
978
- * 江湖的业务千篇一律,复杂的代码好几百行。
979
- */
980
- var SelectCore = {
981
- props: props$6,
982
- useSelect: useSelect$1
983
- };
984
- //#endregion
985
- //#region ../core/components/base/tag/index.ts
986
- /**
987
- * @description tag core
988
- * @author 阿怪
989
- * @date 2025/02/25 00:00
990
- * @version v1.0.0
991
- *
992
- * 江湖的业务千篇一律,复杂的代码好几百行。
993
- */
994
- var TagCore = { props: {
995
- type: {
996
- type: String,
997
- default: "default"
998
- },
999
- size: {
1000
- type: String,
1001
- default: void 0
1002
- },
1003
- closable: {
1004
- type: Boolean,
1005
- default: false
1006
- },
1007
- disabled: {
1008
- type: Boolean,
1009
- default: false
1010
- }
1011
- } };
1012
- (/* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
1013
- (function(t, e) {
1014
- "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).interact = e();
1015
- })(exports, (function() {
1016
- "use strict";
1017
- function t(t, e) {
1018
- var n = Object.keys(t);
1019
- if (Object.getOwnPropertySymbols) {
1020
- var r = Object.getOwnPropertySymbols(t);
1021
- e && (r = r.filter((function(e) {
1022
- return Object.getOwnPropertyDescriptor(t, e).enumerable;
1023
- }))), n.push.apply(n, r);
1024
- }
1025
- return n;
1026
- }
1027
- function e(e) {
1028
- for (var n = 1; n < arguments.length; n++) {
1029
- var r = null != arguments[n] ? arguments[n] : {};
1030
- n % 2 ? t(Object(r), !0).forEach((function(t) {
1031
- a(e, t, r[t]);
1032
- })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(r)) : t(Object(r)).forEach((function(t) {
1033
- Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(r, t));
1034
- }));
1035
- }
1036
- return e;
1037
- }
1038
- function n(t) {
1039
- return n = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(t) {
1040
- return typeof t;
1041
- } : function(t) {
1042
- return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
1043
- }, n(t);
1044
- }
1045
- function r(t, e) {
1046
- if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function");
1047
- }
1048
- function i(t, e) {
1049
- for (var n = 0; n < e.length; n++) {
1050
- var r = e[n];
1051
- r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(t, d(r.key), r);
1052
- }
1053
- }
1054
- function o(t, e, n) {
1055
- return e && i(t.prototype, e), n && i(t, n), Object.defineProperty(t, "prototype", { writable: !1 }), t;
1056
- }
1057
- function a(t, e, n) {
1058
- return (e = d(e)) in t ? Object.defineProperty(t, e, {
1059
- value: n,
1060
- enumerable: !0,
1061
- configurable: !0,
1062
- writable: !0
1063
- }) : t[e] = n, t;
672
+ function a(t, e, n) {
673
+ return (e = d(e)) in t ? Object.defineProperty(t, e, {
674
+ value: n,
675
+ enumerable: !0,
676
+ configurable: !0,
677
+ writable: !0
678
+ }) : t[e] = n, t;
1064
679
  }
1065
680
  function s(t, e) {
1066
681
  if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
@@ -6328,7 +5943,7 @@ var computePosition = (reference, floating, options) => {
6328
5943
  };
6329
5944
  //#endregion
6330
5945
  //#region ../core/components/template/pagination/api.ts
6331
- var props$4 = {
5946
+ var props$6 = {
6332
5947
  total: {
6333
5948
  type: Number,
6334
5949
  default: 0
@@ -6487,7 +6102,7 @@ function usePagination(props, currentValue) {
6487
6102
  * 江湖的业务千篇一律,复杂的代码好几百行。
6488
6103
  */
6489
6104
  var PaginationCore = {
6490
- props: props$4,
6105
+ props: props$6,
6491
6106
  usePagination
6492
6107
  };
6493
6108
  //#endregion
@@ -6906,177 +6521,1085 @@ function useMenu(props, emit) {
6906
6521
  }));
6907
6522
  })))();
6908
6523
  //#endregion
6909
- //#region ../core/components/template/tableColumn/index.ts
6524
+ //#region ../core/components/template/table/api.ts
6525
+ var props$5 = {
6526
+ data: {
6527
+ type: Array,
6528
+ default: () => []
6529
+ },
6530
+ height: {
6531
+ type: String,
6532
+ default: null
6533
+ },
6534
+ paramClass: {
6535
+ type: Boolean,
6536
+ default: false
6537
+ },
6538
+ loading: {
6539
+ type: Boolean,
6540
+ default: false
6541
+ }
6542
+ };
6543
+ //#endregion
6544
+ //#region ../core/components/template/table/useTable.ts
6545
+ /**
6546
+ * @description core table hook
6547
+ * @author 阿怪
6548
+ * @date 2026/2/25
6549
+ * @version v1.0.0
6550
+ *
6551
+ * 江湖的业务千篇一律,复杂的代码好几百行。
6552
+ */
6553
+ function useTable() {
6554
+ const error = (msg) => console.warn(`[水墨UI表格组件] ${msg}`);
6555
+ const initTable = (renders, columns, data) => {
6556
+ const tbodyTrList = [];
6557
+ data.forEach(() => {
6558
+ tbodyTrList.push([]);
6559
+ });
6560
+ /** 从 data[i] 中安全取值 */
6561
+ const getData = (i, param) => {
6562
+ if (data[i] == null) return "";
6563
+ return data[i][param] ?? "";
6564
+ };
6565
+ /** 向每行的 td 列表中追加一列 */
6566
+ const pushTd = (param, bodySlot, style) => {
6567
+ if (!param && !bodySlot) {
6568
+ error("param is undefined, column without param or slot will be ignored!");
6569
+ return;
6570
+ }
6571
+ tbodyTrList.forEach((t, i) => {
6572
+ t.push(renders.tbodyTr({
6573
+ data: param ? getData(i, param) : "",
6574
+ style,
6575
+ param: param ?? "",
6576
+ slot: bodySlot,
6577
+ slotInfo: {
6578
+ data: data[i],
6579
+ index: i
6580
+ }
6581
+ }));
6582
+ });
6583
+ };
6584
+ /** 将列宽 prop 转换为 style 对象 */
6585
+ const getStyle = (options) => {
6586
+ if (!options || !options.width) return {};
6587
+ const numberWidth = Number(options.width);
6588
+ if (!isNaN(numberWidth)) return { width: numberWidth + "px" };
6589
+ return { width: options.width };
6590
+ };
6591
+ /**
6592
+ * 初始化表头,同时将每列对应的 td 推入 tbodyTrList
6593
+ */
6594
+ const initTHead = () => {
6595
+ const ths = (columns ?? []).filter((column) => {
6596
+ if (!column.props) {
6597
+ error("column.props is undefined, column without param will be ignored!");
6598
+ return false;
6599
+ }
6600
+ return true;
6601
+ }).map((column) => {
6602
+ const slots = renders.initSlot(column);
6603
+ let bodySlot;
6604
+ let headSlot;
6605
+ const style = getStyle(column.props);
6606
+ if (slots) {
6607
+ bodySlot = slots.body;
6608
+ headSlot = slots.head;
6609
+ }
6610
+ pushTd(column.props.param, bodySlot, style);
6611
+ return renders.theadTh({
6612
+ label: column.props.label,
6613
+ param: column.props.param,
6614
+ slot: headSlot,
6615
+ style
6616
+ });
6617
+ });
6618
+ return renders.thead(ths);
6619
+ };
6620
+ return {
6621
+ thead: initTHead(),
6622
+ tbody: tbodyTrList.length > 0 ? renders.tbody(tbodyTrList.map((tds, i) => renders.tbodyTrs(tds, i))) : renders.empty
6623
+ };
6624
+ };
6625
+ return {
6626
+ initTable,
6627
+ error
6628
+ };
6629
+ }
6630
+ //#endregion
6631
+ //#region ../core/components/template/table/index.ts
6632
+ /**
6633
+ * @description table core 导出
6634
+ * @author 阿怪
6635
+ * @date 2026/2/25
6636
+ * @version v1.0.0
6637
+ *
6638
+ * 江湖的业务千篇一律,复杂的代码好几百行。
6639
+ */
6640
+ var TableCore = {
6641
+ props: props$5,
6642
+ useTable
6643
+ };
6644
+ //#endregion
6645
+ //#region ../core/components/template/tableColumn/index.ts
6646
+ /**
6647
+ * @description tableColumn core 导출
6648
+ * @author 阿怪
6649
+ * @date 2026/2/25
6650
+ * @version v1.0.0
6651
+ *
6652
+ * 江湖的业务千篇一律,复杂的代码好几百行。
6653
+ */
6654
+ var TableColumnCore = { props: {
6655
+ width: {
6656
+ type: String,
6657
+ default: ""
6658
+ },
6659
+ param: {
6660
+ type: String,
6661
+ default: ""
6662
+ },
6663
+ label: {
6664
+ type: String,
6665
+ default: ""
6666
+ }
6667
+ } };
6668
+ //#endregion
6669
+ //#region ../core/components/base/image/api.ts
6670
+ var props$3 = {
6671
+ src: {
6672
+ type: String,
6673
+ required: true
6674
+ },
6675
+ alt: {
6676
+ type: String,
6677
+ default: ""
6678
+ },
6679
+ fit: {
6680
+ type: String,
6681
+ default: "cover",
6682
+ enum: [
6683
+ "contain",
6684
+ "cover",
6685
+ "fill",
6686
+ "none",
6687
+ "scale-down"
6688
+ ]
6689
+ },
6690
+ width: {
6691
+ type: [String, Number],
6692
+ default: void 0
6693
+ },
6694
+ height: {
6695
+ type: [String, Number],
6696
+ default: void 0
6697
+ },
6698
+ lazy: {
6699
+ type: Boolean,
6700
+ default: false
6701
+ },
6702
+ previewSrcList: {
6703
+ type: Array,
6704
+ default: () => []
6705
+ },
6706
+ zIndex: {
6707
+ type: Number,
6708
+ default: 2e3
6709
+ }
6710
+ };
6711
+ //#endregion
6712
+ //#region ../core/components/base/image/useImage.ts
6713
+ /**
6714
+ * @description image hook
6715
+ * @author 阿怪
6716
+ * @date 2026/2/26
6717
+ * @version v1.0.0
6718
+ *
6719
+ * 江湖的业务千篇一律,复杂的代码好几百行。
6720
+ */
6721
+ function useImage(props, emit) {
6722
+ const status = ref("loading");
6723
+ const previewVisible = ref(false);
6724
+ const previewIndex = ref(0);
6725
+ const previewScale = ref(1);
6726
+ const previewRotate = ref(0);
6727
+ /** 重置加载状态(src 变化时调用) */
6728
+ const reset = () => {
6729
+ status.value = "loading";
6730
+ };
6731
+ const handleLoad = (e) => {
6732
+ status.value = "loaded";
6733
+ emit("load", e);
6734
+ };
6735
+ const handleError = (e) => {
6736
+ status.value = "error";
6737
+ emit("error", e);
6738
+ };
6739
+ /** 打开全屏预览,定位到 src 在 previewSrcList 中的位置 */
6740
+ const openPreview = () => {
6741
+ if (!props.previewSrcList || props.previewSrcList.length === 0) return;
6742
+ const idx = props.previewSrcList.indexOf(props.src);
6743
+ previewIndex.value = idx >= 0 ? idx : 0;
6744
+ previewScale.value = 1;
6745
+ previewRotate.value = 0;
6746
+ previewVisible.value = true;
6747
+ };
6748
+ const closePreview = () => {
6749
+ previewVisible.value = false;
6750
+ };
6751
+ /** 预览切换到上一张 */
6752
+ const previewPrev = () => {
6753
+ const total = props.previewSrcList.length;
6754
+ if (total === 0) return;
6755
+ previewIndex.value = (previewIndex.value - 1 + total) % total;
6756
+ previewScale.value = 1;
6757
+ previewRotate.value = 0;
6758
+ emit("switch", previewIndex.value);
6759
+ };
6760
+ /** 预览切换到下一张 */
6761
+ const previewNext = () => {
6762
+ const total = props.previewSrcList.length;
6763
+ if (total === 0) return;
6764
+ previewIndex.value = (previewIndex.value + 1) % total;
6765
+ previewScale.value = 1;
6766
+ previewRotate.value = 0;
6767
+ emit("switch", previewIndex.value);
6768
+ };
6769
+ /** 预览放大 */
6770
+ const zoomIn = () => {
6771
+ previewScale.value = Math.min(previewScale.value + .25, 5);
6772
+ };
6773
+ /** 预览缩小 */
6774
+ const zoomOut = () => {
6775
+ previewScale.value = Math.max(previewScale.value - .25, .25);
6776
+ };
6777
+ /** 顺时针旋转 90° */
6778
+ const rotate = () => {
6779
+ previewRotate.value = (previewRotate.value + 90) % 360;
6780
+ };
6781
+ watch(() => props.src, reset);
6782
+ onMounted(() => {
6783
+ if (props.lazy) status.value = "loading";
6784
+ });
6785
+ return {
6786
+ status,
6787
+ previewVisible,
6788
+ previewIndex,
6789
+ previewScale,
6790
+ previewRotate,
6791
+ handleLoad,
6792
+ handleError,
6793
+ openPreview,
6794
+ closePreview,
6795
+ previewPrev,
6796
+ previewNext,
6797
+ zoomIn,
6798
+ zoomOut,
6799
+ rotate
6800
+ };
6801
+ }
6802
+ //#endregion
6803
+ //#region ../core/components/base/image/index.ts
6804
+ /**
6805
+ * @description image core 导出
6806
+ * @author 阿怪
6807
+ * @date 2026/2/26
6808
+ * @version v1.0.0
6809
+ *
6810
+ * 江湖的业务千篇一律,复杂的代码好几百行。
6811
+ */
6812
+ var ImageCore = {
6813
+ props: props$3,
6814
+ useImage
6815
+ };
6816
+ //#endregion
6817
+ //#region ../core/compositions/overlay/useOverlayStack.ts
6910
6818
  /**
6911
- * @description tableColumn core 导출
6912
- * @author 阿怪
6913
- * @date 2026/2/25
6819
+ * @description overlay stack manager — unified z-index and ESC handling for all popups
6820
+ * @author kine-design
6821
+ * @date 2026/5/22 00:00
6914
6822
  * @version v1.0.0
6915
6823
  *
6916
6824
  * 江湖的业务千篇一律,复杂的代码好几百行。
6917
6825
  */
6918
- var TableColumnCore = { props: {
6919
- width: {
6920
- type: String,
6921
- default: ""
6922
- },
6923
- param: {
6924
- type: String,
6925
- default: ""
6826
+ var Z_INDEX_BASE = 100;
6827
+ defineStore("overlayStack", () => {
6828
+ const overlayStack = ref([]);
6829
+ const isEmpty = computed(() => overlayStack.value.length === 0);
6830
+ const topItem = computed(() => overlayStack.value.length > 0 ? overlayStack.value[overlayStack.value.length - 1] : void 0);
6831
+ const push = (item) => {
6832
+ const zIndex = overlayStack.value.length + Z_INDEX_BASE;
6833
+ const entry = {
6834
+ ...item,
6835
+ zIndex
6836
+ };
6837
+ overlayStack.value.push(entry);
6838
+ return entry;
6839
+ };
6840
+ const pop = () => {
6841
+ const item = overlayStack.value.pop();
6842
+ if (item) item.close();
6843
+ return item;
6844
+ };
6845
+ const remove = (id) => {
6846
+ const index = overlayStack.value.findIndex((item) => item.id === id);
6847
+ if (index !== -1) {
6848
+ overlayStack.value.splice(index, 1);
6849
+ recalculateZIndices();
6850
+ }
6851
+ };
6852
+ const recalculateZIndices = () => {
6853
+ overlayStack.value.forEach((item, index) => {
6854
+ item.zIndex = index + Z_INDEX_BASE;
6855
+ });
6856
+ };
6857
+ const handleEsc = (e) => {
6858
+ if (e.key === "Escape" && !isEmpty.value) pop();
6859
+ };
6860
+ let listenerAttached = false;
6861
+ const attachEscListener = () => {
6862
+ if (!listenerAttached) {
6863
+ document.addEventListener("keydown", handleEsc);
6864
+ listenerAttached = true;
6865
+ }
6866
+ };
6867
+ const detachEscListener = () => {
6868
+ if (listenerAttached) {
6869
+ document.removeEventListener("keydown", handleEsc);
6870
+ listenerAttached = false;
6871
+ }
6872
+ };
6873
+ return {
6874
+ overlayStack,
6875
+ isEmpty,
6876
+ topItem,
6877
+ push,
6878
+ pop,
6879
+ remove,
6880
+ attachEscListener,
6881
+ detachEscListener
6882
+ };
6883
+ });
6884
+ //#endregion
6885
+ //#region ../core/locale/index.ts
6886
+ /**
6887
+ * @description kine-design locale entry — built-in language packs + types.
6888
+ *
6889
+ * The library ships only the two general languages (zh / en). To use another
6890
+ * language, build a full dictionary that satisfies `KineLocaleMessages` in the
6891
+ * consuming app and pass it straight to the provider:
6892
+ *
6893
+ * import type { KineLocaleMessages } from '@kine-design/core';
6894
+ * const bn: KineLocaleMessages = { ...full Bengali dictionary... };
6895
+ * // <KConfigProvider :locale="bn"> ... </KConfigProvider>
6896
+ *
6897
+ * TypeScript enforces full key coverage, so a partial/forgotten key is a
6898
+ * compile error in the app — the same guarantee the built-in packs enjoy.
6899
+ * @author kine-design
6900
+ * @version v1.0.0
6901
+ */
6902
+ /** Built-in language packs, keyed by locale name. */
6903
+ var locales = {
6904
+ zh: {
6905
+ common: {
6906
+ confirm: "确定",
6907
+ cancel: "取消",
6908
+ close: "关闭",
6909
+ search: "搜索",
6910
+ reset: "重置",
6911
+ all: "全部",
6912
+ noData: "暂无数据",
6913
+ loading: "加载中...",
6914
+ save: "保存"
6915
+ },
6916
+ pagination: {
6917
+ totalPrefix: "共 ",
6918
+ totalSuffix: " 条"
6919
+ },
6920
+ datePicker: {
6921
+ placeholder: "请选择日期...",
6922
+ year: (year) => `${year}年`,
6923
+ month: (month) => `${month}月`,
6924
+ hour: "时",
6925
+ minute: "分",
6926
+ second: "秒",
6927
+ confirm: "确定",
6928
+ weekNames: [
6929
+ "日",
6930
+ "一",
6931
+ "二",
6932
+ "三",
6933
+ "四",
6934
+ "五",
6935
+ "六"
6936
+ ],
6937
+ monthNames: [
6938
+ "1月",
6939
+ "2月",
6940
+ "3月",
6941
+ "4月",
6942
+ "5月",
6943
+ "6月",
6944
+ "7月",
6945
+ "8月",
6946
+ "9月",
6947
+ "10月",
6948
+ "11月",
6949
+ "12月"
6950
+ ]
6951
+ },
6952
+ timePicker: {
6953
+ placeholder: "请选择时间...",
6954
+ hour: "时",
6955
+ minute: "分",
6956
+ second: "秒"
6957
+ },
6958
+ select: {
6959
+ placeholder: "请选择...",
6960
+ clear: "清空",
6961
+ noData: "暂无数据",
6962
+ loading: "加载中..."
6963
+ },
6964
+ autoComplete: {
6965
+ placeholder: "请输入...",
6966
+ loading: "加载中",
6967
+ clear: "清空",
6968
+ noData: "暂无建议"
6969
+ },
6970
+ cascader: {
6971
+ placeholder: "请选择",
6972
+ noMatch: "暂无匹配数据"
6973
+ },
6974
+ image: {
6975
+ loadError: "加载失败",
6976
+ zoomOut: "缩小",
6977
+ zoomIn: "放大",
6978
+ rotate: "旋转",
6979
+ close: "关闭"
6980
+ },
6981
+ transfer: {
6982
+ sourceTitle: "源列表",
6983
+ targetTitle: "目标列表",
6984
+ noData: "暂无数据",
6985
+ filterPlaceholder: "请输入搜索内容"
6986
+ },
6987
+ empty: { description: "暂无数据" },
6988
+ a11y: {
6989
+ rate: "评分",
6990
+ tagClose: "关闭",
6991
+ anchorNav: "锚点导航",
6992
+ backTop: "回到顶部",
6993
+ carouselPrev: "上一张",
6994
+ carouselNext: "下一张",
6995
+ toLightMode: "切换到亮色模式",
6996
+ toDarkMode: "切换到暗色模式",
6997
+ expandSider: "展开侧边栏",
6998
+ collapseSider: "折叠侧边栏"
6999
+ },
7000
+ upload: {
7001
+ trigger: "点击或拖拽文件到此处上传",
7002
+ triggerSimple: "点击上传文件",
7003
+ oversize: (name, max) => max ? `文件 "${name}" 超过大小限制(最大 ${max})` : `文件 "${name}" 超过大小限制`,
7004
+ uploadFailed: "上传失败",
7005
+ statusWaiting: "等待中",
7006
+ statusUploading: "上传中",
7007
+ statusDone: "已完成",
7008
+ statusFailed: "上传失败"
7009
+ },
7010
+ searchTable: {
7011
+ search: "搜索",
7012
+ reset: "重置"
7013
+ },
7014
+ editableTable: {
7015
+ summary: "合计",
7016
+ addRow: "+ 添加行",
7017
+ addFirstRow: "添加第一行",
7018
+ confirmDelete: "确定删除这一行?",
7019
+ delete: "删除",
7020
+ cancel: "取消",
7021
+ noData: "暂无数据"
7022
+ },
7023
+ formPage: {
7024
+ loading: "加载中...",
7025
+ cancel: "取消",
7026
+ saveDraft: "保存草稿",
7027
+ save: "保存",
7028
+ saving: "保存中...",
7029
+ submit: "提交",
7030
+ submitting: "提交中...",
7031
+ basicInfo: "基本信息",
7032
+ editTitle: (title) => `编辑${title}`,
7033
+ createTitle: (title) => `新建${title}`
7034
+ },
7035
+ approvalDialog: {
7036
+ submitTitle: "提交审批",
7037
+ submitConfirm: "确认提交",
7038
+ approveTitle: "审批通过",
7039
+ approveConfirm: "确认通过",
7040
+ rejectTitle: "驳回申请",
7041
+ rejectConfirm: "确认驳回",
7042
+ rejectPlaceholder: "请输入驳回原因...",
7043
+ remarkPlaceholder: "添加备注(可选)...",
7044
+ required: "* 必填",
7045
+ cancel: "取消",
7046
+ processing: "处理中..."
7047
+ },
7048
+ crudPage: {
7049
+ all: "全部",
7050
+ fieldPlaceholder: (label) => `请输入${label}`
7051
+ },
7052
+ login: {
7053
+ username: "用户名",
7054
+ usernamePlaceholder: "请输入用户名",
7055
+ password: "密码",
7056
+ passwordPlaceholder: "请输入密码",
7057
+ remember: "记住我",
7058
+ login: "登 录",
7059
+ loggingIn: "登录中..."
7060
+ },
7061
+ form: {
7062
+ inputPlaceholder: (label) => `请输入${label}`,
7063
+ selectPlaceholder: (label) => `请选择${label}`
7064
+ },
7065
+ routes: {
7066
+ createSuffix: " - 新建",
7067
+ editSuffix: " - 编辑",
7068
+ detailSuffix: " - 详情"
7069
+ },
7070
+ request: {
7071
+ success: "操作成功",
7072
+ opFailed: "操作失败",
7073
+ failed: "请求失败",
7074
+ failedWithStatus: (status) => `请求失败(${status})`,
7075
+ failedWithMsg: (msg) => `请求失败: ${msg}`,
7076
+ timeout: "请求超时",
7077
+ timeoutRetry: "请求超时,请稍后重试",
7078
+ canceled: "请求已取消",
7079
+ permissionDenied: "权限不足,无法执行此操作",
7080
+ invalidUrl: (url) => `无效的请求地址: ${url}`,
7081
+ decodeFailed: (reason) => `解码失败: ${reason}`,
7082
+ parseFailed: "数据解析失败",
7083
+ networkError: "网络错误",
7084
+ networkException: "网络异常,请检查网络连接",
7085
+ unknownError: (error) => `未知错误: ${error}`,
7086
+ unknown: "发生了未知错误",
7087
+ uploadFailed: "上传失败",
7088
+ uploadCanceled: "上传已取消"
7089
+ },
7090
+ aiChat: {
7091
+ inputPlaceholder: "输入消息…",
7092
+ send: "发送",
7093
+ defaultTitle: (n) => `对话 ${n}`,
7094
+ phaseTyping: "正在输入…",
7095
+ phaseThinking: "思考中…",
7096
+ phaseReplying: "回复中…",
7097
+ phaseSending: "发送中…"
7098
+ }
6926
7099
  },
6927
- label: {
6928
- type: String,
6929
- default: ""
7100
+ en: {
7101
+ common: {
7102
+ confirm: "OK",
7103
+ cancel: "Cancel",
7104
+ close: "Close",
7105
+ search: "Search",
7106
+ reset: "Reset",
7107
+ all: "All",
7108
+ noData: "No data",
7109
+ loading: "Loading...",
7110
+ save: "Save"
7111
+ },
7112
+ pagination: {
7113
+ totalPrefix: "",
7114
+ totalSuffix: " items"
7115
+ },
7116
+ datePicker: {
7117
+ placeholder: "Select date...",
7118
+ year: (year) => `${year}`,
7119
+ month: (month) => `${month}`,
7120
+ hour: "Hour",
7121
+ minute: "Min",
7122
+ second: "Sec",
7123
+ confirm: "OK",
7124
+ weekNames: [
7125
+ "Sun",
7126
+ "Mon",
7127
+ "Tue",
7128
+ "Wed",
7129
+ "Thu",
7130
+ "Fri",
7131
+ "Sat"
7132
+ ],
7133
+ monthNames: [
7134
+ "Jan",
7135
+ "Feb",
7136
+ "Mar",
7137
+ "Apr",
7138
+ "May",
7139
+ "Jun",
7140
+ "Jul",
7141
+ "Aug",
7142
+ "Sep",
7143
+ "Oct",
7144
+ "Nov",
7145
+ "Dec"
7146
+ ]
7147
+ },
7148
+ timePicker: {
7149
+ placeholder: "Select time...",
7150
+ hour: "Hour",
7151
+ minute: "Min",
7152
+ second: "Sec"
7153
+ },
7154
+ select: {
7155
+ placeholder: "Select...",
7156
+ clear: "Clear",
7157
+ noData: "No data",
7158
+ loading: "Loading..."
7159
+ },
7160
+ autoComplete: {
7161
+ placeholder: "Enter...",
7162
+ loading: "Loading",
7163
+ clear: "Clear",
7164
+ noData: "No suggestions"
7165
+ },
7166
+ cascader: {
7167
+ placeholder: "Select",
7168
+ noMatch: "No matching data"
7169
+ },
7170
+ image: {
7171
+ loadError: "Load failed",
7172
+ zoomOut: "Zoom out",
7173
+ zoomIn: "Zoom in",
7174
+ rotate: "Rotate",
7175
+ close: "Close"
7176
+ },
7177
+ transfer: {
7178
+ sourceTitle: "Source",
7179
+ targetTitle: "Target",
7180
+ noData: "No data",
7181
+ filterPlaceholder: "Search..."
7182
+ },
7183
+ empty: { description: "No data" },
7184
+ a11y: {
7185
+ rate: "Rating",
7186
+ tagClose: "Close",
7187
+ anchorNav: "Anchor navigation",
7188
+ backTop: "Back to top",
7189
+ carouselPrev: "Previous",
7190
+ carouselNext: "Next",
7191
+ toLightMode: "Switch to light mode",
7192
+ toDarkMode: "Switch to dark mode",
7193
+ expandSider: "Expand sidebar",
7194
+ collapseSider: "Collapse sidebar"
7195
+ },
7196
+ upload: {
7197
+ trigger: "Click or drag file here to upload",
7198
+ triggerSimple: "Click to upload",
7199
+ oversize: (name, max) => max ? `File "${name}" exceeds size limit (max ${max})` : `File "${name}" exceeds size limit`,
7200
+ uploadFailed: "Upload failed",
7201
+ statusWaiting: "Waiting",
7202
+ statusUploading: "Uploading",
7203
+ statusDone: "Done",
7204
+ statusFailed: "Failed"
7205
+ },
7206
+ searchTable: {
7207
+ search: "Search",
7208
+ reset: "Reset"
7209
+ },
7210
+ editableTable: {
7211
+ summary: "Total",
7212
+ addRow: "+ Add row",
7213
+ addFirstRow: "Add first row",
7214
+ confirmDelete: "Delete this row?",
7215
+ delete: "Delete",
7216
+ cancel: "Cancel",
7217
+ noData: "No data"
7218
+ },
7219
+ formPage: {
7220
+ loading: "Loading...",
7221
+ cancel: "Cancel",
7222
+ saveDraft: "Save draft",
7223
+ save: "Save",
7224
+ saving: "Saving...",
7225
+ submit: "Submit",
7226
+ submitting: "Submitting...",
7227
+ basicInfo: "Basic info",
7228
+ editTitle: (title) => `Edit ${title}`,
7229
+ createTitle: (title) => `New ${title}`
7230
+ },
7231
+ approvalDialog: {
7232
+ submitTitle: "Submit for approval",
7233
+ submitConfirm: "Confirm submission",
7234
+ approveTitle: "Approve",
7235
+ approveConfirm: "Confirm approval",
7236
+ rejectTitle: "Reject",
7237
+ rejectConfirm: "Confirm rejection",
7238
+ rejectPlaceholder: "Enter rejection reason...",
7239
+ remarkPlaceholder: "Add remark (optional)...",
7240
+ required: "* Required",
7241
+ cancel: "Cancel",
7242
+ processing: "Processing..."
7243
+ },
7244
+ crudPage: {
7245
+ all: "All",
7246
+ fieldPlaceholder: (label) => `Enter ${label}`
7247
+ },
7248
+ login: {
7249
+ username: "Username",
7250
+ usernamePlaceholder: "Enter username",
7251
+ password: "Password",
7252
+ passwordPlaceholder: "Enter password",
7253
+ remember: "Remember me",
7254
+ login: "Log in",
7255
+ loggingIn: "Logging in..."
7256
+ },
7257
+ form: {
7258
+ inputPlaceholder: (label) => `Enter ${label}`,
7259
+ selectPlaceholder: (label) => `Select ${label}`
7260
+ },
7261
+ routes: {
7262
+ createSuffix: " - New",
7263
+ editSuffix: " - Edit",
7264
+ detailSuffix: " - Details"
7265
+ },
7266
+ request: {
7267
+ success: "Success",
7268
+ opFailed: "Operation failed",
7269
+ failed: "Request failed",
7270
+ failedWithStatus: (status) => `Request failed (${status})`,
7271
+ failedWithMsg: (msg) => `Request failed: ${msg}`,
7272
+ timeout: "Request timed out",
7273
+ timeoutRetry: "Request timed out, please try again",
7274
+ canceled: "Request canceled",
7275
+ permissionDenied: "Permission denied",
7276
+ invalidUrl: (url) => `Invalid request URL: ${url}`,
7277
+ decodeFailed: (reason) => `Decode failed: ${reason}`,
7278
+ parseFailed: "Failed to parse data",
7279
+ networkError: "Network error",
7280
+ networkException: "Network error, please check your connection",
7281
+ unknownError: (error) => `Unknown error: ${error}`,
7282
+ unknown: "An unknown error occurred",
7283
+ uploadFailed: "Upload failed",
7284
+ uploadCanceled: "Upload canceled"
7285
+ },
7286
+ aiChat: {
7287
+ inputPlaceholder: "Type a message…",
7288
+ send: "Send",
7289
+ defaultTitle: (n) => `Chat ${n}`,
7290
+ phaseTyping: "Typing…",
7291
+ phaseThinking: "Thinking…",
7292
+ phaseReplying: "Replying…",
7293
+ phaseSending: "Sending…"
7294
+ }
6930
7295
  }
6931
- } };
7296
+ };
6932
7297
  //#endregion
6933
- //#region ../core/components/base/image/api.ts
6934
- var props$2 = {
6935
- src: {
6936
- type: String,
6937
- required: true
6938
- },
6939
- alt: {
6940
- type: String,
6941
- default: ""
6942
- },
6943
- fit: {
6944
- type: String,
6945
- default: "cover",
6946
- enum: [
6947
- "contain",
6948
- "cover",
6949
- "fill",
6950
- "none",
6951
- "scale-down"
6952
- ]
6953
- },
6954
- width: {
6955
- type: [String, Number],
6956
- default: void 0
6957
- },
6958
- height: {
6959
- type: [String, Number],
6960
- default: void 0
6961
- },
6962
- lazy: {
6963
- type: Boolean,
6964
- default: false
6965
- },
6966
- previewSrcList: {
6967
- type: Array,
6968
- default: () => []
6969
- },
6970
- zIndex: {
6971
- type: Number,
6972
- default: 2e3
7298
+ //#region ../core/compositions/common/useLocale.ts
7299
+ /**
7300
+ * @description composable for unified component locale with global injection
7301
+ * support — the i18n counterpart of `useComponentSize`. Components call
7302
+ * `useLocale()` to get the active message dictionary (a computed ref) and read
7303
+ * their user-facing strings from it instead of hardcoding text.
7304
+ * @author kine-design
7305
+ * @version v1.0.0
7306
+ *
7307
+ * 江湖的业务千篇一律,复杂的代码好几百行。
7308
+ */
7309
+ var KINE_LOCALE_KEY = Symbol("kine-locale");
7310
+ function resolveMessages(config) {
7311
+ if (!config) return locales["zh"];
7312
+ if (typeof config === "string") return locales[config] ?? locales["zh"];
7313
+ return config;
7314
+ }
7315
+ /**
7316
+ * Resolve the active message dictionary from the nearest `KConfigProvider`.
7317
+ * Falls back to the default (zh) pack when no provider is present.
7318
+ */
7319
+ function useLocale() {
7320
+ const injected = inject(KINE_LOCALE_KEY, void 0);
7321
+ return computed(() => resolveMessages(injected?.value));
7322
+ }
7323
+ var activeLocaleMessages = locales["zh"];
7324
+ /** Read the global active dictionary from non-component (pure-function) code. */
7325
+ function getActiveLocaleMessages() {
7326
+ return activeLocaleMessages;
7327
+ }
7328
+ //#endregion
7329
+ //#region components/layout/KSider.tsx
7330
+ /**
7331
+ * @description KSider 侧边栏组件,支持折叠动画和自定义 trigger
7332
+ * @author 阿怪
7333
+ * @date 2026/2/26
7334
+ * @version v0.0.1
7335
+ *
7336
+ * 江湖的业务千篇一律,复杂的代码好几百行。
7337
+ */
7338
+ var KSider_default = /* @__PURE__ */ defineComponent({
7339
+ name: "KSider",
7340
+ setup(_, ctx) {
7341
+ const t = useLocale();
7342
+ const collapsed = inject(LAYOUT_COLLAPSED_KEY);
7343
+ const toggle = inject(LAYOUT_TOGGLE_KEY);
7344
+ const drawerOpen = inject(LAYOUT_DRAWER_OPEN_KEY, ref(false));
7345
+ const siderWidth = inject(LAYOUT_SIDER_WIDTH_KEY, "240px");
7346
+ const collapsedWidth = inject(LAYOUT_COLLAPSED_WIDTH_KEY, "64px");
7347
+ const currentWidth = computed(() => {
7348
+ if (!collapsed) return siderWidth;
7349
+ return collapsed.value ? collapsedWidth : siderWidth;
7350
+ });
7351
+ const isCollapsed = computed(() => collapsed?.value ?? false);
7352
+ const handleTriggerClick = () => {
7353
+ toggle?.();
7354
+ };
7355
+ const defaultTrigger = () => createVNode("button", {
7356
+ "class": "k-crud-sider-trigger-btn",
7357
+ "onClick": handleTriggerClick,
7358
+ "title": isCollapsed.value ? t.value.a11y.expandSider : t.value.a11y.collapseSider
7359
+ }, [createVNode("span", { "class": ["k-crud-sider-trigger-icon", isCollapsed.value && "k-crud-sider-trigger-icon--collapsed"] }, ["‹"])]);
7360
+ return () => createVNode("aside", {
7361
+ "class": [
7362
+ "k-crud-sider",
7363
+ isCollapsed.value && "k-crud-sider--collapsed",
7364
+ drawerOpen.value && "k-crud-sider--drawer-open"
7365
+ ],
7366
+ "style": { width: currentWidth.value }
7367
+ }, [
7368
+ ctx.slots.logo && createVNode("div", { "class": "k-crud-sider-logo" }, [ctx.slots.logo()]),
7369
+ createVNode("div", { "class": "k-crud-sider-body" }, [ctx.slots.default?.()]),
7370
+ createVNode("div", { "class": "k-crud-sider-trigger" }, [ctx.slots.trigger ? ctx.slots.trigger({
7371
+ collapsed: isCollapsed.value,
7372
+ toggle
7373
+ }) : defaultTrigger()])
7374
+ ]);
7375
+ }
7376
+ });
7377
+ //#endregion
7378
+ //#region components/layout/KHeader.tsx
7379
+ /**
7380
+ * @description KHeader 顶部栏组件
7381
+ * @author 阿怪
7382
+ * @date 2026/2/26
7383
+ * @version v0.0.1
7384
+ *
7385
+ * 江湖的业务千篇一律,复杂的代码好几百行。
7386
+ */
7387
+ var KHeader_default = /* @__PURE__ */ defineComponent({
7388
+ name: "KHeader",
7389
+ setup(_, ctx) {
7390
+ const toggleDrawer = inject(LAYOUT_TOGGLE_DRAWER_KEY, () => {});
7391
+ return () => createVNode("header", { "class": "k-crud-header" }, [
7392
+ createVNode("button", {
7393
+ "class": "k-crud-header-hamburger",
7394
+ "onClick": toggleDrawer
7395
+ }, [createVNode("span", { "class": "k-crud-header-hamburger-icon" }, null)]),
7396
+ createVNode("div", { "class": "k-crud-header-main" }, [ctx.slots.default?.()]),
7397
+ ctx.slots.extra && createVNode("div", { "class": "k-crud-header-extra" }, [ctx.slots.extra()])
7398
+ ]);
7399
+ }
7400
+ });
7401
+ //#endregion
7402
+ //#region components/layout/KContent.tsx
7403
+ /**
7404
+ * @description KContent 主内容区组件
7405
+ * @author 阿怪
7406
+ * @date 2026/2/26
7407
+ * @version v0.0.1
7408
+ *
7409
+ * 江湖的业务千篇一律,复杂的代码好几百行。
7410
+ */
7411
+ var KContent_default = /* @__PURE__ */ defineComponent({
7412
+ name: "KContent",
7413
+ setup(_, ctx) {
7414
+ return () => createVNode("main", { "class": "k-crud-content" }, [ctx.slots.default?.()]);
7415
+ }
7416
+ });
7417
+ //#endregion
7418
+ //#region ../core/compositions/common/testAnchor.ts
7419
+ /**
7420
+ * @description Test anchor system: provide/inject keys, types, and kDefineComponent wrapper
7421
+ * @author kine-design
7422
+ * @date 2026/4/28
7423
+ * @version v1.0.0
7424
+ *
7425
+ * Renders business-semantic `data-k` attributes on DOM elements when testAnchor is enabled,
7426
+ * allowing E2E tests to target components by stable identifiers.
7427
+ */
7428
+ /** Global switch provided by KConfigProvider to enable data-k rendering */
7429
+ var K_TEST_ANCHOR_KEY = Symbol("k-test-anchor");
7430
+ /** Field identity provided by KFormItem so child inputs inherit field name */
7431
+ var K_FIELD_KEY = Symbol("k-field");
7432
+ /**
7433
+ * Extract text content from a VNode tree (for slotText mode).
7434
+ * Walks children recursively, concatenating string segments.
7435
+ */
7436
+ function extractSlotText(nodes) {
7437
+ if (!nodes) return "";
7438
+ let text = "";
7439
+ for (const node of nodes) if (typeof node.children === "string") text += node.children;
7440
+ else if (Array.isArray(node.children)) text += extractSlotText(node.children);
7441
+ return text.trim();
7442
+ }
7443
+ /**
7444
+ * Resolve the data-k value for a component instance.
7445
+ * Returns undefined when testAnchor is off or no identity can be derived.
7446
+ */
7447
+ function resolveDataK(config, props, attrs, fieldFromParent, slotNodes) {
7448
+ const override = attrs[`k-${config.type}`];
7449
+ if (typeof override === "string" && override) return `${config.type}:${override}`;
7450
+ if (config.prop) {
7451
+ const val = props[config.prop];
7452
+ if (typeof val === "string" && val) return `${config.type}:${val}`;
6973
7453
  }
6974
- };
6975
- //#endregion
6976
- //#region ../core/components/base/image/useImage.ts
7454
+ if (config.slotText && slotNodes) {
7455
+ const text = extractSlotText(slotNodes);
7456
+ if (text) return `${config.type}:${text}`;
7457
+ }
7458
+ if (config.slotText && typeof props.text === "string" && props.text) return `${config.type}:${props.text}`;
7459
+ if (config.type === "field" && !config.prop && fieldFromParent) return `${config.type}:${fieldFromParent}`;
7460
+ }
6977
7461
  /**
6978
- * @description image hook
6979
- * @author 阿怪
6980
- * @date 2026/2/26
6981
- * @version v1.0.0
7462
+ * Inject data-k attribute into a VNode.
7463
+ * Uses cloneVNode to avoid mutating the original.
7464
+ */
7465
+ function injectDataK(vnode, dataK) {
7466
+ return cloneVNode(vnode, { "data-k": dataK });
7467
+ }
7468
+ /**
7469
+ * Wrapper around Vue's defineComponent that automatically injects `data-k`
7470
+ * test anchor attributes when testAnchor is enabled via KConfigProvider.
6982
7471
  *
6983
- * 江湖的业务千篇一律,复杂的代码好几百行。
7472
+ * Usage:
7473
+ * ```tsx
7474
+ * export default kDefineComponent((_props, ctx) => {
7475
+ * return () => <div class="k-form-item">...</div>;
7476
+ * }, {
7477
+ * name: 'KFormItem',
7478
+ * props: FormCore.formItemProps,
7479
+ * kAnchor: { type: 'field', prop: 'prop' },
7480
+ * });
7481
+ * ```
6984
7482
  */
6985
- function useImage(props, emit) {
6986
- const status = ref("loading");
6987
- const previewVisible = ref(false);
6988
- const previewIndex = ref(0);
6989
- const previewScale = ref(1);
6990
- const previewRotate = ref(0);
6991
- /** 重置加载状态(src 变化时调用) */
6992
- const reset = () => {
6993
- status.value = "loading";
6994
- };
6995
- const handleLoad = (e) => {
6996
- status.value = "loaded";
6997
- emit("load", e);
6998
- };
6999
- const handleError = (e) => {
7000
- status.value = "error";
7001
- emit("error", e);
7002
- };
7003
- /** 打开全屏预览,定位到 src previewSrcList 中的位置 */
7004
- const openPreview = () => {
7005
- if (!props.previewSrcList || props.previewSrcList.length === 0) return;
7006
- const idx = props.previewSrcList.indexOf(props.src);
7007
- previewIndex.value = idx >= 0 ? idx : 0;
7008
- previewScale.value = 1;
7009
- previewRotate.value = 0;
7010
- previewVisible.value = true;
7011
- };
7012
- const closePreview = () => {
7013
- previewVisible.value = false;
7014
- };
7015
- /** 预览切换到上一张 */
7016
- const previewPrev = () => {
7017
- const total = props.previewSrcList.length;
7018
- if (total === 0) return;
7019
- previewIndex.value = (previewIndex.value - 1 + total) % total;
7020
- previewScale.value = 1;
7021
- previewRotate.value = 0;
7022
- emit("switch", previewIndex.value);
7023
- };
7024
- /** 预览切换到下一张 */
7025
- const previewNext = () => {
7026
- const total = props.previewSrcList.length;
7027
- if (total === 0) return;
7028
- previewIndex.value = (previewIndex.value + 1) % total;
7029
- previewScale.value = 1;
7030
- previewRotate.value = 0;
7031
- emit("switch", previewIndex.value);
7032
- };
7033
- /** 预览放大 */
7034
- const zoomIn = () => {
7035
- previewScale.value = Math.min(previewScale.value + .25, 5);
7036
- };
7037
- /** 预览缩小 */
7038
- const zoomOut = () => {
7039
- previewScale.value = Math.max(previewScale.value - .25, .25);
7040
- };
7041
- /** 顺时针旋转 90° */
7042
- const rotate = () => {
7043
- previewRotate.value = (previewRotate.value + 90) % 360;
7044
- };
7045
- watch(() => props.src, reset);
7046
- onMounted(() => {
7047
- if (props.lazy) status.value = "loading";
7048
- });
7049
- return {
7050
- status,
7051
- previewVisible,
7052
- previewIndex,
7053
- previewScale,
7054
- previewRotate,
7055
- handleLoad,
7056
- handleError,
7057
- openPreview,
7058
- closePreview,
7059
- previewPrev,
7060
- previewNext,
7061
- zoomIn,
7062
- zoomOut,
7063
- rotate
7064
- };
7483
+ function kDefineComponent(setup, options) {
7484
+ const { kAnchor, ...defineOptions } = options;
7485
+ return defineComponent((rawProps, ctx) => {
7486
+ const render = setup(rawProps, ctx);
7487
+ if (!kAnchor) return render;
7488
+ const testAnchor = inject(K_TEST_ANCHOR_KEY, void 0);
7489
+ const parentField = kAnchor.type === "field" && !kAnchor.prop ? inject(K_FIELD_KEY, void 0) : void 0;
7490
+ return () => {
7491
+ const vnode = render();
7492
+ if (!testAnchor?.value) return vnode;
7493
+ const props = rawProps;
7494
+ const slotNodes = kAnchor.slotText ? ctx.slots.default?.() ?? void 0 : void 0;
7495
+ const dataK = resolveDataK(kAnchor, props, ctx.attrs, parentField?.value, slotNodes);
7496
+ if (!dataK) return vnode;
7497
+ if (vnode === null || vnode === void 0) return vnode;
7498
+ if (Array.isArray(vnode)) {
7499
+ if (vnode.length === 0) return vnode;
7500
+ const first = vnode[0];
7501
+ if (first && typeof first === "object") return [injectDataK(first, dataK), ...vnode.slice(1)];
7502
+ return vnode;
7503
+ }
7504
+ return injectDataK(vnode, dataK);
7505
+ };
7506
+ }, defineOptions);
7065
7507
  }
7066
7508
  //#endregion
7067
- //#region ../core/components/base/image/index.ts
7509
+ //#region ../ui/components/table/KTable.tsx
7068
7510
  /**
7069
- * @description image core 导出
7511
+ * @description kine-ui table 组件
7070
7512
  * @author 阿怪
7071
7513
  * @date 2026/2/26
7072
- * @version v1.0.0
7514
+ * @version v1.1.0
7073
7515
  *
7074
7516
  * 江湖的业务千篇一律,复杂的代码好几百行。
7517
+ *
7518
+ * v1.1.0 migrate to kDefineComponent for data-k test anchor 2026/4/28
7075
7519
  */
7076
- var ImageCore = {
7520
+ var { props: props$2 } = TableCore;
7521
+ var KTable_default = kDefineComponent((_props, { slots }) => {
7522
+ const p = _props;
7523
+ const { initTable } = useTable();
7524
+ const t = useLocale();
7525
+ const testAnchor = inject(K_TEST_ANCHOR_KEY, void 0);
7526
+ return () => {
7527
+ const columns = [];
7528
+ (slots.default?.() ?? []).forEach((s) => {
7529
+ if (s.type === Fragment) {
7530
+ s.children?.forEach((c) => columns.push(c));
7531
+ return;
7532
+ }
7533
+ if (s.type === Comment) return;
7534
+ if (typeof s.type === "object" && s.type.name === "KTableColumn") columns.push(s);
7535
+ });
7536
+ const style = p.height ? { height: p.height } : void 0;
7537
+ const { thead, tbody } = initTable({
7538
+ empty: createVNode("tbody", {
7539
+ "class": "m-table-empty k-table-empty",
7540
+ "data-k": "state:tableEmpty"
7541
+ }, [createVNode("tr", null, [createVNode("th", { "colspan": columns.length }, [slots.empty?.() ?? t.value.common.noData])])]),
7542
+ tbodyTr: ({ data, param, slot, style: cellStyle, slotInfo }) => createVNode("td", {
7543
+ "style": cellStyle,
7544
+ "data-k": testAnchor?.value && param ? `col:${param}` : void 0,
7545
+ "class": [
7546
+ "m-td",
7547
+ "k-td",
7548
+ p.paramClass ? `m-td-${param}` : null,
7549
+ p.paramClass ? `k-td-${param}` : null
7550
+ ]
7551
+ }, [slot ? slot({
7552
+ data: slotInfo?.data,
7553
+ row: slotInfo?.data,
7554
+ index: slotInfo?.index
7555
+ }) : data]),
7556
+ theadTh: ({ label, param, slot, style: cellStyle }) => createVNode("th", {
7557
+ "class": [
7558
+ "m-th",
7559
+ "k-th",
7560
+ p.paramClass ? `m-th-${param}` : null,
7561
+ p.paramClass ? `k-th-${param}` : null
7562
+ ],
7563
+ "style": cellStyle
7564
+ }, [slot ? slot() : label]),
7565
+ thead: (ths) => createVNode("thead", {
7566
+ "class": "m-thead k-thead",
7567
+ "data-k": "region:tableHead"
7568
+ }, [createVNode("tr", { "class": "m-tr k-tr" }, [ths])]),
7569
+ tbody: (trs) => createVNode("tbody", {
7570
+ "class": "m-tbody k-tbody",
7571
+ "data-k": "region:tableBody"
7572
+ }, [trs]),
7573
+ tbodyTrs: (tds, i) => createVNode("tr", {
7574
+ "class": "m-tr k-tr",
7575
+ "key": i
7576
+ }, [...tds]),
7577
+ initSlot: (tableColumn) => {
7578
+ const children = tableColumn.children;
7579
+ if (!children || Array.isArray(children) || typeof children !== "object") return;
7580
+ return {
7581
+ body: children.default,
7582
+ head: children.head
7583
+ };
7584
+ }
7585
+ }, columns, p.data);
7586
+ return createVNode("div", {
7587
+ "class": [
7588
+ "m-table",
7589
+ "k-table",
7590
+ p.loading && "k-table-loading-wrap"
7591
+ ],
7592
+ "style": style
7593
+ }, [p.loading && createVNode("div", { "class": "k-table-loading-overlay" }, [createVNode("div", { "class": "k-table-loading-spinner" }, null)]), createVNode("table", { "class": "m-table-inner k-table-inner" }, [thead, tbody])]);
7594
+ };
7595
+ }, {
7596
+ name: "KTable",
7077
7597
  props: props$2,
7078
- useImage
7079
- };
7598
+ kAnchor: {
7599
+ type: "table",
7600
+ prop: "name"
7601
+ }
7602
+ });
7080
7603
  //#endregion
7081
7604
  //#region ../ui/components/pagination/KPagination.tsx
7082
7605
  /**
@@ -7090,6 +7613,7 @@ var ImageCore = {
7090
7613
  var { props: paginationProps } = PaginationCore;
7091
7614
  var KPagination_default = /* @__PURE__ */ defineComponent((_props, { emit }) => {
7092
7615
  const p = _props;
7616
+ const t = useLocale();
7093
7617
  const currentValue = ref(p.modelValue ?? p.defaultCurrent ?? 1);
7094
7618
  watch(() => p.modelValue, (val) => {
7095
7619
  if (val !== void 0) currentValue.value = val;
@@ -7119,9 +7643,9 @@ var KPagination_default = /* @__PURE__ */ defineComponent((_props, { emit }) =>
7119
7643
  const totalPages = getPageBtnLength();
7120
7644
  const btnGetter = {
7121
7645
  total: () => createVNode("div", { "class": "m-page-total k-page-total" }, [
7122
- createTextVNode("共 "),
7646
+ t.value.pagination.totalPrefix,
7123
7647
  createVNode("span", null, [p.total]),
7124
- createTextVNode(" 条")
7648
+ t.value.pagination.totalSuffix
7125
7649
  ]),
7126
7650
  prev: () => createVNode("div", {
7127
7651
  "class": [
@@ -7266,6 +7790,7 @@ var KSearchTable_default = /* @__PURE__ */ defineComponent({
7266
7790
  "reset"
7267
7791
  ],
7268
7792
  setup(props, ctx) {
7793
+ const t = useLocale();
7269
7794
  /** 点击搜索按钮 */
7270
7795
  const onSearch = () => {
7271
7796
  ctx.emit("search");
@@ -7279,12 +7804,17 @@ var KSearchTable_default = /* @__PURE__ */ defineComponent({
7279
7804
  ctx.emit("update:page", page);
7280
7805
  };
7281
7806
  return () => createVNode("div", { "class": ["k-search-table", props.loading ? "k-search-table--loading" : ""] }, [
7282
- props.searchable && createVNode("div", { "class": "k-search-table-search" }, [createVNode("div", { "class": "k-search-table-search-form" }, [ctx.slots.search?.()]), createVNode("div", { "class": "k-search-table-search-actions" }, [createVNode(KButton_default, {
7807
+ props.searchable && createVNode("div", { "class": "k-search-table-search" }, [createVNode("div", { "class": "k-search-table-search-form" }, [ctx.slots.search?.()]), createVNode("div", {
7808
+ "class": "k-search-table-search-actions",
7809
+ "data-k": "action:searchActions"
7810
+ }, [createVNode(KButton_default, {
7283
7811
  "type": "primary",
7284
- "text": "搜索",
7812
+ "text": t.value.searchTable.search,
7813
+ "data-k": "action:search",
7285
7814
  "onClick": onSearch
7286
7815
  }, null), createVNode(KButton_default, {
7287
- "text": "重置",
7816
+ "text": t.value.searchTable.reset,
7817
+ "data-k": "action:reset",
7288
7818
  "onClick": onReset
7289
7819
  }, null)])]),
7290
7820
  ctx.slots.toolbar && createVNode("div", { "class": "k-search-table-toolbar" }, [ctx.slots.toolbar()]),
@@ -7329,6 +7859,7 @@ var { props: props$1 } = TagCore;
7329
7859
  var KTag_default = /* @__PURE__ */ defineComponent((_props, { slots, emit }) => {
7330
7860
  const props = _props;
7331
7861
  const componentSize = useComponentSize(props);
7862
+ const t = useLocale();
7332
7863
  return () => {
7333
7864
  const { type, closable, disabled } = props;
7334
7865
  const classes = [
@@ -7355,7 +7886,7 @@ var KTag_default = /* @__PURE__ */ defineComponent((_props, { slots, emit }) =>
7355
7886
  }, [slots.default?.() ?? "", closable && createVNode("span", {
7356
7887
  "class": "k-tag-close",
7357
7888
  "onClick": handleClose,
7358
- "aria-label": "关闭"
7889
+ "aria-label": t.value.a11y.tagClose
7359
7890
  }, [createTextVNode("×")])]);
7360
7891
  };
7361
7892
  }, {
@@ -7404,7 +7935,7 @@ var LoadingPlaceholder = () => createVNode("div", { "class": "k-image-placeholde
7404
7935
  }, null)
7405
7936
  ])]);
7406
7937
  /** 加载失败占位图标 */
7407
- var ErrorPlaceholder = () => createVNode("div", { "class": "k-image-placeholder k-image-error" }, [createVNode("svg", {
7938
+ var ErrorPlaceholder = ({ text }) => createVNode("div", { "class": "k-image-placeholder k-image-error" }, [createVNode("svg", {
7408
7939
  "viewBox": "0 0 24 24",
7409
7940
  "fill": "none",
7410
7941
  "class": "k-image-placeholder-icon"
@@ -7421,9 +7952,10 @@ var ErrorPlaceholder = () => createVNode("div", { "class": "k-image-placeholder
7421
7952
  "stroke": "currentColor",
7422
7953
  "stroke-width": "1.5",
7423
7954
  "stroke-linecap": "round"
7424
- }, null)]), createVNode("span", { "class": "k-image-error-text" }, [createTextVNode("加载失败")])]);
7955
+ }, null)]), createVNode("span", { "class": "k-image-error-text" }, [text])]);
7425
7956
  var KImage_default = /* @__PURE__ */ defineComponent((_props, { slots, emit }) => {
7426
7957
  const cProps = _props;
7958
+ const t = useLocale();
7427
7959
  const { status, previewVisible, previewIndex, previewScale, previewRotate, handleLoad, handleError, openPreview, closePreview, previewPrev, previewNext, zoomIn, zoomOut, rotate } = useImage(cProps, (event, payload) => emit(event, payload));
7428
7960
  const hasPreview = computed(() => cProps.previewSrcList && cProps.previewSrcList.length > 0);
7429
7961
  return () => {
@@ -7437,7 +7969,7 @@ var KImage_default = /* @__PURE__ */ defineComponent((_props, { slots, emit }) =
7437
7969
  "onClick": hasPreview.value ? openPreview : void 0
7438
7970
  }, [
7439
7971
  status.value === "loading" && (slots.placeholder ? slots.placeholder() : createVNode(LoadingPlaceholder, null, null)),
7440
- status.value === "error" && (slots.error ? slots.error() : createVNode(ErrorPlaceholder, null, null)),
7972
+ status.value === "error" && (slots.error ? slots.error() : createVNode(ErrorPlaceholder, { "text": t.value.image.loadError }, null)),
7441
7973
  createVNode("img", {
7442
7974
  "class": ["k-image-inner", status.value !== "loaded" ? "k-image-inner-hidden" : ""],
7443
7975
  "src": cProps.src,
@@ -7458,23 +7990,23 @@ var KImage_default = /* @__PURE__ */ defineComponent((_props, { slots, emit }) =
7458
7990
  createVNode("button", {
7459
7991
  "class": "k-image-preview-btn",
7460
7992
  "onClick": zoomOut,
7461
- "title": "缩小"
7993
+ "title": t.value.image.zoomOut
7462
7994
  }, [createTextVNode("-")]),
7463
7995
  createVNode("span", { "class": "k-image-preview-scale" }, [Math.round(previewScale.value * 100), createTextVNode("%")]),
7464
7996
  createVNode("button", {
7465
7997
  "class": "k-image-preview-btn",
7466
7998
  "onClick": zoomIn,
7467
- "title": "放大"
7999
+ "title": t.value.image.zoomIn
7468
8000
  }, [createTextVNode("+")]),
7469
8001
  createVNode("button", {
7470
8002
  "class": "k-image-preview-btn",
7471
8003
  "onClick": rotate,
7472
- "title": "旋转"
8004
+ "title": t.value.image.rotate
7473
8005
  }, [createTextVNode("↻")]),
7474
8006
  createVNode("button", {
7475
8007
  "class": "k-image-preview-btn k-image-preview-close",
7476
8008
  "onClick": closePreview,
7477
- "title": "关闭"
8009
+ "title": t.value.image.close
7478
8010
  }, [createTextVNode("✕")])
7479
8011
  ]),
7480
8012
  createVNode("img", {
@@ -7595,6 +8127,7 @@ var { props: selectProps, useSelect } = SelectCore;
7595
8127
  var KSelect_default = kDefineComponent((_props, _ctx) => {
7596
8128
  const props = _props;
7597
8129
  const { slots, expose } = _ctx;
8130
+ const t = useLocale();
7598
8131
  const componentSize = useComponentSize(props);
7599
8132
  const { inputValue, isOpen, fetchLoading, inputReadonly, displayOptions, selectedTags, lastOptionRef, optionsContainerRef, onSelect, onDeleteTag, onInput, onFocus, onBlur, tools, updateFetchObserver } = useSelect(props, _ctx);
7600
8133
  const triggerRef = ref(null);
@@ -7696,7 +8229,7 @@ var KSelect_default = kDefineComponent((_props, _ctx) => {
7696
8229
  "class": "m-select-input k-select-input",
7697
8230
  "type": "text",
7698
8231
  "value": inputValue.value,
7699
- "placeholder": selectedTags.value.length === 0 ? props.placeholder : "",
8232
+ "placeholder": selectedTags.value.length === 0 ? props.placeholder ?? t.value.select.placeholder : "",
7700
8233
  "onInput": (e) => {
7701
8234
  inputValue.value = e.target.value;
7702
8235
  onInput();
@@ -7717,7 +8250,7 @@ var KSelect_default = kDefineComponent((_props, _ctx) => {
7717
8250
  "value": inputValue.value,
7718
8251
  "readonly": inputReadonly.value,
7719
8252
  "disabled": props.disabled,
7720
- "placeholder": props.placeholder,
8253
+ "placeholder": props.placeholder ?? t.value.select.placeholder,
7721
8254
  "onInput": (e) => {
7722
8255
  inputValue.value = e.target.value;
7723
8256
  onInput();
@@ -7728,7 +8261,7 @@ var KSelect_default = kDefineComponent((_props, _ctx) => {
7728
8261
  props.loading && createVNode("span", { "class": "k-select-loading-icon" }, null),
7729
8262
  showClear && !props.loading && createVNode("span", {
7730
8263
  "class": "k-select-clear",
7731
- "aria-label": "清空",
8264
+ "aria-label": t.value.select.clear,
7732
8265
  "onMousedown": (e) => {
7733
8266
  e.preventDefault();
7734
8267
  e.stopPropagation();
@@ -7758,7 +8291,7 @@ var KSelect_default = kDefineComponent((_props, _ctx) => {
7758
8291
  if (i === lastIndex) lastOptionRef.value = el;
7759
8292
  },
7760
8293
  "onClick": () => handleOptionClick(o)
7761
- }, [getOptionDisplay(o.value)])) : createVNode("div", { "class": "m-select-empty k-select-empty" }, [slots.empty ? slots.empty() : createVNode("span", null, [createTextVNode("暂无数据")])])]), fetchLoading.value && createVNode("div", { "class": "m-select-loading k-select-loading" }, [createTextVNode("加载中...")])])] });
8294
+ }, [getOptionDisplay(o.value)])) : createVNode("div", { "class": "m-select-empty k-select-empty" }, [slots.empty ? slots.empty() : createVNode("span", null, [t.value.select.noData])])]), fetchLoading.value && createVNode("div", { "class": "m-select-loading k-select-loading" }, [t.value.select.loading])])] });
7762
8295
  };
7763
8296
  return createVNode("div", { "class": [
7764
8297
  "m-select",
@@ -8286,6 +8819,14 @@ var FetchTransport = class {
8286
8819
  };
8287
8820
  //#endregion
8288
8821
  //#region composables/request/types.ts
8822
+ /**
8823
+ * @description 请求模块类型定义
8824
+ * @author 阿怪
8825
+ * @date 2026/3/15
8826
+ * @version v0.0.1
8827
+ *
8828
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8829
+ */
8289
8830
  /** 业务错误:后端返回 success=false */
8290
8831
  var BusinessError = class extends Error {
8291
8832
  name = "BusinessError";
@@ -8303,13 +8844,14 @@ var NetworkRequestError = class extends Error {
8303
8844
  }
8304
8845
  };
8305
8846
  function networkErrorMessage(e) {
8847
+ const t = getActiveLocaleMessages();
8306
8848
  switch (e.type) {
8307
- case "invalidURL": return `无效的 URL: ${e.url}`;
8308
- case "timeout": return "请求超时";
8849
+ case "invalidURL": return t.request.invalidUrl(e.url);
8850
+ case "timeout": return t.request.timeout;
8309
8851
  case "httpError": return `HTTP ${e.status} ${e.statusText}`;
8310
- case "decodingFailed": return `解码失败: ${e.reason}`;
8311
- case "aborted": return "请求已取消";
8312
- case "unknown": return `未知错误: ${String(e.error)}`;
8852
+ case "decodingFailed": return t.request.decodeFailed(e.reason);
8853
+ case "aborted": return t.request.canceled;
8854
+ case "unknown": return t.request.unknownError(String(e.error));
8313
8855
  }
8314
8856
  }
8315
8857
  //#endregion
@@ -8344,11 +8886,12 @@ function toQueryString(params) {
8344
8886
  }
8345
8887
  /** 根据 Content-Type 解析响应,并检查 WrappedResponse */
8346
8888
  async function unwrapResponse(response) {
8889
+ const t = getActiveLocaleMessages();
8347
8890
  if (response.status < 200 || response.status >= 300) {
8348
8891
  if (response.status >= 400) {
8349
8892
  if ((response.headers["content-type"] ?? "").includes("application/json")) try {
8350
8893
  const json = await response.json();
8351
- if (isWrappedResponse$1(json) && !json.success) throw new BusinessError(json.message || `请求失败(${response.status})`);
8894
+ if (isWrappedResponse$1(json) && !json.success) throw new BusinessError(json.message || t.request.failedWithStatus(response.status));
8352
8895
  } catch (e) {
8353
8896
  if (e instanceof BusinessError) throw e;
8354
8897
  }
@@ -8362,7 +8905,7 @@ async function unwrapResponse(response) {
8362
8905
  if ((response.headers["content-type"] ?? "").includes("application/json")) {
8363
8906
  const json = await response.json();
8364
8907
  if (isWrappedResponse$1(json)) {
8365
- if (!json.success) throw new BusinessError(json.message || "请求失败");
8908
+ if (!json.success) throw new BusinessError(json.message || t.request.failed);
8366
8909
  return json.data;
8367
8910
  }
8368
8911
  return json;
@@ -8461,6 +9004,7 @@ var RequestBuilder = class {
8461
9004
  return this;
8462
9005
  }
8463
9006
  async execute() {
9007
+ const t = getActiveLocaleMessages();
8464
9008
  const { transport, controlGate, baseURL, defaultHeaders, getToken, onUnauthorized, responseInterceptor, registerAbort } = this.context;
8465
9009
  let fullURL = this.url.startsWith("http") ? this.url : `${baseURL}${this.url}`;
8466
9010
  const abortController = new AbortController();
@@ -8516,12 +9060,12 @@ var RequestBuilder = class {
8516
9060
  "PUT",
8517
9061
  "PATCH",
8518
9062
  "DELETE"
8519
- ].includes(this.method)) this.context.feedback.showSuccess("操作成功");
9063
+ ].includes(this.method)) this.context.feedback.showSuccess(t.request.success);
8520
9064
  return finalResult;
8521
9065
  } catch (error) {
8522
9066
  if (error instanceof NetworkRequestError && error.detail.type === "httpError" && error.detail.status === 401) onUnauthorized?.();
8523
9067
  if (this.context.feedback) {
8524
- const msg = error instanceof BusinessError ? error.message : error instanceof NetworkRequestError ? `请求失败: ${error.message}` : "操作失败";
9068
+ const msg = error instanceof BusinessError ? error.message : error instanceof NetworkRequestError ? t.request.failedWithMsg(error.message) : t.request.opFailed;
8525
9069
  this.context.feedback.showError(msg);
8526
9070
  }
8527
9071
  throw error;
@@ -8716,6 +9260,7 @@ var defaultFeedbackHandler = {
8716
9260
  * createErrorHandler 和 useErrorHandler 共用此函数。
8717
9261
  */
8718
9262
  function dispatchError(error, options) {
9263
+ const t = getActiveLocaleMessages();
8719
9264
  const feedback = options.feedbackHandler ?? defaultFeedbackHandler;
8720
9265
  if (error instanceof BusinessError) {
8721
9266
  options.onBusinessError?.(error);
@@ -8732,28 +9277,28 @@ function dispatchError(error, options) {
8732
9277
  return;
8733
9278
  }
8734
9279
  if (detail.status === 403) {
8735
- feedback.showWarning("权限不足,无法执行此操作");
9280
+ feedback.showWarning(t.request.permissionDenied);
8736
9281
  return;
8737
9282
  }
8738
- feedback.showError(`请求失败 (${detail.status})`);
9283
+ feedback.showError(t.request.failedWithStatus(detail.status));
8739
9284
  return;
8740
9285
  case "timeout":
8741
- feedback.showWarning("请求超时,请稍后重试");
9286
+ feedback.showWarning(t.request.timeoutRetry);
8742
9287
  return;
8743
9288
  case "aborted": return;
8744
9289
  case "invalidURL":
8745
- feedback.showError(`无效的请求地址: ${detail.url}`);
9290
+ feedback.showError(t.request.invalidUrl(detail.url));
8746
9291
  return;
8747
9292
  case "decodingFailed":
8748
- feedback.showError("数据解析失败");
9293
+ feedback.showError(t.request.parseFailed);
8749
9294
  return;
8750
9295
  case "unknown":
8751
- feedback.showError("网络异常,请检查网络连接");
9296
+ feedback.showError(t.request.networkException);
8752
9297
  return;
8753
9298
  }
8754
9299
  }
8755
9300
  const message = error instanceof Error ? error.message : String(error);
8756
- feedback.showError(message || "发生了未知错误");
9301
+ feedback.showError(message || t.request.unknown);
8757
9302
  }
8758
9303
  //#endregion
8759
9304
  //#region composables/error/createErrorHandler.ts
@@ -8959,6 +9504,7 @@ function useCrudPage(config, tableBodyRef) {
8959
9504
  loading.value = true;
8960
9505
  try {
8961
9506
  const params = {
9507
+ ...config.defaultParams,
8962
9508
  page: page.value,
8963
9509
  pageSize: pageSize.value
8964
9510
  };
@@ -9021,6 +9567,7 @@ var KCrudPage_default = /* @__PURE__ */ defineComponent({
9021
9567
  required: true
9022
9568
  } },
9023
9569
  setup(props, { slots }) {
9570
+ const t = useLocale();
9024
9571
  const rootRef = ref();
9025
9572
  const tableBodyRef = computed(() => rootRef.value?.querySelector(".k-search-table-body"));
9026
9573
  const { page, pageSize, total, list, loading, filters, onPageChange, onSearch, onReset } = useCrudPage(props.config, tableBodyRef);
@@ -9109,6 +9656,7 @@ var KCrudPage_default = /* @__PURE__ */ defineComponent({
9109
9656
  if (!props.config.filters?.length) return null;
9110
9657
  return props.config.filters.map((f) => createVNode("div", {
9111
9658
  "class": "k-crud-page-filter-item",
9659
+ "data-k": `filter:${f.param}`,
9112
9660
  "key": f.param
9113
9661
  }, [createVNode("label", { "class": "k-crud-page-filter-label" }, [f.label]), f.type === "select" ? createVNode(KSelect_default, {
9114
9662
  "class": "k-crud-page-filter-control",
@@ -9116,13 +9664,13 @@ var KCrudPage_default = /* @__PURE__ */ defineComponent({
9116
9664
  "options": f.options ?? [],
9117
9665
  "optionParam": "label",
9118
9666
  "valueParam": "value",
9119
- "placeholder": f.placeholder ?? "全部",
9667
+ "placeholder": f.placeholder ?? t.value.crudPage.all,
9120
9668
  "onUpdate:modelValue": (v) => {
9121
9669
  filters[f.param] = v || void 0;
9122
9670
  }
9123
9671
  }, null) : createVNode(KInput_default, {
9124
9672
  "class": "k-crud-page-filter-control",
9125
- "placeholder": f.placeholder ?? `请输入${f.label}`,
9673
+ "placeholder": f.placeholder ?? t.value.crudPage.fieldPlaceholder(f.label),
9126
9674
  "modelValue": filters[f.param] ?? "",
9127
9675
  "onUpdate:modelValue": (v) => {
9128
9676
  filters[f.param] = v || void 0;
@@ -9131,6 +9679,7 @@ var KCrudPage_default = /* @__PURE__ */ defineComponent({
9131
9679
  };
9132
9680
  return () => createVNode("div", {
9133
9681
  "class": "k-crud-page",
9682
+ "data-k": "page:crudList",
9134
9683
  "ref": rootRef
9135
9684
  }, [createVNode(KPageHeader_default, { "title": props.config.title }, { extra: slots.headerExtra }), createVNode(KSearchTable_default, {
9136
9685
  "data": list.value,
@@ -9185,6 +9734,7 @@ var KLoginPage_default = /* @__PURE__ */ defineComponent({
9185
9734
  }
9186
9735
  },
9187
9736
  setup(props, ctx) {
9737
+ const t = useLocale();
9188
9738
  const username = ref("");
9189
9739
  const password = ref("");
9190
9740
  const remember = ref(false);
@@ -9204,17 +9754,17 @@ var KLoginPage_default = /* @__PURE__ */ defineComponent({
9204
9754
  "class": "k-login-form",
9205
9755
  "onSubmit": handleSubmit
9206
9756
  }, [
9207
- createVNode("div", { "class": "k-login-form-item" }, [createVNode("label", null, [createTextVNode("用户名")]), createVNode(KInput_default, {
9757
+ createVNode("div", { "class": "k-login-form-item" }, [createVNode("label", null, [t.value.login.username]), createVNode(KInput_default, {
9208
9758
  "modelValue": username.value,
9209
- "placeholder": "请输入用户名",
9759
+ "placeholder": t.value.login.usernamePlaceholder,
9210
9760
  "onUpdate:modelValue": (v) => {
9211
9761
  username.value = v;
9212
9762
  }
9213
9763
  }, null)]),
9214
- createVNode("div", { "class": "k-login-form-item" }, [createVNode("label", null, [createTextVNode("密码")]), createVNode(KInput_default, {
9764
+ createVNode("div", { "class": "k-login-form-item" }, [createVNode("label", null, [t.value.login.password]), createVNode(KInput_default, {
9215
9765
  "type": "password",
9216
9766
  "modelValue": password.value,
9217
- "placeholder": "请输入密码",
9767
+ "placeholder": t.value.login.passwordPlaceholder,
9218
9768
  "onUpdate:modelValue": (v) => {
9219
9769
  password.value = v;
9220
9770
  }
@@ -9225,10 +9775,10 @@ var KLoginPage_default = /* @__PURE__ */ defineComponent({
9225
9775
  "onChange": (e) => {
9226
9776
  remember.value = e.target.checked;
9227
9777
  }
9228
- }, null), createTextVNode("记住我")]),
9778
+ }, null), t.value.login.remember]),
9229
9779
  createVNode(KButton_default, {
9230
9780
  "type": "primary",
9231
- "text": props.loading ? "登录中..." : "登 录",
9781
+ "text": props.loading ? t.value.login.loggingIn : t.value.login.login,
9232
9782
  "disabled": props.loading,
9233
9783
  "loading": props.loading,
9234
9784
  "onClick": handleSubmit
@@ -9688,6 +10238,7 @@ var KUpload_default = /* @__PURE__ */ defineComponent({
9688
10238
  "error"
9689
10239
  ],
9690
10240
  setup(props, ctx) {
10241
+ const t = useLocale();
9691
10242
  const inputRef = ref(null);
9692
10243
  const isDragOver = ref(false);
9693
10244
  const openFileDialog = () => {
@@ -9695,7 +10246,7 @@ var KUpload_default = /* @__PURE__ */ defineComponent({
9695
10246
  inputRef.value?.click();
9696
10247
  };
9697
10248
  const validateFile = (file) => {
9698
- if (props.maxSize !== Infinity && file.size > props.maxSize) return `文件 "${file.name}" 超过大小限制(最大 ${formatSize$1(props.maxSize)})`;
10249
+ if (props.maxSize !== Infinity && file.size > props.maxSize) return t.value.upload.oversize(file.name, formatSize$1(props.maxSize));
9699
10250
  return null;
9700
10251
  };
9701
10252
  const processFiles = async (rawFiles) => {
@@ -9814,7 +10365,7 @@ var KUpload_default = /* @__PURE__ */ defineComponent({
9814
10365
  "accept": props.accept,
9815
10366
  "multiple": props.multiple,
9816
10367
  "onChange": onInputChange
9817
- }, null), ctx.slots.default?.() ?? createVNode("div", { "class": "k-upload-placeholder" }, [createVNode("span", { "class": "k-upload-icon" }, [createTextVNode("↑")]), createVNode("span", { "class": "k-upload-text" }, [props.drag ? "点击或拖拽文件到此处上传" : "点击上传文件"])])]);
10368
+ }, null), ctx.slots.default?.() ?? createVNode("div", { "class": "k-upload-placeholder" }, [createVNode("span", { "class": "k-upload-icon" }, [createTextVNode("↑")]), createVNode("span", { "class": "k-upload-text" }, [props.drag ? t.value.upload.trigger : t.value.upload.triggerSimple])])]);
9818
10369
  };
9819
10370
  }
9820
10371
  });
@@ -9878,6 +10429,7 @@ var KImageUpload_default = /* @__PURE__ */ defineComponent({
9878
10429
  "error"
9879
10430
  ],
9880
10431
  setup(props, ctx) {
10432
+ const t = useLocale();
9881
10433
  const inputRef = ref(null);
9882
10434
  const isDragOver = ref(false);
9883
10435
  const objectUrlMap = ref(/* @__PURE__ */ new Map());
@@ -9913,7 +10465,7 @@ var KImageUpload_default = /* @__PURE__ */ defineComponent({
9913
10465
  if (props.maxSize !== Infinity && raw.size > props.maxSize) {
9914
10466
  ctx.emit("error", {
9915
10467
  file: raw,
9916
- message: `文件 "${raw.name}" 超过大小限制`
10468
+ message: t.value.upload.oversize(raw.name)
9917
10469
  });
9918
10470
  continue;
9919
10471
  }
@@ -10027,7 +10579,7 @@ var KImageUpload_default = /* @__PURE__ */ defineComponent({
10027
10579
  "class": "k-image-upload-progress",
10028
10580
  "style": { height: `${file.percent}%` }
10029
10581
  }, null), createVNode("span", { "class": "k-image-upload-percent" }, [file.percent, createTextVNode("%")])]),
10030
- file.status === "error" && createVNode("div", { "class": "k-image-upload-overlay k-image-upload-overlay--error" }, [createVNode("span", null, [createTextVNode("上传失败")])]),
10582
+ file.status === "error" && createVNode("div", { "class": "k-image-upload-overlay k-image-upload-overlay--error" }, [createVNode("span", null, [t.value.upload.uploadFailed])]),
10031
10583
  !props.disabled && createVNode(KButton_default, {
10032
10584
  "class": "k-image-upload-remove",
10033
10585
  "plain": true,
@@ -10072,12 +10624,6 @@ var KImageUpload_default = /* @__PURE__ */ defineComponent({
10072
10624
  *
10073
10625
  * 江湖的业务千篇一律,复杂的代码好几百行。
10074
10626
  */
10075
- var STATUS_TEXT = {
10076
- pending: "等待中",
10077
- uploading: "上传中",
10078
- success: "已完成",
10079
- error: "上传失败"
10080
- };
10081
10627
  function formatSize(bytes) {
10082
10628
  if (bytes < 1024) return `${bytes} B`;
10083
10629
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -10097,6 +10643,16 @@ var KFileList_default = /* @__PURE__ */ defineComponent({
10097
10643
  },
10098
10644
  emits: ["remove"],
10099
10645
  setup(props, ctx) {
10646
+ const t = useLocale();
10647
+ const statusText = (status) => {
10648
+ const u = t.value.upload;
10649
+ return {
10650
+ pending: u.statusWaiting,
10651
+ uploading: u.statusUploading,
10652
+ success: u.statusDone,
10653
+ error: u.statusFailed
10654
+ }[status];
10655
+ };
10100
10656
  return () => {
10101
10657
  if (props.files.length === 0) return null;
10102
10658
  return createVNode("ul", { "class": "k-file-list" }, [props.files.map((file) => {
@@ -10120,7 +10676,7 @@ var KFileList_default = /* @__PURE__ */ defineComponent({
10120
10676
  }, [file.name]) : file.name]), createVNode("span", { "class": "k-file-list-meta" }, [
10121
10677
  formatSize(file.size),
10122
10678
  createTextVNode(" · "),
10123
- STATUS_TEXT[file.status]
10679
+ statusText(file.status)
10124
10680
  ])]),
10125
10681
  props.removable && createVNode(KButton_default, {
10126
10682
  "class": "k-file-list-remove",
@@ -10140,6 +10696,14 @@ var KFileList_default = /* @__PURE__ */ defineComponent({
10140
10696
  //#endregion
10141
10697
  //#region composables/router/defineCrudRoutes.ts
10142
10698
  /**
10699
+ * @description CRUD 路由便利函数 — 根据实体配置批量生成路由记录
10700
+ * @author 阿怪
10701
+ * @date 2026/2/26
10702
+ * @version v0.0.1
10703
+ *
10704
+ * 江湖的业务千篇一律,复杂的代码好几百行。
10705
+ */
10706
+ /**
10143
10707
  * 根据实体配置生成标准 CRUD 路由记录数组。
10144
10708
  *
10145
10709
  * 生成规则:
@@ -10166,6 +10730,7 @@ var KFileList_default = /* @__PURE__ */ defineComponent({
10166
10730
  function defineCrudRoutes(config) {
10167
10731
  const { name, path, permission, title, icon } = config;
10168
10732
  const resource = permission ?? name.toLowerCase();
10733
+ const t = getActiveLocaleMessages();
10169
10734
  const basePath = path.replace(/\/+$/, "");
10170
10735
  const opSpecs = [
10171
10736
  {
@@ -10182,7 +10747,7 @@ function defineCrudRoutes(config) {
10182
10747
  suffix: "/new",
10183
10748
  action: "create",
10184
10749
  component: config.new,
10185
- titleSuffix: " - 新建",
10750
+ titleSuffix: t.routes.createSuffix,
10186
10751
  keepAlive: false,
10187
10752
  hidden: true
10188
10753
  },
@@ -10191,7 +10756,7 @@ function defineCrudRoutes(config) {
10191
10756
  suffix: "/:id/edit",
10192
10757
  action: "update",
10193
10758
  component: config.modify,
10194
- titleSuffix: " - 编辑",
10759
+ titleSuffix: t.routes.editSuffix,
10195
10760
  keepAlive: false,
10196
10761
  hidden: true
10197
10762
  },
@@ -10200,7 +10765,7 @@ function defineCrudRoutes(config) {
10200
10765
  suffix: "/:id",
10201
10766
  action: "read",
10202
10767
  component: config.detail,
10203
- titleSuffix: " - 详情",
10768
+ titleSuffix: t.routes.detailSuffix,
10204
10769
  keepAlive: false,
10205
10770
  hidden: true
10206
10771
  }
@@ -10486,6 +11051,14 @@ function addTabFromRoute(tabStore, route) {
10486
11051
  }
10487
11052
  //#endregion
10488
11053
  //#region composables/request/transport/xhrTransport.ts
11054
+ /**
11055
+ * @description 基于 XMLHttpRequest 的传输层实现,支持上传进度
11056
+ * @author 阿怪
11057
+ * @date 2026/3/15
11058
+ * @version v0.0.1
11059
+ *
11060
+ * 江湖的业务千篇一律,复杂的代码好几百行。
11061
+ */
10489
11062
  /** 解析 XHR 响应头字符串为 Record */
10490
11063
  function parseResponseHeaders(rawHeaders) {
10491
11064
  const headers = {};
@@ -10501,6 +11074,7 @@ function parseResponseHeaders(rawHeaders) {
10501
11074
  }
10502
11075
  var XhrTransport = class {
10503
11076
  send(request) {
11077
+ const t = getActiveLocaleMessages();
10504
11078
  return new Promise((resolve, reject) => {
10505
11079
  const xhr = new XMLHttpRequest();
10506
11080
  xhr.open(request.method, request.url, true);
@@ -10545,7 +11119,7 @@ var XhrTransport = class {
10545
11119
  xhr.onerror = () => {
10546
11120
  reject({
10547
11121
  type: "unknown",
10548
- error: /* @__PURE__ */ new Error("网络错误")
11122
+ error: new Error(t.request.networkError)
10549
11123
  });
10550
11124
  };
10551
11125
  xhr.ontimeout = () => {
@@ -10796,11 +11370,12 @@ function createUploader(uploaderOptions = {}) {
10796
11370
  return headers;
10797
11371
  }
10798
11372
  function doUpload(formData, options) {
11373
+ const t = getActiveLocaleMessages();
10799
11374
  const progress = ref(0);
10800
11375
  const abortController = new AbortController();
10801
11376
  return {
10802
11377
  progress,
10803
- abort: () => abortController.abort("上传已取消"),
11378
+ abort: () => abortController.abort(t.request.uploadCanceled),
10804
11379
  promise: transport.send({
10805
11380
  url: buildURL(options.url),
10806
11381
  method: "POST",
@@ -10821,7 +11396,7 @@ function createUploader(uploaderOptions = {}) {
10821
11396
  if ((response.headers["content-type"] ?? "").includes("application/json")) {
10822
11397
  const json = await response.json();
10823
11398
  if (isWrappedResponse(json)) {
10824
- if (!json.success) throw new BusinessError(json.message || "上传失败");
11399
+ if (!json.success) throw new BusinessError(json.message || t.request.uploadFailed);
10825
11400
  return json.data;
10826
11401
  }
10827
11402
  return json;
@@ -11003,22 +11578,10 @@ var KStickyActionBar_default = /* @__PURE__ */ defineComponent({
11003
11578
  * - approve: 绿色确认按钮
11004
11579
  * - reject: 红色确认按钮,备注必填
11005
11580
  */
11006
- var CONFIG = {
11007
- submit: {
11008
- title: "提交审批",
11009
- confirmText: "确认提交",
11010
- btnType: "primary"
11011
- },
11012
- approve: {
11013
- title: "审批通过",
11014
- confirmText: "确认通过",
11015
- btnType: "success"
11016
- },
11017
- reject: {
11018
- title: "驳回申请",
11019
- confirmText: "确认驳回",
11020
- btnType: "danger"
11021
- }
11581
+ var BTN_TYPE = {
11582
+ submit: "primary",
11583
+ approve: "success",
11584
+ reject: "danger"
11022
11585
  };
11023
11586
  var KApprovalDialog_default = /* @__PURE__ */ defineComponent({
11024
11587
  name: "KApprovalDialog",
@@ -11062,6 +11625,7 @@ var KApprovalDialog_default = /* @__PURE__ */ defineComponent({
11062
11625
  "cancel"
11063
11626
  ],
11064
11627
  setup(props, { emit, slots }) {
11628
+ const t = useLocale();
11065
11629
  const remark = ref("");
11066
11630
  const close = () => {
11067
11631
  emit("update:visible", false);
@@ -11080,9 +11644,19 @@ var KApprovalDialog_default = /* @__PURE__ */ defineComponent({
11080
11644
  };
11081
11645
  return () => {
11082
11646
  if (!props.visible) return null;
11083
- const cfg = CONFIG[props.type];
11084
- const displayTitle = props.title || cfg.title;
11085
- const placeholder = props.remarkPlaceholder || (props.type === "reject" ? "请输入驳回原因..." : "添加备注(可选)...");
11647
+ const ad = t.value.approvalDialog;
11648
+ const titleMap = {
11649
+ submit: ad.submitTitle,
11650
+ approve: ad.approveTitle,
11651
+ reject: ad.rejectTitle
11652
+ };
11653
+ const confirmMap = {
11654
+ submit: ad.submitConfirm,
11655
+ approve: ad.approveConfirm,
11656
+ reject: ad.rejectConfirm
11657
+ };
11658
+ const displayTitle = props.title || titleMap[props.type];
11659
+ const placeholder = props.remarkPlaceholder || (props.type === "reject" ? ad.rejectPlaceholder : ad.remarkPlaceholder);
11086
11660
  return createVNode("div", {
11087
11661
  "class": "k-approval-overlay",
11088
11662
  "onClick": close
@@ -11108,14 +11682,14 @@ var KApprovalDialog_default = /* @__PURE__ */ defineComponent({
11108
11682
  remark.value = v;
11109
11683
  }
11110
11684
  }, null),
11111
- isRemarkRequired() && createVNode("span", { "class": "k-approval-dialog-required-hint" }, [createTextVNode("* 必填")])
11685
+ isRemarkRequired() && createVNode("span", { "class": "k-approval-dialog-required-hint" }, [ad.required])
11112
11686
  ]),
11113
11687
  createVNode("div", { "class": "k-approval-dialog-footer" }, [createVNode(KButton_default, {
11114
- "text": "取消",
11688
+ "text": ad.cancel,
11115
11689
  "onClick": close
11116
11690
  }, null), createVNode(KButton_default, {
11117
- "type": cfg.btnType,
11118
- "text": props.loading ? "处理中..." : cfg.confirmText,
11691
+ "type": BTN_TYPE[props.type],
11692
+ "text": props.loading ? ad.processing : confirmMap[props.type],
11119
11693
  "disabled": !canConfirm(),
11120
11694
  "loading": props.loading,
11121
11695
  "onClick": confirm
@@ -11177,7 +11751,7 @@ function useFormPage(options) {
11177
11751
  }
11178
11752
  if (field.required) {
11179
11753
  if (value === void 0 || value === null || value === "") {
11180
- const msg = `请输入${field.label}`;
11754
+ const msg = getActiveLocaleMessages().form.inputPlaceholder(field.label);
11181
11755
  errors.value[param] = msg;
11182
11756
  return msg;
11183
11757
  }
@@ -11251,6 +11825,7 @@ function useFormPage(options) {
11251
11825
  * @param slots 组件 slots,用于支持 `field-{param}` 自定义插槽
11252
11826
  */
11253
11827
  function renderFormField(field, formData, errors, validateField, slots) {
11828
+ const t = getActiveLocaleMessages();
11254
11829
  const customSlot = slots[`field-${field.param}`];
11255
11830
  if (customSlot) return customSlot({
11256
11831
  formData,
@@ -11269,7 +11844,7 @@ function renderFormField(field, formData, errors, validateField, slots) {
11269
11844
  field.disabled && "k-fp-input--disabled"
11270
11845
  ].filter(Boolean),
11271
11846
  "modelValue": val,
11272
- "placeholder": field.placeholder ?? `请输入${field.label}`,
11847
+ "placeholder": field.placeholder ?? t.form.inputPlaceholder(field.label),
11273
11848
  "disabled": field.disabled,
11274
11849
  "readonly": field.readonly,
11275
11850
  "onUpdate:modelValue": (v) => {
@@ -11287,7 +11862,7 @@ function renderFormField(field, formData, errors, validateField, slots) {
11287
11862
  field.disabled && "k-fp-input--disabled"
11288
11863
  ].filter(Boolean),
11289
11864
  "modelValue": val,
11290
- "placeholder": field.placeholder ?? `请输入${field.label}`,
11865
+ "placeholder": field.placeholder ?? t.form.inputPlaceholder(field.label),
11291
11866
  "disabled": field.disabled,
11292
11867
  "readonly": field.readonly,
11293
11868
  "onUpdate:modelValue": (v) => {
@@ -11303,7 +11878,7 @@ function renderFormField(field, formData, errors, validateField, slots) {
11303
11878
  "options": field.options ?? [],
11304
11879
  "optionParam": "label",
11305
11880
  "valueParam": "value",
11306
- "placeholder": field.placeholder ?? `请选择${field.label}`,
11881
+ "placeholder": field.placeholder ?? t.form.selectPlaceholder(field.label),
11307
11882
  "disabled": field.disabled,
11308
11883
  "readonly": field.readonly,
11309
11884
  "onUpdate:modelValue": (v) => {
@@ -11326,7 +11901,7 @@ function renderFormField(field, formData, errors, validateField, slots) {
11326
11901
  field.disabled && "k-fp-input--disabled"
11327
11902
  ].filter(Boolean),
11328
11903
  "modelValue": val,
11329
- "placeholder": field.placeholder ?? `请输入${field.label}`,
11904
+ "placeholder": field.placeholder ?? t.form.inputPlaceholder(field.label),
11330
11905
  "disabled": field.disabled,
11331
11906
  "readonly": field.readonly,
11332
11907
  "onUpdate:modelValue": (v) => {
@@ -11356,6 +11931,7 @@ var KFormPage_default = /* @__PURE__ */ defineComponent({
11356
11931
  required: true
11357
11932
  } },
11358
11933
  setup(props, { slots }) {
11934
+ const t = useLocale();
11359
11935
  const { formData, isEdit, loading, submitting, errors, validateField, submit, saveDraft, goBack } = useFormPage({
11360
11936
  api: props.config.api,
11361
11937
  fields: props.config.fields,
@@ -11364,8 +11940,8 @@ var KFormPage_default = /* @__PURE__ */ defineComponent({
11364
11940
  });
11365
11941
  const cols = props.config.columns ?? 2;
11366
11942
  return () => {
11367
- if (loading.value) return createVNode("div", { "class": "k-fp-loading" }, [createTextVNode("加载中...")]);
11368
- const title = isEdit.value ? `编辑${props.config.title}` : `新建${props.config.title}`;
11943
+ if (loading.value) return createVNode("div", { "class": "k-fp-loading" }, [t.value.formPage.loading]);
11944
+ const title = isEdit.value ? t.value.formPage.editTitle(props.config.title) : t.value.formPage.createTitle(props.config.title);
11369
11945
  return createVNode("div", { "class": "k-form-page-wrapper" }, [createVNode("div", { "class": "k-form-page" }, [
11370
11946
  createVNode("div", { "class": "k-fp-header" }, [createVNode("div", { "class": "k-fp-header-left" }, [createVNode(KButton_default, {
11371
11947
  "text": "←",
@@ -11395,17 +11971,17 @@ var KFormPage_default = /* @__PURE__ */ defineComponent({
11395
11971
  submitting
11396
11972
  }) ?? createVNode(Fragment, null, [
11397
11973
  createVNode(KButton_default, {
11398
- "text": "取消",
11974
+ "text": t.value.formPage.cancel,
11399
11975
  "onClick": goBack
11400
11976
  }, null),
11401
11977
  props.config.showDraft && createVNode(KButton_default, {
11402
- "text": "保存草稿",
11978
+ "text": t.value.formPage.saveDraft,
11403
11979
  "disabled": submitting.value,
11404
11980
  "onClick": () => saveDraft()
11405
11981
  }, null),
11406
11982
  createVNode(KButton_default, {
11407
11983
  "type": "primary",
11408
- "text": submitting.value ? "保存中..." : props.config.submitText ?? "保存",
11984
+ "text": submitting.value ? t.value.formPage.saving : props.config.submitText ?? t.value.formPage.save,
11409
11985
  "disabled": submitting.value,
11410
11986
  "loading": submitting.value,
11411
11987
  "onClick": () => submit()
@@ -11459,11 +12035,11 @@ var KEditableTable_default = /* @__PURE__ */ defineComponent({
11459
12035
  },
11460
12036
  summaryLabel: {
11461
12037
  type: String,
11462
- default: "合计"
12038
+ default: void 0
11463
12039
  },
11464
12040
  addText: {
11465
12041
  type: String,
11466
- default: "+ 添加行"
12042
+ default: void 0
11467
12043
  },
11468
12044
  addable: {
11469
12045
  type: Boolean,
@@ -11480,6 +12056,7 @@ var KEditableTable_default = /* @__PURE__ */ defineComponent({
11480
12056
  "remove"
11481
12057
  ],
11482
12058
  setup(props, { emit, slots }) {
12059
+ const t = useLocale();
11483
12060
  const deletingId = ref(null);
11484
12061
  const updateCell = (rowIdx, param, value) => {
11485
12062
  const rows = [...props.modelValue];
@@ -11552,7 +12129,7 @@ var KEditableTable_default = /* @__PURE__ */ defineComponent({
11552
12129
  return createVNode("div", { "class": "k-editable-table" }, [
11553
12130
  (props.title || slots.header) && createVNode("div", { "class": "k-et-header" }, [createVNode("div", { "class": "k-et-header-left" }, [props.title && createVNode("span", { "class": "k-et-title" }, [props.title]), rows.length > 0 && createVNode("span", { "class": "k-et-count" }, [rows.length, createTextVNode(" items")])]), createVNode("div", { "class": "k-et-header-right" }, [slots.headerExtra?.(), props.addable && createVNode(KButton_default, {
11554
12131
  "plain": true,
11555
- "text": props.addText,
12132
+ "text": props.addText ?? t.value.editableTable.addRow,
11556
12133
  "onClick": addRow
11557
12134
  }, null)])]),
11558
12135
  createVNode("table", { "class": "k-et-table" }, [
@@ -11574,17 +12151,17 @@ var KEditableTable_default = /* @__PURE__ */ defineComponent({
11574
12151
  "colspan": cols.length + (props.showActions ? 1 : 0)
11575
12152
  }, [createVNode("span", { "class": "k-et-delete-confirm" }, [
11576
12153
  createVNode("s", { "class": "k-et-deleted-text" }, [row[cols[0].param] ?? ""]),
11577
- createVNode("span", { "class": "k-et-delete-ask" }, [createTextVNode("确定删除这一行?")]),
12154
+ createVNode("span", { "class": "k-et-delete-ask" }, [t.value.editableTable.confirmDelete]),
11578
12155
  createVNode(KButton_default, {
11579
12156
  "type": "danger",
11580
12157
  "size": "small",
11581
- "text": "删除",
12158
+ "text": t.value.editableTable.delete,
11582
12159
  "onClick": confirmDelete
11583
12160
  }, null),
11584
12161
  createVNode(KButton_default, {
11585
12162
  "plain": true,
11586
12163
  "size": "small",
11587
- "text": "取消",
12164
+ "text": t.value.editableTable.cancel,
11588
12165
  "onClick": cancelDelete
11589
12166
  }, null)
11590
12167
  ])])]);
@@ -11610,13 +12187,13 @@ var KEditableTable_default = /* @__PURE__ */ defineComponent({
11610
12187
  cols.map((col, ci) => createVNode("td", {
11611
12188
  "key": col.param,
11612
12189
  "class": ["k-et-td k-et-td-summary", col.align && `k-et-align-${col.align}`]
11613
- }, [ci === 0 ? props.summaryLabel : "", col.summary ? createVNode("span", { "class": "k-et-summary-value" }, [props.currencyPrefix && col.summaryFormat === void 0 && ci === cols.length - 1 ? `${props.currencyPrefix} ` : "", getSummary(col)]) : ""])),
12190
+ }, [ci === 0 ? props.summaryLabel ?? t.value.editableTable.summary : "", col.summary ? createVNode("span", { "class": "k-et-summary-value" }, [props.currencyPrefix && col.summaryFormat === void 0 && ci === cols.length - 1 ? `${props.currencyPrefix} ` : "", getSummary(col)]) : ""])),
11614
12191
  props.showActions && createVNode("td", { "class": "k-et-td" }, null)
11615
12192
  ])])
11616
12193
  ]),
11617
- isEmpty && createVNode("div", { "class": "k-et-empty" }, [slots.empty?.() ?? createVNode(Fragment, null, [createVNode("span", { "class": "k-et-empty-text" }, [createTextVNode("暂无数据")]), props.addable && createVNode(KButton_default, {
12194
+ isEmpty && createVNode("div", { "class": "k-et-empty" }, [slots.empty?.() ?? createVNode(Fragment, null, [createVNode("span", { "class": "k-et-empty-text" }, [t.value.editableTable.noData]), props.addable && createVNode(KButton_default, {
11618
12195
  "type": "primary",
11619
- "text": "添加第一行",
12196
+ "text": t.value.editableTable.addFirstRow,
11620
12197
  "onClick": addRow
11621
12198
  }, null)])])
11622
12199
  ]);
@@ -11644,6 +12221,7 @@ var KMasterDetailPage_default = /* @__PURE__ */ defineComponent({
11644
12221
  } },
11645
12222
  setup(props, { slots }) {
11646
12223
  const router = useRouter();
12224
+ const t = useLocale();
11647
12225
  const { formData, isEdit, loading, submitting, errors, validateField, submit, saveDraft, goBack } = useFormPage({
11648
12226
  api: props.config.api,
11649
12227
  fields: props.config.fields,
@@ -11670,8 +12248,8 @@ var KMasterDetailPage_default = /* @__PURE__ */ defineComponent({
11670
12248
  formData[param] = newRows;
11671
12249
  };
11672
12250
  return () => {
11673
- if (loading.value) return createVNode("div", { "class": "k-fp-loading" }, [createTextVNode("加载中...")]);
11674
- const title = isEdit.value ? `${props.config.title}` : `新建${props.config.title}`;
12251
+ if (loading.value) return createVNode("div", { "class": "k-fp-loading" }, [t.value.formPage.loading]);
12252
+ const title = isEdit.value ? `${props.config.title}` : t.value.formPage.createTitle(props.config.title);
11675
12253
  return createVNode("div", { "class": "k-form-page k-master-detail-page" }, [
11676
12254
  createVNode("div", { "class": "k-fp-header" }, [createVNode("div", { "class": "k-fp-header-left" }, [props.config.breadcrumb ? createVNode("div", { "class": "k-fp-breadcrumb" }, [props.config.breadcrumb.map((item, i) => createVNode(Fragment, null, [i > 0 && createVNode("span", { "class": "k-fp-breadcrumb-sep" }, [createTextVNode("›")]), item.path ? createVNode("a", {
11677
12255
  "class": "k-fp-breadcrumb-link",
@@ -11680,7 +12258,7 @@ var KMasterDetailPage_default = /* @__PURE__ */ defineComponent({
11680
12258
  "text": "←",
11681
12259
  "onClick": goBack
11682
12260
  }, null), createVNode("h1", { "class": "k-fp-title" }, [title])])]), createVNode("div", { "class": "k-fp-header-right" }, [slots.headerExtra?.({ formData })])]),
11683
- createVNode(KFormCard_default, { "title": "基本信息" }, { default: () => createVNode("div", { "class": `k-fp-grid k-fp-grid--${cols}` }, [props.config.fields.map((field) => {
12261
+ createVNode(KFormCard_default, { "title": t.value.formPage.basicInfo }, { default: () => createVNode("div", { "class": `k-fp-grid k-fp-grid--${cols}` }, [props.config.fields.map((field) => {
11684
12262
  const span = field.span === "full" ? cols : field.span ?? 1;
11685
12263
  return createVNode("div", {
11686
12264
  "key": field.param,
@@ -11718,17 +12296,17 @@ var KMasterDetailPage_default = /* @__PURE__ */ defineComponent({
11718
12296
  submitting
11719
12297
  }) ?? createVNode(Fragment, null, [
11720
12298
  createVNode(KButton_default, {
11721
- "text": "取消",
12299
+ "text": t.value.formPage.cancel,
11722
12300
  "onClick": goBack
11723
12301
  }, null),
11724
12302
  props.config.showDraft && createVNode(KButton_default, {
11725
- "text": "保存草稿",
12303
+ "text": t.value.formPage.saveDraft,
11726
12304
  "disabled": submitting.value,
11727
12305
  "onClick": () => saveDraft()
11728
12306
  }, null),
11729
12307
  createVNode(KButton_default, {
11730
12308
  "type": "primary",
11731
- "text": submitting.value ? "保存中..." : props.config.submitText ?? "提交",
12309
+ "text": submitting.value ? t.value.formPage.saving : props.config.submitText ?? t.value.formPage.submit,
11732
12310
  "disabled": submitting.value,
11733
12311
  "loading": submitting.value,
11734
12312
  "onClick": () => submit()