@veltra/desktop 1.2.7 → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
  4. package/dist/components/batch-edit/batch-edit-form.js +41 -79
  5. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  6. package/dist/components/batch-edit/batch-edit-list.js +17 -29
  7. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  8. package/dist/components/batch-edit/batch-edit.js +34 -60
  9. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  10. package/dist/components/batch-edit/batch-edit.vue.d.ts +3 -14
  11. package/dist/components/batch-edit/di.js.map +1 -1
  12. package/dist/components/batch-edit/form-action-header.js +25 -0
  13. package/dist/components/batch-edit/form-action-header.js.map +1 -0
  14. package/dist/components/batch-edit/style2.css +0 -13
  15. package/dist/components/batch-edit/use-edit-state.js +40 -0
  16. package/dist/components/batch-edit/use-edit-state.js.map +1 -0
  17. package/dist/components/batch-edit/use-handlers.js +146 -0
  18. package/dist/components/batch-edit/use-handlers.js.map +1 -0
  19. package/dist/components/batch-edit/use-shortcut-key.js +44 -0
  20. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -0
  21. package/dist/components/cascade/cascade.vue.d.ts +4 -4
  22. package/dist/components/checkbox/checkbox-button.vue.d.ts +2 -2
  23. package/dist/components/checkbox/checkbox.vue.d.ts +1 -1
  24. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  25. package/dist/components/code-editor/code-editor.vue.d.ts +1 -1
  26. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  27. package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
  28. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
  29. package/dist/components/empty/empty.vue.d.ts +1 -1
  30. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  31. package/dist/components/form/form.js.map +1 -1
  32. package/dist/components/form/form.vue.d.ts +5 -1
  33. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +8 -2
  34. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/form-item/form-item.js.map +1 -1
  36. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +3 -2
  37. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/input/input.vue.d.ts +8 -8
  39. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  40. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +6 -6
  41. package/dist/components/number-input/number-input.vue.d.ts +4 -4
  42. package/dist/components/number-range-input/number-range-input.vue.d.ts +2 -2
  43. package/dist/components/palette/palette.vue.d.ts +1 -1
  44. package/dist/components/password-input/password-input.vue.d.ts +2 -2
  45. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  46. package/dist/components/progress-nodes/progress-nodes.vue.d.ts +1 -1
  47. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  48. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +2 -2
  49. package/dist/components/scroll/scroll.vue.d.ts +2 -2
  50. package/dist/components/select/select.vue.d.ts +2 -2
  51. package/dist/components/steps/steps.vue.d.ts +3 -3
  52. package/dist/components/switch/switch.vue.d.ts +1 -1
  53. package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
  54. package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
  55. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  56. package/dist/components/textarea/textarea.vue.d.ts +4 -4
  57. package/dist/components/tip/tip.vue.d.ts +1 -1
  58. package/dist/components/tree/tree.vue.d.ts +1 -1
  59. package/dist/components/tree-select/tree-select.vue.d.ts +6 -6
  60. package/dist/index.d.ts +4 -4
  61. package/dist/types/batch-edit.d.ts +38 -12
  62. package/dist/types/form.d.ts +4 -1
  63. package/dist/types/index.d.ts +3 -3
  64. package/package.json +6 -6
  65. package/src/components/batch-edit/batch-edit-form.vue +40 -86
  66. package/src/components/batch-edit/batch-edit-list.vue +13 -29
  67. package/src/components/batch-edit/batch-edit.vue +27 -92
  68. package/src/components/batch-edit/di.ts +6 -5
  69. package/src/components/batch-edit/form-action-header.ts +21 -0
  70. package/src/components/batch-edit/style.scss +0 -14
  71. package/src/components/batch-edit/use-dialog-effect.ts +21 -0
  72. package/src/components/batch-edit/use-edit-state.ts +55 -0
  73. package/src/components/batch-edit/use-handlers.ts +226 -0
  74. package/src/components/batch-edit/use-shortcut-key.ts +58 -0
  75. package/src/components/form/form.vue +10 -3
  76. package/src/components/form-item/form-item.vue +4 -2
  77. package/src/types/batch-edit.ts +43 -22
  78. package/src/types/form.ts +4 -0
  79. package/dist/components/batch-edit/use-edit.js +0 -215
  80. package/dist/components/batch-edit/use-edit.js.map +0 -1
  81. package/src/components/batch-edit/use-edit.ts +0 -343
@@ -1,17 +1,7 @@
1
- import { TableColumn, TableColumnSlotsScope, TableRow } from "../../types/table.js";
2
- import { BatchEditMode, BatchEditProps } from "../../types/batch-edit.js";
1
+ import { TableColumn, TableRow } from "../../types/table.js";
2
+ import { BatchEditProps, BatchEditSlots } from "../../types/batch-edit.js";
3
3
  //#region src/components/batch-edit/batch-edit.vue.d.ts
4
- type __VLS_Slots = {
5
- form?: (props: {
6
- /** 当前编辑的层级 */depth?: number; /** 当前编辑的行 */
7
- row?: TableRow; /** 当前编辑的行索引 */
8
- index?: number; /** 操作的目标行索引路径 */
9
- indexes?: number[];
10
- }) => any;
11
- header?: () => any;
12
- } & Partial<{
13
- [key: `column:${string}`]: (props: TableColumnSlotsScope) => any;
14
- }>;
4
+ type __VLS_Slots = BatchEditSlots;
15
5
  declare const __VLS_base: import("vue").DefineComponent<BatchEditProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
16
6
  "update:selected": (value: Record<string, any> | undefined) => any;
17
7
  "update:checked": (value: Record<string, any>[]) => any;
@@ -32,7 +22,6 @@ declare const __VLS_base: import("vue").DefineComponent<BatchEditProps, {}, {},
32
22
  "onRow-click"?: ((row: TableRow, ev: MouseEvent) => any) | undefined;
33
23
  }>, {
34
24
  cols: string | [string, string];
35
- mode: BatchEditMode;
36
25
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
37
26
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
38
27
  declare const _default: typeof __VLS_export;
@@ -1 +1 @@
1
- {"version":3,"file":"di.js","names":[],"sources":["../../../src/components/batch-edit/di.ts"],"sourcesContent":["import type { BEM } from '@veltra/utils'\nimport type { ComputedRef, InjectionKey, ShallowRef, Ref } from 'vue'\n\nimport type {\n BatchEditEmits,\n BatchEditFeature,\n BatchEditProps,\n TableExposed,\n TableRow\n} from '../../types'\nimport type { EditReturned } from './use-edit'\nexport const BatchEditDIKey: InjectionKey<\n {\n cls: BEM<'batch-edit', 'u-'>\n props: BatchEditProps\n emit: BatchEditEmits\n tableRef: ShallowRef<TableExposed | undefined>\n staticFeatures: ComputedRef<Set<BatchEditFeature>>\n dynamicFeatures: ComputedRef<\n Record<BatchEditFeature, ((row?: TableRow) => boolean) | undefined>\n >\n /** 组件或其子节点是否处于聚焦状态 */\n focused: Ref<boolean>\n } & EditReturned\n> = Symbol('BatchEditDIKey')\n"],"mappings":";AAWA,MAAa,iBAaT,OAAO,gBAAgB"}
1
+ {"version":3,"file":"di.js","names":[],"sources":["../../../src/components/batch-edit/di.ts"],"sourcesContent":["import type { BEM } from '@veltra/utils'\nimport type { ComputedRef, InjectionKey, ShallowRef } from 'vue'\n\nimport type {\n BatchEditEmits,\n BatchEditFeature,\n BatchEditProps,\n BatchEditStates,\n TableRow\n} from '../../types'\nimport type { EditReturned } from './use-handlers'\n\nexport const BatchEditDIKey: InjectionKey<\n {\n cls: BEM<'batch-edit', 'u-'>\n props: BatchEditProps\n emit: BatchEditEmits\n state: BatchEditStates\n staticFeatures: ComputedRef<Set<BatchEditFeature>>\n dynamicFeatures: ComputedRef<\n Record<BatchEditFeature, ((row?: TableRow) => boolean) | undefined>\n >\n /** 组件或其子节点是否处于聚焦状态 */\n focused: ShallowRef<boolean>\n } & EditReturned\n> = Symbol('BatchEditDIKey')\n"],"mappings":";AAYA,MAAa,iBAaT,OAAO,gBAAgB"}
@@ -0,0 +1,25 @@
1
+ import { AddChild, EditPen, Plus, View } from "@veltra/icons/normal";
2
+ //#region src/components/batch-edit/form-action-header.ts
3
+ /** 按 formActionType 映射的表单头部信息 */
4
+ const FORM_ACTION_HEADER_MAP = {
5
+ create: {
6
+ icon: Plus,
7
+ title: "新增"
8
+ },
9
+ update: {
10
+ icon: EditPen,
11
+ title: "编辑"
12
+ },
13
+ view: {
14
+ icon: View,
15
+ title: "查看详情"
16
+ },
17
+ createChild: {
18
+ icon: AddChild,
19
+ title: "新增子级"
20
+ }
21
+ };
22
+ //#endregion
23
+ export { FORM_ACTION_HEADER_MAP };
24
+
25
+ //# sourceMappingURL=form-action-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-action-header.js","names":[],"sources":["../../../src/components/batch-edit/form-action-header.ts"],"sourcesContent":["import { AddChild, EditPen, Plus, View } from '@veltra/icons/normal'\nimport type { Component } from 'vue'\n\nimport type { BatchEditStates } from '../../types'\n\n/** 表单操作类型对应的头部展示信息 */\nexport interface FormActionHeaderInfo {\n icon: Component\n title: string\n}\n\n/** 按 formActionType 映射的表单头部信息 */\nexport const FORM_ACTION_HEADER_MAP: Record<\n BatchEditStates['formActionType'],\n FormActionHeaderInfo\n> = {\n create: { icon: Plus, title: '新增' },\n update: { icon: EditPen, title: '编辑' },\n view: { icon: View, title: '查看详情' },\n createChild: { icon: AddChild, title: '新增子级' }\n}\n"],"mappings":";;;AAYA,MAAa,yBAGT;CACF,QAAQ;EAAE,MAAM;EAAM,OAAO;CAAK;CAClC,QAAQ;EAAE,MAAM;EAAS,OAAO;CAAK;CACrC,MAAM;EAAE,MAAM;EAAM,OAAO;CAAO;CAClC,aAAa;EAAE,MAAM;EAAU,OAAO;CAAO;AAC/C"}
@@ -106,19 +106,6 @@
106
106
  color: var(--u-text-color-title);
107
107
  line-height: 1.25;
108
108
  }
109
- .u-batch-edit__form-chip {
110
- display: inline-block;
111
- padding: 1px 8px;
112
- font-size: 12px;
113
- line-height: 18px;
114
- border-radius: var(--u-radius-default);
115
- background-color: var(--u-bg-color-default);
116
- color: var(--u-text-color-second);
117
- white-space: nowrap;
118
- overflow: hidden;
119
- text-overflow: ellipsis;
120
- max-width: 60%;
121
- }
122
109
  .u-batch-edit__form-close {
123
110
  flex-shrink: 0;
124
111
  }
@@ -0,0 +1,40 @@
1
+ import { shallowReactive, watch } from "vue";
2
+ import { o } from "@cat-kit/core";
3
+ //#region src/components/batch-edit/use-edit-state.ts
4
+ function useEditState(options) {
5
+ const { props } = options;
6
+ const defaultState = {
7
+ depth: -1,
8
+ formVisible: false,
9
+ formActionType: "create",
10
+ loading: false,
11
+ indexPath: []
12
+ };
13
+ const state = shallowReactive({ ...defaultState });
14
+ function resetState() {
15
+ if (state.row) state.row.isCurrent = false;
16
+ Object.keys(state).forEach((key) => {
17
+ delete state[key];
18
+ });
19
+ Object.assign(state, defaultState);
20
+ }
21
+ watch(() => state.row, (row) => {
22
+ if (row) {
23
+ if (props.model) o(props.model).deepExtend(row.data);
24
+ state.formActionType = props.readonly ? "view" : "update";
25
+ state.formVisible = true;
26
+ state.depth = row.depth;
27
+ state.indexPath = [...row.indexes];
28
+ return;
29
+ }
30
+ resetState();
31
+ });
32
+ return {
33
+ state,
34
+ resetState
35
+ };
36
+ }
37
+ //#endregion
38
+ export { useEditState };
39
+
40
+ //# sourceMappingURL=use-edit-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-edit-state.js","names":[],"sources":["../../../src/components/batch-edit/use-edit-state.ts"],"sourcesContent":["import { o } from '@cat-kit/core'\nimport { shallowReactive, watch } from 'vue'\n\nimport type { BatchEditProps, BatchEditStates } from '../../types'\n\ninterface Options {\n props: BatchEditProps\n}\n\nexport function useEditState(options: Options) {\n const { props } = options\n\n const defaultState: BatchEditStates = {\n depth: -1,\n formVisible: false,\n formActionType: 'create',\n loading: false,\n indexPath: []\n }\n\n const state = shallowReactive({ ...defaultState })\n\n function resetState() {\n if (state.row) {\n state.row.isCurrent = false\n }\n\n Object.keys(state).forEach((key) => {\n delete state[key]\n })\n\n Object.assign(state, defaultState)\n }\n\n watch(\n () => state.row,\n (row) => {\n if (row) {\n if (props.model) {\n o(props.model).deepExtend(row.data)\n }\n state.formActionType = props.readonly ? 'view' : 'update'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n\n return\n }\n\n resetState()\n }\n )\n\n return { state, resetState }\n}\n"],"mappings":";;;AASA,SAAgB,aAAa,SAAkB;CAC7C,MAAM,EAAE,UAAU;CAElB,MAAM,eAAgC;EACpC,OAAO;EACP,aAAa;EACb,gBAAgB;EAChB,SAAS;EACT,WAAW,CAAC;CACd;CAEA,MAAM,QAAQ,gBAAgB,EAAE,GAAG,aAAa,CAAC;CAEjD,SAAS,aAAa;EACpB,IAAI,MAAM,KACR,MAAM,IAAI,YAAY;EAGxB,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;GAClC,OAAO,MAAM;EACf,CAAC;EAED,OAAO,OAAO,OAAO,YAAY;CACnC;CAEA,YACQ,MAAM,MACX,QAAQ;EACP,IAAI,KAAK;GACP,IAAI,MAAM,OACR,EAAE,MAAM,KAAK,EAAE,WAAW,IAAI,IAAI;GAEpC,MAAM,iBAAiB,MAAM,WAAW,SAAS;GACjD,MAAM,cAAc;GACpB,MAAM,QAAQ,IAAI;GAClB,MAAM,YAAY,CAAC,GAAG,IAAI,OAAO;GAEjC;EACF;EAEA,WAAW;CACb,CACF;CAEA,OAAO;EAAE;EAAO;CAAW;AAC7B"}
@@ -0,0 +1,146 @@
1
+ import { computed, nextTick } from "vue";
2
+ import { last, o } from "@cat-kit/core";
3
+ //#region src/components/batch-edit/use-handlers.ts
4
+ /** 按索引路径在原始数据树中定位节点(等价于历史 Forest.visit 路径访问) */
5
+ function visitDataByIndexPath(roots, indexPath, childrenKey) {
6
+ if (!indexPath.length) return void 0;
7
+ let currentList = roots;
8
+ let node;
9
+ for (let i = 0; i < indexPath.length; i++) {
10
+ const idx = indexPath[i];
11
+ if (!currentList) return void 0;
12
+ node = currentList[idx];
13
+ if (node === void 0) return void 0;
14
+ if (i < indexPath.length - 1) {
15
+ const next = node[childrenKey];
16
+ currentList = Array.isArray(next) ? next : void 0;
17
+ }
18
+ }
19
+ return node;
20
+ }
21
+ function useHandlers(options) {
22
+ const { props, emit, state, resetState, formRef } = options;
23
+ const childrenKey = computed(() => {
24
+ return typeof props.tree === "string" ? props.tree : "children";
25
+ });
26
+ function getInsertData() {
27
+ return o(props.model ?? {}).copy();
28
+ }
29
+ /**
30
+ * 插入数据
31
+ * @param item 数据
32
+ */
33
+ function insert(item) {
34
+ const data = [...props.data ?? []];
35
+ const parent = visitDataByIndexPath(data ?? [], state.indexPath.slice(0, -1), childrenKey.value);
36
+ if (parent) {
37
+ const children = parent[childrenKey.value];
38
+ if (!children) parent[childrenKey.value] = [item];
39
+ else children.splice(last(state.indexPath), 0, item);
40
+ } else data.splice(last(state.indexPath), 0, item);
41
+ emit("update:data", data);
42
+ return item;
43
+ }
44
+ /**
45
+ * 点击新增按钮
46
+ */
47
+ function handleCreate() {
48
+ const { data } = props;
49
+ resetState();
50
+ nextTick(() => {
51
+ state.formActionType = "create";
52
+ state.formVisible = true;
53
+ state.depth = 1;
54
+ state.indexPath = [data?.length ?? 0];
55
+ });
56
+ }
57
+ function handleInsertToPrev(row) {
58
+ resetState();
59
+ state.formActionType = "create";
60
+ state.formVisible = true;
61
+ state.depth = row.depth;
62
+ state.indexPath = [...row.indexes];
63
+ }
64
+ function handleInsertToNext(row) {
65
+ resetState();
66
+ state.formActionType = "create";
67
+ state.formVisible = true;
68
+ state.depth = row.depth;
69
+ state.indexPath = [...row.indexes.slice(0, -1), row.index + 1];
70
+ }
71
+ function handleInsertChild(row) {
72
+ resetState();
73
+ state.formActionType = "createChild";
74
+ state.formVisible = true;
75
+ state.parentRow = row;
76
+ state.depth = row.depth + 1;
77
+ row.expanded = true;
78
+ state.indexPath = [...row.indexes, row.children?.length ?? 0];
79
+ }
80
+ function runWithLoading(fn) {
81
+ return async (...args) => {
82
+ state.loading = true;
83
+ if (state.row) state.row.operating = true;
84
+ try {
85
+ await fn.apply(null, args);
86
+ } catch (error) {
87
+ console.error(error);
88
+ }
89
+ state.loading = false;
90
+ if (state.row) state.row.operating = false;
91
+ };
92
+ }
93
+ /** 保存 */
94
+ const handleSave = runWithLoading(async () => {
95
+ const { model, saveMethod } = props;
96
+ const valid = await formRef.value?.validate();
97
+ if (!model || !valid) return;
98
+ let item = getInsertData();
99
+ if (state.formActionType === "create" || state.formActionType === "createChild") {
100
+ await props.beforeCreate?.(item, state.parentRow?.data);
101
+ if (saveMethod) {
102
+ const result = await saveMethod(item, state.formActionType, state.parentRow?.data);
103
+ if (result) item = result;
104
+ }
105
+ insert(item);
106
+ return;
107
+ }
108
+ if (saveMethod) {
109
+ const result = await saveMethod(item, state.formActionType, state.parentRow?.data);
110
+ if (result) item = result;
111
+ }
112
+ const { row } = state;
113
+ row && Object.keys(item).forEach((key) => {
114
+ o(row.data).set(key, o(item).get(key));
115
+ });
116
+ });
117
+ const handleDelete = runWithLoading(async (row) => {
118
+ const { deleteMethod } = props;
119
+ if (deleteMethod) await deleteMethod([row.data]);
120
+ const data = [...props.data ?? []];
121
+ const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value);
122
+ if (parent) {
123
+ const children = parent[childrenKey.value];
124
+ if (children) children.splice(row.index, 1);
125
+ else console.error("行路径不正确");
126
+ } else data.splice(row.index, 1);
127
+ if (state.row === row) resetState();
128
+ emit("update:data", data);
129
+ });
130
+ function handleClose() {
131
+ resetState();
132
+ }
133
+ return {
134
+ handleDelete,
135
+ handleInsertChild,
136
+ handleInsertToNext,
137
+ handleInsertToPrev,
138
+ handleCreate,
139
+ handleClose,
140
+ handleSave
141
+ };
142
+ }
143
+ //#endregion
144
+ export { useHandlers };
145
+
146
+ //# sourceMappingURL=use-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-handlers.js","names":[],"sources":["../../../src/components/batch-edit/use-handlers.ts"],"sourcesContent":["import { last, o } from '@cat-kit/core'\nimport { computed, nextTick, type ShallowRef } from 'vue'\n\nimport type {\n TableRow,\n BatchEditEmits,\n BatchEditProps,\n BatchEditStates,\n FormExposed\n} from '../../types'\n\n/** 按索引路径在原始数据树中定位节点(等价于历史 Forest.visit 路径访问) */\nfunction visitDataByIndexPath<T extends Record<string, unknown>>(\n roots: T[],\n indexPath: number[],\n childrenKey: string\n): T | undefined {\n if (!indexPath.length) return undefined\n let currentList: T[] | undefined = roots\n let node: T | undefined\n for (let i = 0; i < indexPath.length; i++) {\n const idx = indexPath[i]!\n if (!currentList) return undefined\n node = currentList[idx]\n if (node === undefined) return undefined\n if (i < indexPath.length - 1) {\n const next = node[childrenKey]\n currentList = Array.isArray(next) ? (next as T[]) : undefined\n }\n }\n return node\n}\n\ninterface Options {\n props: BatchEditProps\n emit: BatchEditEmits\n state: BatchEditStates\n formRef: ShallowRef<FormExposed | null>\n resetState: () => void\n}\n\nexport interface EditReturned {\n handleSave: () => Promise<void>\n handleClose: () => void\n handleCreate: () => void\n handleDelete: (row: TableRow) => Promise<void>\n handleInsertToNext: (row: TableRow) => void\n handleInsertToPrev: (row: TableRow) => void\n handleInsertChild: (row: TableRow) => void\n}\n\nexport function useHandlers(options: Options): EditReturned {\n const { props, emit, state, resetState, formRef } = options\n\n const childrenKey = computed(() => {\n return typeof props.tree === 'string' ? props.tree : 'children'\n })\n\n function getInsertData(): Record<string, any> {\n return o(props.model ?? {}).copy()\n }\n\n /**\n * 插入数据\n * @param item 数据\n */\n function insert(item: Record<string, any>) {\n const data = [...(props.data ?? [])]\n\n const parent = visitDataByIndexPath(data ?? [], state.indexPath.slice(0, -1), childrenKey.value)\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (!children) {\n parent[childrenKey.value] = [item]\n } else {\n children.splice(last(state.indexPath), 0, item)\n }\n } else {\n data.splice(last(state.indexPath), 0, item)\n }\n\n emit('update:data', data)\n\n return item\n }\n\n /**\n * 点击新增按钮\n */\n function handleCreate() {\n const { data } = props\n\n resetState()\n\n nextTick(() => {\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = 1\n state.indexPath = [data?.length ?? 0]\n })\n }\n\n function handleInsertToPrev(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes]\n }\n\n function handleInsertToNext(row: TableRow) {\n resetState()\n state.formActionType = 'create'\n state.formVisible = true\n state.depth = row.depth\n state.indexPath = [...row.indexes.slice(0, -1), row.index + 1]\n }\n\n function handleInsertChild(row: TableRow) {\n resetState()\n state.formActionType = 'createChild'\n state.formVisible = true\n state.parentRow = row\n state.depth = row.depth + 1\n row.expanded = true\n state.indexPath = [...row.indexes, row.children?.length ?? 0]\n }\n\n function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {\n return async (...args: Arg) => {\n state.loading = true\n if (state.row) {\n state.row.operating = true\n }\n try {\n await fn.apply(null, args)\n } catch (error) {\n console.error(error)\n }\n state.loading = false\n if (state.row) {\n state.row.operating = false\n }\n }\n }\n\n /** 保存 */\n const handleSave = runWithLoading(async () => {\n const { model, saveMethod } = props\n const valid = await formRef.value?.validate()\n if (!model || !valid) return\n\n let item = getInsertData()\n\n // 新增\n if (state.formActionType === 'create' || state.formActionType === 'createChild') {\n await props.beforeCreate?.(item, state.parentRow?.data)\n if (saveMethod) {\n const result = await saveMethod(item, state.formActionType, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n insert(item)\n return\n }\n\n // 普通模式下的编辑\n if (saveMethod) {\n const result = await saveMethod(item, state.formActionType, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n\n const { row } = state\n row &&\n Object.keys(item).forEach((key) => {\n o(row.data).set(key, o(item).get(key))\n })\n })\n\n const handleDelete = runWithLoading(async (row: TableRow) => {\n const { deleteMethod } = props\n\n if (deleteMethod) {\n await deleteMethod([row.data])\n }\n\n const data = [...(props.data ?? [])]\n\n const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value)\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (children) {\n children.splice(row.index, 1)\n } else {\n console.error('行路径不正确')\n }\n } else {\n data.splice(row.index, 1)\n }\n\n if (state.row === row) {\n resetState()\n }\n\n emit('update:data', data)\n })\n\n function handleClose() {\n resetState()\n }\n\n return {\n handleDelete,\n handleInsertChild,\n handleInsertToNext,\n handleInsertToPrev,\n handleCreate,\n handleClose,\n handleSave\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { shallowRef } from "vue";
2
+ //#region src/components/batch-edit/use-shortcut-key.ts
3
+ function useShortcutKey(options) {
4
+ const { props, onClose, onSave, state } = options;
5
+ const focused = shallowRef(false);
6
+ function handleFocusIn() {
7
+ focused.value = true;
8
+ }
9
+ function handleFocusOut(e) {
10
+ if (!e.currentTarget.contains(e.relatedTarget)) focused.value = false;
11
+ }
12
+ function runEscape(e) {
13
+ e.preventDefault();
14
+ onClose();
15
+ }
16
+ function runSave(e) {
17
+ if (!state.formVisible) return;
18
+ if (props.quickEdit && state.formActionType === "update") return;
19
+ e.preventDefault();
20
+ onSave();
21
+ }
22
+ /**
23
+ * 键盘快捷键(仅组件获焦时生效):
24
+ * - Esc 关闭表单
25
+ * - ⌘/Ctrl + S 保存(快速编辑模式下编辑行时除外)
26
+ */
27
+ function handleKeydown(e) {
28
+ if (!focused.value) return;
29
+ if (props.readonly && e.key !== "Escape") return;
30
+ if (e.key === "Escape") return runEscape(e);
31
+ if (!(e.metaKey || e.ctrlKey)) return;
32
+ if (e.key.toLowerCase() === "s") return runSave(e);
33
+ }
34
+ return {
35
+ focused,
36
+ handleFocusIn,
37
+ handleFocusOut,
38
+ handleKeydown
39
+ };
40
+ }
41
+ //#endregion
42
+ export { useShortcutKey };
43
+
44
+ //# sourceMappingURL=use-shortcut-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-shortcut-key.js","names":[],"sources":["../../../src/components/batch-edit/use-shortcut-key.ts"],"sourcesContent":["import { shallowRef } from 'vue'\n\nimport type { BatchEditProps, BatchEditStates } from '../../types'\n\ninterface Options {\n props: BatchEditProps\n onSave: () => void\n onClose: () => void\n state: BatchEditStates\n}\nexport function useShortcutKey(options: Options) {\n const { props, onClose, onSave, state } = options\n const focused = shallowRef(false)\n\n function handleFocusIn() {\n focused.value = true\n }\n\n function handleFocusOut(e: FocusEvent) {\n const root = e.currentTarget as HTMLElement\n if (!root.contains(e.relatedTarget as Node | null)) {\n focused.value = false\n }\n }\n\n function runEscape(e: KeyboardEvent) {\n e.preventDefault()\n onClose()\n }\n\n function runSave(e: KeyboardEvent) {\n if (!state.formVisible) return\n if (props.quickEdit && state.formActionType === 'update') return\n e.preventDefault()\n onSave()\n }\n\n /**\n * 键盘快捷键(仅组件获焦时生效):\n * - Esc 关闭表单\n * - ⌘/Ctrl + S 保存(快速编辑模式下编辑行时除外)\n */\n function handleKeydown(e: KeyboardEvent) {\n if (!focused.value) return\n if (props.readonly && e.key !== 'Escape') return\n\n if (e.key === 'Escape') return runEscape(e)\n\n const meta = e.metaKey || e.ctrlKey\n if (!meta) return\n\n const key = e.key.toLowerCase()\n\n if (key === 's') return runSave(e)\n }\n\n return { focused, handleFocusIn, handleFocusOut, handleKeydown }\n}\n"],"mappings":";;AAUA,SAAgB,eAAe,SAAkB;CAC/C,MAAM,EAAE,OAAO,SAAS,QAAQ,UAAU;CAC1C,MAAM,UAAU,WAAW,KAAK;CAEhC,SAAS,gBAAgB;EACvB,QAAQ,QAAQ;CAClB;CAEA,SAAS,eAAe,GAAe;EAErC,IAAI,CADS,EAAE,cACL,SAAS,EAAE,aAA4B,GAC/C,QAAQ,QAAQ;CAEpB;CAEA,SAAS,UAAU,GAAkB;EACnC,EAAE,eAAe;EACjB,QAAQ;CACV;CAEA,SAAS,QAAQ,GAAkB;EACjC,IAAI,CAAC,MAAM,aAAa;EACxB,IAAI,MAAM,aAAa,MAAM,mBAAmB,UAAU;EAC1D,EAAE,eAAe;EACjB,OAAO;CACT;;;;;;CAOA,SAAS,cAAc,GAAkB;EACvC,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,MAAM,YAAY,EAAE,QAAQ,UAAU;EAE1C,IAAI,EAAE,QAAQ,UAAU,OAAO,UAAU,CAAC;EAG1C,IAAI,EADS,EAAE,WAAW,EAAE,UACjB;EAIX,IAFY,EAAE,IAAI,YAEZ,MAAM,KAAK,OAAO,QAAQ,CAAC;CACnC;CAEA,OAAO;EAAE;EAAS;EAAe;EAAgB;CAAc;AACjE"}
@@ -2,13 +2,15 @@ import { CascadeProps } from "../../types/cascade.js";
2
2
  //#region src/components/cascade/cascade.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<CascadeProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
4
4
  "update:modelValue": (value?: string | string[] | undefined) => any;
5
- clear: () => any;
6
5
  change: (...args: [value?: string | undefined, label?: string | undefined, item?: Record<string, any> | undefined] | [value: string[], label: string[], data: Record<string, any>[]]) => any;
6
+ clear: () => any;
7
7
  }, string, import("vue").PublicProps, Readonly<CascadeProps> & Readonly<{
8
8
  "onUpdate:modelValue"?: ((value?: string | string[] | undefined) => any) | undefined;
9
- onClear?: (() => any) | undefined;
10
9
  onChange?: ((...args: [value?: string | undefined, label?: string | undefined, item?: Record<string, any> | undefined] | [value: string[], label: string[], data: Record<string, any>[]]) => any) | undefined;
10
+ onClear?: (() => any) | undefined;
11
11
  }>, {
12
+ readonly: boolean;
13
+ disabled: boolean;
12
14
  labelKey: string;
13
15
  valueKey: string;
14
16
  childrenKey: string;
@@ -16,8 +18,6 @@ declare const __VLS_export: import("vue").DefineComponent<CascadeProps, {}, {},
16
18
  placeholder: string;
17
19
  clearable: boolean;
18
20
  filterable: boolean;
19
- disabled: boolean;
20
- readonly: boolean;
21
21
  visibilityLimit: number;
22
22
  separator: string;
23
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -17,9 +17,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
17
17
  "onUpdate:modelValue"?: ((checked: boolean) => any) | undefined;
18
18
  onChange?: ((checked: boolean) => any) | undefined;
19
19
  }>, {
20
- type: import("@veltra/utils").ColorType;
21
- disabled: boolean;
22
20
  readonly: boolean;
21
+ disabled: boolean;
22
+ type: import("@veltra/utils").ColorType;
23
23
  round: boolean;
24
24
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
25
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -18,8 +18,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
18
18
  "onUpdate:modelValue"?: ((checked: boolean) => any) | undefined;
19
19
  onChange?: ((checked: boolean, e: MouseEvent) => any) | undefined;
20
20
  }>, {
21
- disabled: boolean;
22
21
  readonly: boolean;
22
+ disabled: boolean;
23
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
24
24
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
25
25
  declare const _default: typeof __VLS_export;
@@ -12,10 +12,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
12
12
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
13
13
  "onUpdate:modelValue"?: ((value: any[]) => any) | undefined;
14
14
  }>, {
15
+ readonly: boolean;
16
+ disabled: boolean;
15
17
  labelKey: string;
16
18
  valueKey: string;
17
- disabled: boolean;
18
- readonly: boolean;
19
19
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
20
  declare const _default: typeof __VLS_export;
21
21
  //#endregion
@@ -10,8 +10,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
10
10
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
11
  "onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
12
12
  }>, {
13
- disabled: boolean;
14
13
  readonly: boolean;
14
+ disabled: boolean;
15
15
  dark: boolean;
16
16
  defaultLines: number;
17
17
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -7,8 +7,8 @@ declare const __VLS_export: import("vue").DefineComponent<DatePanelProps, {}, {}
7
7
  "onSelect:range-date"?: ((rangeDate?: [import("@cat-kit/core").Dater, import("@cat-kit/core").Dater] | undefined) => any) | undefined;
8
8
  "onSelect:date"?: ((date: import("@cat-kit/core").Dater) => any) | undefined;
9
9
  }>, {
10
- type: "date" | "month" | "year";
11
10
  size: import("@veltra/utils").ComponentSize;
11
+ type: "date" | "month" | "year";
12
12
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  //#endregion
@@ -5,11 +5,11 @@ declare const __VLS_export: import("vue").DefineComponent<DatePickerProps, {}, {
5
5
  }, string, import("vue").PublicProps, Readonly<DatePickerProps> & Readonly<{
6
6
  "onUpdate:modelValue"?: ((value?: string | number | Date | undefined) => any) | undefined;
7
7
  }>, {
8
+ readonly: boolean;
9
+ disabled: boolean;
8
10
  type: "date" | "month" | "year";
9
11
  placeholder: string;
10
12
  clearable: boolean;
11
- disabled: boolean;
12
- readonly: boolean;
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -5,11 +5,11 @@ declare const __VLS_export: import("vue").DefineComponent<DateRangePickerProps,
5
5
  }, string, import("vue").PublicProps, Readonly<DateRangePickerProps> & Readonly<{
6
6
  "onUpdate:modelValue"?: ((value?: [string, string] | undefined) => any) | undefined;
7
7
  }>, {
8
+ readonly: boolean;
9
+ disabled: boolean;
8
10
  type: "date" | "month" | "year";
9
11
  placeholder: [string, string];
10
12
  clearable: boolean;
11
- disabled: boolean;
12
- readonly: boolean;
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
15
15
  //#endregion
@@ -1,8 +1,8 @@
1
1
  import { EmptyProps } from "../../types/empty.js";
2
2
  //#region src/components/empty/empty.vue.d.ts
3
3
  declare const __VLS_export: import("vue").DefineComponent<EmptyProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<EmptyProps> & Readonly<{}>, {
4
- text: string;
5
4
  size: number;
5
+ text: string;
6
6
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
7
  declare const _default: typeof __VLS_export;
8
8
  //#endregion
@@ -11,8 +11,8 @@ declare const __VLS_base: import("vue").DefineComponent<UploaderProps, {}, {}, {
11
11
  }, string, import("vue").PublicProps, Readonly<UploaderProps> & Readonly<{
12
12
  onPick?: ((files: File[]) => any) | undefined;
13
13
  }>, {
14
- tag: string;
15
14
  multiple: boolean;
15
+ tag: string;
16
16
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
17
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
18
18
  declare const _default: typeof __VLS_export;
@@ -1 +1 @@
1
- {"version":3,"file":"form.js","names":[],"sources":["../../../src/components/form/form.vue"],"sourcesContent":["<template>\n <u-grid\n tag=\"form\"\n ref=\"grid\"\n @submit.prevent\n :cols=\"cols || breakpointCols\"\n :class=\"[cls.b, bem.is('readonly', readonly)]\"\n >\n <template\n v-for=\"{ node, isFormItem, formItemProps, field, modelValue } of getSlotsNodes()\"\n :key=\"node.key\"\n >\n <u-form-item v-if=\"!isFormItem && field\" v-bind=\"formItemProps\">\n <component\n :is=\"node\"\n :model-value=\"modelValue ?? o(model ?? {}).get(field)\"\n @update:model-value=\"handleUpdateValue(field, $event)\"\n />\n </u-form-item>\n\n <component v-else :is=\"node\" />\n </template>\n </u-grid>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { bem } from '@veltra/utils'\nimport { provideFormContext } from '@veltra/utils'\nimport { nextTick, shallowRef, toRef, useTemplateRef } from 'vue'\n\nimport type { BreakCols, GridExposed, FormProps, _FormExposed } from '../../types'\nimport { UFormItem } from '../form-item'\nimport { UGrid } from '../grid'\nimport { useFormFields } from './use-form-fields'\nimport { useNodeInterceptor } from './use-node-interceptor'\n\ndefineOptions({ name: 'Form' })\n\nconst props = defineProps<FormProps>()\n\ndefineSlots<{ default(props?: { data: Record<string, any> | undefined }): any }>()\n\nconst cls = bem('form')\nconst formItemCls = bem('form-item')\nconst gridRef = useTemplateRef('grid')\n\nconst breakpointCols: BreakCols = { xs: 1, md: 2, lg: 3, xl: 4, default: 4 }\n\nconst {\n validate: runValidate,\n clearValidate,\n reset,\n registerField,\n unregisterField,\n shouldValidate\n} = useFormFields({ props })\n\nasync function validate(keys?: string[]) {\n const valid = await runValidate(keys)\n if (!valid) {\n await nextTick()\n\n gridRef.value?.el\n ?.querySelector(`.${formItemCls.e('error-text')}`)\n ?.scrollIntoView({ block: 'center' })\n }\n return valid\n}\n\nprovideFormContext({\n formProps: props,\n registerField,\n unregisterField,\n validateFields: validate,\n shouldValidate\n})\n\nconst { getSlotsNodes } = useNodeInterceptor()\n\nfunction handleUpdateValue(field: string, value: any) {\n if (!props.model) return\n o(props.model).set(field, value)\n}\n\ndefineExpose<_FormExposed>({ el: toRef(() => gridRef.value?.el), validate, clearValidate, reset })\n</script>\n"],"mappings":""}
1
+ {"version":3,"file":"form.js","names":[],"sources":["../../../src/components/form/form.vue"],"sourcesContent":["<template>\n <u-grid\n tag=\"form\"\n ref=\"grid\"\n @submit.prevent\n :cols=\"cols || breakpointCols\"\n :class=\"[cls.b, bem.is('readonly', readonly)]\"\n >\n <template\n v-for=\"{ node, isFormItem, formItemProps, field, modelValue } of getSlotsNodes()\"\n :key=\"node.key\"\n >\n <u-form-item v-if=\"!isFormItem && field\" v-bind=\"formItemProps\">\n <component\n :is=\"node\"\n :model-value=\"modelValue ?? o(model ?? {}).get(field)\"\n @update:model-value=\"handleUpdateValue(field, $event)\"\n />\n </u-form-item>\n\n <component v-else :is=\"node\" />\n </template>\n </u-grid>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { bem } from '@veltra/utils'\nimport { provideFormContext } from '@veltra/utils'\nimport { nextTick, toRef, useTemplateRef } from 'vue'\n\nimport type { BreakCols, FormProps, _FormExposed, FormEmits } from '../../types'\nimport { UFormItem } from '../form-item'\nimport { UGrid } from '../grid'\nimport { useFormFields } from './use-form-fields'\nimport { useNodeInterceptor } from './use-node-interceptor'\n\ndefineOptions({ name: 'Form' })\n\nconst props = defineProps<FormProps>()\n\nconst emit = defineEmits<FormEmits>()\n\ndefineSlots<{ default(props?: { data: Record<string, any> | undefined }): any }>()\n\nconst cls = bem('form')\nconst formItemCls = bem('form-item')\nconst gridRef = useTemplateRef('grid')\n\nconst breakpointCols: BreakCols = { xs: 1, md: 2, lg: 3, xl: 4, default: 4 }\n\nconst {\n validate: runValidate,\n clearValidate,\n reset,\n registerField,\n unregisterField,\n shouldValidate\n} = useFormFields({ props })\n\nasync function validate(keys?: string[]) {\n const valid = await runValidate(keys)\n if (!valid) {\n await nextTick()\n\n gridRef.value?.el\n ?.querySelector(`.${formItemCls.e('error-text')}`)\n ?.scrollIntoView({ block: 'center' })\n }\n return valid\n}\n\nfunction handleFieldChange(field: string, value: any) {\n emit('field:change', field, value)\n}\n\nprovideFormContext({\n formProps: props,\n registerField,\n unregisterField,\n validateFields: validate,\n shouldValidate,\n handleFieldChange\n})\n\nconst { getSlotsNodes } = useNodeInterceptor()\n\nfunction handleUpdateValue(field: string, value: any) {\n if (!props.model) return\n o(props.model).set(field, value)\n}\n\ndefineExpose<_FormExposed>({ el: toRef(() => gridRef.value?.el), validate, clearValidate, reset })\n</script>\n"],"mappings":""}
@@ -5,7 +5,11 @@ type __VLS_Slots = {
5
5
  data: Record<string, any> | undefined;
6
6
  }): any;
7
7
  };
8
- declare const __VLS_base: import("vue").DefineComponent<FormProps, _FormExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FormProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const __VLS_base: import("vue").DefineComponent<FormProps, _FormExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
9
+ "field:change": (field: string, value: any) => any;
10
+ }, string, import("vue").PublicProps, Readonly<FormProps> & Readonly<{
11
+ "onField:change"?: ((field: string, value: any) => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
13
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
10
14
  declare const _default: typeof __VLS_export;
11
15
  type __VLS_WithSlots<T, S> = T & {
@@ -18,8 +18,10 @@ var form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
18
18
  disabled: { type: Boolean },
19
19
  size: {}
20
20
  },
21
- setup(__props, { expose: __expose }) {
21
+ emits: ["field:change"],
22
+ setup(__props, { expose: __expose, emit: __emit }) {
22
23
  const props = __props;
24
+ const emit = __emit;
23
25
  const cls = bem("form");
24
26
  const formItemCls = bem("form-item");
25
27
  const gridRef = useTemplateRef("grid");
@@ -39,12 +41,16 @@ var form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
39
41
  }
40
42
  return valid;
41
43
  }
44
+ function handleFieldChange(field, value) {
45
+ emit("field:change", field, value);
46
+ }
42
47
  provideFormContext({
43
48
  formProps: props,
44
49
  registerField,
45
50
  unregisterField,
46
51
  validateFields: validate,
47
- shouldValidate
52
+ shouldValidate,
53
+ handleFieldChange
48
54
  });
49
55
  const { getSlotsNodes } = useNodeInterceptor();
50
56
  function handleUpdateValue(field, value) {
@@ -1 +1 @@
1
- {"version":3,"file":"form.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/form.vue"],"sourcesContent":["<template>\n <u-grid\n tag=\"form\"\n ref=\"grid\"\n @submit.prevent\n :cols=\"cols || breakpointCols\"\n :class=\"[cls.b, bem.is('readonly', readonly)]\"\n >\n <template\n v-for=\"{ node, isFormItem, formItemProps, field, modelValue } of getSlotsNodes()\"\n :key=\"node.key\"\n >\n <u-form-item v-if=\"!isFormItem && field\" v-bind=\"formItemProps\">\n <component\n :is=\"node\"\n :model-value=\"modelValue ?? o(model ?? {}).get(field)\"\n @update:model-value=\"handleUpdateValue(field, $event)\"\n />\n </u-form-item>\n\n <component v-else :is=\"node\" />\n </template>\n </u-grid>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { bem } from '@veltra/utils'\nimport { provideFormContext } from '@veltra/utils'\nimport { nextTick, shallowRef, toRef, useTemplateRef } from 'vue'\n\nimport type { BreakCols, GridExposed, FormProps, _FormExposed } from '../../types'\nimport { UFormItem } from '../form-item'\nimport { UGrid } from '../grid'\nimport { useFormFields } from './use-form-fields'\nimport { useNodeInterceptor } from './use-node-interceptor'\n\ndefineOptions({ name: 'Form' })\n\nconst props = defineProps<FormProps>()\n\ndefineSlots<{ default(props?: { data: Record<string, any> | undefined }): any }>()\n\nconst cls = bem('form')\nconst formItemCls = bem('form-item')\nconst gridRef = useTemplateRef('grid')\n\nconst breakpointCols: BreakCols = { xs: 1, md: 2, lg: 3, xl: 4, default: 4 }\n\nconst {\n validate: runValidate,\n clearValidate,\n reset,\n registerField,\n unregisterField,\n shouldValidate\n} = useFormFields({ props })\n\nasync function validate(keys?: string[]) {\n const valid = await runValidate(keys)\n if (!valid) {\n await nextTick()\n\n gridRef.value?.el\n ?.querySelector(`.${formItemCls.e('error-text')}`)\n ?.scrollIntoView({ block: 'center' })\n }\n return valid\n}\n\nprovideFormContext({\n formProps: props,\n registerField,\n unregisterField,\n validateFields: validate,\n shouldValidate\n})\n\nconst { getSlotsNodes } = useNodeInterceptor()\n\nfunction handleUpdateValue(field: string, value: any) {\n if (!props.model) return\n o(props.model).set(field, value)\n}\n\ndefineExpose<_FormExposed>({ el: toRef(() => gridRef.value?.el), validate, clearValidate, reset })\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAuCA,MAAM,QAAQ;EAId,MAAM,MAAM,IAAI,MAAM;EACtB,MAAM,cAAc,IAAI,WAAW;EACnC,MAAM,UAAU,eAAe,MAAM;EAErC,MAAM,iBAA4B;GAAE,IAAI;GAAG,IAAI;GAAG,IAAI;GAAG,IAAI;GAAG,SAAS;EAAE;EAE3E,MAAM,EACJ,UAAU,aACV,eACA,OACA,eACA,iBACA,mBACE,cAAc,EAAE,MAAM,CAAC;EAE3B,eAAe,SAAS,MAAiB;GACvC,MAAM,QAAQ,MAAM,YAAY,IAAI;GACpC,IAAI,CAAC,OAAO;IACV,MAAM,SAAS;IAEf,QAAQ,OAAO,IACX,cAAc,IAAI,YAAY,EAAE,YAAY,GAAG,GAC/C,eAAe,EAAE,OAAO,SAAS,CAAC;GACxC;GACA,OAAO;EACT;EAEA,mBAAmB;GACjB,WAAW;GACX;GACA;GACA,gBAAgB;GAChB;EACF,CAAC;EAED,MAAM,EAAE,kBAAkB,mBAAmB;EAE7C,SAAS,kBAAkB,OAAe,OAAY;GACpD,IAAI,CAAC,MAAM,OAAO;GAClB,EAAE,MAAM,KAAK,EAAE,IAAI,OAAO,KAAK;EACjC;EAEA,SAA2B;GAAE,IAAI,YAAY,QAAQ,OAAO,EAAE;GAAG;GAAU;GAAe;EAAM,CAAC;;uBApF/F,YAqBS,MAAA,SAAA,GAAA;IApBP,KAAI;IACJ,KAAI;IACH,UAAM,OAAA,OAAA,OAAA,KAAA,oBAAP,CAAA,GAAe,CAAA,SAAA,CAAA;IACd,MAAM,QAAA,QAAQ;IACd,OAAK,eAAA,CAAG,MAAA,GAAA,EAAI,GAAG,MAAA,GAAA,EAAI,GAAE,YAAa,QAAA,QAAQ,CAAA,CAAA;;2BAGwC,EAAA,UAAA,IAAA,GADnF,mBAaW,UAAA,MAAA,WAZwD,MAAA,aAAA,EAAa,IAAA,EAArE,MAAM,YAAY,eAAe,OAAO,iBAAU;6DACrD,KAAK,IAAA,GAAA,CAAA,CAES,cAAc,SAAA,UAAA,GAAlC,YAMc,MAAA,iBAAA,GANd,WAMc;;;QANmC,aAAa,GAAA;6BAK1D,EAAA,UAAA,GAJF,YAIE,wBAHK,IAAI,GAAA;OACR,eAAa,cAAc,MAAA,CAAA,EAAE,QAAA,SAAK,CAAA,CAAA,EAAQ,IAAI,KAAK;OACnD,wBAAkB,WAAE,kBAAkB,OAAO,MAAM;;;+BAIxD,YAA+B,wBAAR,IAAI,GAAA,EAAA,KAAA,EAAA,CAAA,EAAA,GAAA,EAAA"}
1
+ {"version":3,"file":"form.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/form/form.vue"],"sourcesContent":["<template>\n <u-grid\n tag=\"form\"\n ref=\"grid\"\n @submit.prevent\n :cols=\"cols || breakpointCols\"\n :class=\"[cls.b, bem.is('readonly', readonly)]\"\n >\n <template\n v-for=\"{ node, isFormItem, formItemProps, field, modelValue } of getSlotsNodes()\"\n :key=\"node.key\"\n >\n <u-form-item v-if=\"!isFormItem && field\" v-bind=\"formItemProps\">\n <component\n :is=\"node\"\n :model-value=\"modelValue ?? o(model ?? {}).get(field)\"\n @update:model-value=\"handleUpdateValue(field, $event)\"\n />\n </u-form-item>\n\n <component v-else :is=\"node\" />\n </template>\n </u-grid>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { bem } from '@veltra/utils'\nimport { provideFormContext } from '@veltra/utils'\nimport { nextTick, toRef, useTemplateRef } from 'vue'\n\nimport type { BreakCols, FormProps, _FormExposed, FormEmits } from '../../types'\nimport { UFormItem } from '../form-item'\nimport { UGrid } from '../grid'\nimport { useFormFields } from './use-form-fields'\nimport { useNodeInterceptor } from './use-node-interceptor'\n\ndefineOptions({ name: 'Form' })\n\nconst props = defineProps<FormProps>()\n\nconst emit = defineEmits<FormEmits>()\n\ndefineSlots<{ default(props?: { data: Record<string, any> | undefined }): any }>()\n\nconst cls = bem('form')\nconst formItemCls = bem('form-item')\nconst gridRef = useTemplateRef('grid')\n\nconst breakpointCols: BreakCols = { xs: 1, md: 2, lg: 3, xl: 4, default: 4 }\n\nconst {\n validate: runValidate,\n clearValidate,\n reset,\n registerField,\n unregisterField,\n shouldValidate\n} = useFormFields({ props })\n\nasync function validate(keys?: string[]) {\n const valid = await runValidate(keys)\n if (!valid) {\n await nextTick()\n\n gridRef.value?.el\n ?.querySelector(`.${formItemCls.e('error-text')}`)\n ?.scrollIntoView({ block: 'center' })\n }\n return valid\n}\n\nfunction handleFieldChange(field: string, value: any) {\n emit('field:change', field, value)\n}\n\nprovideFormContext({\n formProps: props,\n registerField,\n unregisterField,\n validateFields: validate,\n shouldValidate,\n handleFieldChange\n})\n\nconst { getSlotsNodes } = useNodeInterceptor()\n\nfunction handleUpdateValue(field: string, value: any) {\n if (!props.model) return\n o(props.model).set(field, value)\n}\n\ndefineExpose<_FormExposed>({ el: toRef(() => gridRef.value?.el), validate, clearValidate, reset })\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EAuCA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAIb,MAAM,MAAM,IAAI,MAAM;EACtB,MAAM,cAAc,IAAI,WAAW;EACnC,MAAM,UAAU,eAAe,MAAM;EAErC,MAAM,iBAA4B;GAAE,IAAI;GAAG,IAAI;GAAG,IAAI;GAAG,IAAI;GAAG,SAAS;EAAE;EAE3E,MAAM,EACJ,UAAU,aACV,eACA,OACA,eACA,iBACA,mBACE,cAAc,EAAE,MAAM,CAAC;EAE3B,eAAe,SAAS,MAAiB;GACvC,MAAM,QAAQ,MAAM,YAAY,IAAI;GACpC,IAAI,CAAC,OAAO;IACV,MAAM,SAAS;IAEf,QAAQ,OAAO,IACX,cAAc,IAAI,YAAY,EAAE,YAAY,GAAG,GAC/C,eAAe,EAAE,OAAO,SAAS,CAAC;GACxC;GACA,OAAO;EACT;EAEA,SAAS,kBAAkB,OAAe,OAAY;GACpD,KAAK,gBAAgB,OAAO,KAAK;EACnC;EAEA,mBAAmB;GACjB,WAAW;GACX;GACA;GACA,gBAAgB;GAChB;GACA;EACF,CAAC;EAED,MAAM,EAAE,kBAAkB,mBAAmB;EAE7C,SAAS,kBAAkB,OAAe,OAAY;GACpD,IAAI,CAAC,MAAM,OAAO;GAClB,EAAE,MAAM,KAAK,EAAE,IAAI,OAAO,KAAK;EACjC;EAEA,SAA2B;GAAE,IAAI,YAAY,QAAQ,OAAO,EAAE;GAAG;GAAU;GAAe;EAAM,CAAC;;uBA3F/F,YAqBS,MAAA,SAAA,GAAA;IApBP,KAAI;IACJ,KAAI;IACH,UAAM,OAAA,OAAA,OAAA,KAAA,oBAAP,CAAA,GAAe,CAAA,SAAA,CAAA;IACd,MAAM,QAAA,QAAQ;IACd,OAAK,eAAA,CAAG,MAAA,GAAA,EAAI,GAAG,MAAA,GAAA,EAAI,GAAE,YAAa,QAAA,QAAQ,CAAA,CAAA;;2BAGwC,EAAA,UAAA,IAAA,GADnF,mBAaW,UAAA,MAAA,WAZwD,MAAA,aAAA,EAAa,IAAA,EAArE,MAAM,YAAY,eAAe,OAAO,iBAAU;6DACrD,KAAK,IAAA,GAAA,CAAA,CAES,cAAc,SAAA,UAAA,GAAlC,YAMc,MAAA,iBAAA,GANd,WAMc;;;QANmC,aAAa,GAAA;6BAK1D,EAAA,UAAA,GAJF,YAIE,wBAHK,IAAI,GAAA;OACR,eAAa,cAAc,MAAA,CAAA,EAAE,QAAA,SAAK,CAAA,CAAA,EAAQ,IAAI,KAAK;OACnD,wBAAkB,WAAE,kBAAkB,OAAO,MAAM;;;+BAIxD,YAA+B,wBAAR,IAAI,GAAA,EAAA,KAAA,EAAA,CAAA,EAAA,GAAA,EAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"form-item.js","names":[],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', !!rules?.required)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}:</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}:</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <slot></slot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTip\">\n {{ errorTip }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { type CSSProperties, computed, onBeforeUnmount, shallowRef, watch } from 'vue'\n\nimport type { FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls, defineField } from './helper'\nimport { validateField } from './validate'\n\ndefineOptions({ name: 'FormItem' })\n\nconst props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined })\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst { formProps, registerField, unregisterField, shouldValidate } = injectFormContext()\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst errorTip = shallowRef<string>()\n/** 递增序号,丢弃过期的异步校验结果 */\nlet validateSeq = 0\n/** 最近一次 validate 的 Promise,供过期调用等待最新结果 */\nlet latestValidatePromise: Promise<boolean> = Promise.resolve(true)\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value), bem.is('error', !!errorTip.value)].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\nconst fieldItem = defineField({\n clearValidate() {\n errorTip.value = ''\n },\n async validate() {\n if (!props.field || !formProps?.model || !props.rules || !shouldValidate?.()) return true\n\n const seq = ++validateSeq\n\n latestValidatePromise = validateField(formProps.model!, props.field!, props.rules!).then(\n (tip) => {\n if (seq !== validateSeq) return latestValidatePromise\n\n errorTip.value = tip\n return !errorTip.value\n }\n )\n return latestValidatePromise\n }\n})\n\nwatch(\n () => props.field,\n (field, oldField) => {\n if (oldField) {\n unregisterField?.(oldField)\n }\n if (field) {\n registerField?.(field, fieldItem)\n }\n },\n { immediate: true }\n)\n\nlet stopWatchFieldValue: (() => void) | undefined\n\nwatch(\n [() => formProps?.model, () => props.field],\n ([model, field]) => {\n stopWatchFieldValue?.()\n\n if (!field || !model) return\n\n stopWatchFieldValue = watch(\n () => o(model).get(field),\n () => {\n fieldItem.validate()\n }\n )\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n props.field && unregisterField?.(props.field)\n})\n</script>\n"],"mappings":""}
1
+ {"version":3,"file":"form-item.js","names":[],"sources":["../../../src/components/form-item/form-item.vue"],"sourcesContent":["<template>\n <u-grid-item :span=\"span\" :class=\"className\">\n <label\n v-if=\"props.label || $slots.label\"\n :class=\"[cls.e('label'), bem.is('required', !!rules?.required)]\"\n :style=\"labelStyles\"\n >\n <u-tip v-if=\"tips\" :content=\"tips\" :class=\"cls.e('tips')\">\n <span :class=\"cls.e('tip-label')\">\n <slot name=\"label\">{{ label }}:</slot>\n </span>\n </u-tip>\n <template v-else>\n <slot name=\"label\">{{ label }}:</slot>\n </template>\n </label>\n\n <section :class=\"cls.e('wrapper')\">\n <div :class=\"cls.e('content')\">\n <slot></slot>\n </div>\n\n <!-- 只有表单控件处于非只读状态时,才显示错误提示 -->\n <section :class=\"cls.e('error')\" v-if=\"!readonly && !formProps?.noTips\">\n <transition name=\"form-item-tips\" mode=\"out-in\">\n <span :class=\"cls.e('error-text')\" v-if=\"!!errorTip\">\n {{ errorTip }}\n </span>\n </transition>\n </section>\n </section>\n </u-grid-item>\n</template>\n\n<script lang=\"tsx\" setup>\nimport { o } from '@cat-kit/core'\nimport { useConfig, useFallbackProps } from '@veltra/compositions'\nimport { bem, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { type CSSProperties, computed, onBeforeUnmount, shallowRef, watch } from 'vue'\n\nimport type { FormItemProps, ComponentSize } from '../../types'\nimport { UGridItem } from '../grid'\nimport { UTip } from '../tip'\nimport { formItemCls as cls, defineField } from './helper'\nimport { validateField } from './validate'\n\ndefineOptions({ name: 'FormItem' })\n\nconst props = withDefaults(defineProps<FormItemProps>(), { readonly: undefined })\n\ndefineSlots<{\n /** 标签插槽 */\n label?: () => any\n default?: () => any\n}>()\n\n/** 表单组件上下文 */\nconst { formProps, registerField, unregisterField, shouldValidate, handleFieldChange } =\n injectFormContext()\n\nconst { config } = useConfig()\n\nconst { size, readonly } = useFallbackProps([formProps ?? {}, props], {\n size: 'default' as ComponentSize,\n readonly: false\n})\n\nconst errorTip = shallowRef<string>()\n/** 递增序号,丢弃过期的异步校验结果 */\nlet validateSeq = 0\n/** 最近一次 validate 的 Promise,供过期调用等待最新结果 */\nlet latestValidatePromise: Promise<boolean> = Promise.resolve(true)\n\nconst className = computed(() => {\n return [cls.b, cls.m(size.value), bem.is('error', !!errorTip.value)].join(' ')\n})\n\n/** label样式 */\nconst labelStyles = computed<CSSProperties>(() => {\n return {\n width: withUnit(props.labelWidth ?? formProps?.labelWidth ?? config.form.labelWidth, 'px')\n }\n})\n\nconst fieldItem = defineField({\n clearValidate() {\n errorTip.value = ''\n },\n async validate() {\n if (!props.field || !formProps?.model || !props.rules || !shouldValidate?.()) return true\n\n const seq = ++validateSeq\n\n latestValidatePromise = validateField(formProps.model!, props.field!, props.rules!).then(\n (tip) => {\n if (seq !== validateSeq) return latestValidatePromise\n\n errorTip.value = tip\n return !errorTip.value\n }\n )\n return latestValidatePromise\n }\n})\n\nwatch(\n () => props.field,\n (field, oldField) => {\n if (oldField) {\n unregisterField?.(oldField)\n }\n if (field) {\n registerField?.(field, fieldItem)\n }\n },\n { immediate: true }\n)\n\nlet stopWatchFieldValue: (() => void) | undefined\n\nwatch(\n [() => formProps?.model, () => props.field],\n ([model, field]) => {\n stopWatchFieldValue?.()\n\n if (!field || !model) return\n\n stopWatchFieldValue = watch(\n () => o(model).get(field),\n (value) => {\n handleFieldChange?.(field, value)\n fieldItem.validate()\n }\n )\n },\n { immediate: true }\n)\n\nonBeforeUnmount(() => {\n props.field && unregisterField?.(props.field)\n})\n</script>\n"],"mappings":""}
@@ -27,7 +27,7 @@ var form_item_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
27
27
  setup(__props) {
28
28
  const props = __props;
29
29
  /** 表单组件上下文 */
30
- const { formProps, registerField, unregisterField, shouldValidate } = injectFormContext();
30
+ const { formProps, registerField, unregisterField, shouldValidate, handleFieldChange } = injectFormContext();
31
31
  const { config } = useConfig();
32
32
  const { size, readonly } = useFallbackProps([formProps ?? {}, props], {
33
33
  size: "default",
@@ -72,7 +72,8 @@ var form_item_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
72
72
  watch([() => formProps?.model, () => props.field], ([model, field]) => {
73
73
  stopWatchFieldValue?.();
74
74
  if (!field || !model) return;
75
- stopWatchFieldValue = watch(() => o(model).get(field), () => {
75
+ stopWatchFieldValue = watch(() => o(model).get(field), (value) => {
76
+ handleFieldChange?.(field, value);
76
77
  fieldItem.validate();
77
78
  });
78
79
  }, { immediate: true });