@veltra/desktop 1.2.8 → 1.2.10

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 (67) hide show
  1. package/dist/components/action/action-group.vue.d.ts +1 -1
  2. package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
  3. package/dist/components/batch-edit/batch-edit-form.js +40 -74
  4. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit-list.js +17 -29
  6. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  7. package/dist/components/batch-edit/batch-edit.js +37 -60
  8. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  9. package/dist/components/batch-edit/batch-edit.vue.d.ts +3 -14
  10. package/dist/components/batch-edit/di.js.map +1 -1
  11. package/dist/components/batch-edit/form-action-header.js +25 -0
  12. package/dist/components/batch-edit/form-action-header.js.map +1 -0
  13. package/dist/components/batch-edit/style2.css +0 -13
  14. package/dist/components/batch-edit/use-edit-state.js +44 -0
  15. package/dist/components/batch-edit/use-edit-state.js.map +1 -0
  16. package/dist/components/batch-edit/use-handlers.js +146 -0
  17. package/dist/components/batch-edit/use-handlers.js.map +1 -0
  18. package/dist/components/batch-edit/use-shortcut-key.js +44 -0
  19. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -0
  20. package/dist/components/cascade/cascade.vue.d.ts +2 -2
  21. package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
  22. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
  23. package/dist/components/form/form.js.map +1 -1
  24. package/dist/components/form/form.vue.d.ts +5 -1
  25. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +8 -2
  26. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
  27. package/dist/components/form-item/form-item.js.map +1 -1
  28. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +3 -2
  29. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/input/input.vue.d.ts +2 -2
  31. package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
  32. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +3 -3
  33. package/dist/components/number-input/number-input.vue.d.ts +2 -2
  34. package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
  35. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  36. package/dist/components/progress-nodes/progress-nodes.vue.d.ts +1 -1
  37. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
  38. package/dist/components/select/select.vue.d.ts +2 -2
  39. package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
  40. package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
  41. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  42. package/dist/components/textarea/textarea.vue.d.ts +2 -2
  43. package/dist/components/tree/tree.vue.d.ts +1 -1
  44. package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
  45. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  46. package/dist/index.d.ts +4 -4
  47. package/dist/types/batch-edit.d.ts +38 -12
  48. package/dist/types/form.d.ts +4 -1
  49. package/dist/types/index.d.ts +3 -3
  50. package/package.json +6 -6
  51. package/src/components/batch-edit/batch-edit-form.vue +39 -79
  52. package/src/components/batch-edit/batch-edit-list.vue +13 -29
  53. package/src/components/batch-edit/batch-edit.vue +27 -92
  54. package/src/components/batch-edit/di.ts +6 -5
  55. package/src/components/batch-edit/form-action-header.ts +21 -0
  56. package/src/components/batch-edit/style.scss +0 -14
  57. package/src/components/batch-edit/use-dialog-effect.ts +21 -0
  58. package/src/components/batch-edit/use-edit-state.ts +61 -0
  59. package/src/components/batch-edit/use-handlers.ts +226 -0
  60. package/src/components/batch-edit/use-shortcut-key.ts +58 -0
  61. package/src/components/form/form.vue +10 -3
  62. package/src/components/form-item/form-item.vue +4 -2
  63. package/src/types/batch-edit.ts +43 -22
  64. package/src/types/form.ts +4 -0
  65. package/dist/components/batch-edit/use-edit.js +0 -231
  66. package/dist/components/batch-edit/use-edit.js.map +0 -1
  67. package/src/components/batch-edit/use-edit.ts +0 -365
@@ -0,0 +1,226 @@
1
+ import { last, o } from '@cat-kit/core'
2
+ import { computed, nextTick, type ShallowRef } from 'vue'
3
+
4
+ import type {
5
+ TableRow,
6
+ BatchEditEmits,
7
+ BatchEditProps,
8
+ BatchEditStates,
9
+ FormExposed
10
+ } from '../../types'
11
+
12
+ /** 按索引路径在原始数据树中定位节点(等价于历史 Forest.visit 路径访问) */
13
+ function visitDataByIndexPath<T extends Record<string, unknown>>(
14
+ roots: T[],
15
+ indexPath: number[],
16
+ childrenKey: string
17
+ ): T | undefined {
18
+ if (!indexPath.length) return undefined
19
+ let currentList: T[] | undefined = roots
20
+ let node: T | undefined
21
+ for (let i = 0; i < indexPath.length; i++) {
22
+ const idx = indexPath[i]!
23
+ if (!currentList) return undefined
24
+ node = currentList[idx]
25
+ if (node === undefined) return undefined
26
+ if (i < indexPath.length - 1) {
27
+ const next = node[childrenKey]
28
+ currentList = Array.isArray(next) ? (next as T[]) : undefined
29
+ }
30
+ }
31
+ return node
32
+ }
33
+
34
+ interface Options {
35
+ props: BatchEditProps
36
+ emit: BatchEditEmits
37
+ state: BatchEditStates
38
+ formRef: ShallowRef<FormExposed | null>
39
+ resetState: () => void
40
+ }
41
+
42
+ export interface EditReturned {
43
+ handleSave: () => Promise<void>
44
+ handleClose: () => void
45
+ handleCreate: () => void
46
+ handleDelete: (row: TableRow) => Promise<void>
47
+ handleInsertToNext: (row: TableRow) => void
48
+ handleInsertToPrev: (row: TableRow) => void
49
+ handleInsertChild: (row: TableRow) => void
50
+ }
51
+
52
+ export function useHandlers(options: Options): EditReturned {
53
+ const { props, emit, state, resetState, formRef } = options
54
+
55
+ const childrenKey = computed(() => {
56
+ return typeof props.tree === 'string' ? props.tree : 'children'
57
+ })
58
+
59
+ function getInsertData(): Record<string, any> {
60
+ return o(props.model ?? {}).copy()
61
+ }
62
+
63
+ /**
64
+ * 插入数据
65
+ * @param item 数据
66
+ */
67
+ function insert(item: Record<string, any>) {
68
+ const data = [...(props.data ?? [])]
69
+
70
+ const parent = visitDataByIndexPath(data ?? [], state.indexPath.slice(0, -1), childrenKey.value)
71
+
72
+ if (parent) {
73
+ const children = parent[childrenKey.value]
74
+ if (!children) {
75
+ parent[childrenKey.value] = [item]
76
+ } else {
77
+ children.splice(last(state.indexPath), 0, item)
78
+ }
79
+ } else {
80
+ data.splice(last(state.indexPath), 0, item)
81
+ }
82
+
83
+ emit('update:data', data)
84
+
85
+ return item
86
+ }
87
+
88
+ /**
89
+ * 点击新增按钮
90
+ */
91
+ function handleCreate() {
92
+ const { data } = props
93
+
94
+ resetState()
95
+
96
+ nextTick(() => {
97
+ state.formActionType = 'create'
98
+ state.formVisible = true
99
+ state.depth = 1
100
+ state.indexPath = [data?.length ?? 0]
101
+ })
102
+ }
103
+
104
+ function handleInsertToPrev(row: TableRow) {
105
+ resetState()
106
+ state.formActionType = 'create'
107
+ state.formVisible = true
108
+ state.depth = row.depth
109
+ state.indexPath = [...row.indexes]
110
+ }
111
+
112
+ function handleInsertToNext(row: TableRow) {
113
+ resetState()
114
+ state.formActionType = 'create'
115
+ state.formVisible = true
116
+ state.depth = row.depth
117
+ state.indexPath = [...row.indexes.slice(0, -1), row.index + 1]
118
+ }
119
+
120
+ function handleInsertChild(row: TableRow) {
121
+ resetState()
122
+ state.formActionType = 'createChild'
123
+ state.formVisible = true
124
+ state.parentRow = row
125
+ state.depth = row.depth + 1
126
+ row.expanded = true
127
+ state.indexPath = [...row.indexes, row.children?.length ?? 0]
128
+ }
129
+
130
+ function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {
131
+ return async (...args: Arg) => {
132
+ state.loading = true
133
+ if (state.row) {
134
+ state.row.operating = true
135
+ }
136
+ try {
137
+ await fn.apply(null, args)
138
+ } catch (error) {
139
+ console.error(error)
140
+ }
141
+ state.loading = false
142
+ if (state.row) {
143
+ state.row.operating = false
144
+ }
145
+ }
146
+ }
147
+
148
+ /** 保存 */
149
+ const handleSave = runWithLoading(async () => {
150
+ const { model, saveMethod } = props
151
+ const valid = await formRef.value?.validate()
152
+ if (!model || !valid) return
153
+
154
+ let item = getInsertData()
155
+
156
+ // 新增
157
+ if (state.formActionType === 'create' || state.formActionType === 'createChild') {
158
+ await props.beforeCreate?.(item, state.parentRow?.data)
159
+ if (saveMethod) {
160
+ const result = await saveMethod(item, state.formActionType, state.parentRow?.data)
161
+ if (result) {
162
+ item = result
163
+ }
164
+ }
165
+ insert(item)
166
+ return
167
+ }
168
+
169
+ // 普通模式下的编辑
170
+ if (saveMethod) {
171
+ const result = await saveMethod(item, state.formActionType, state.parentRow?.data)
172
+ if (result) {
173
+ item = result
174
+ }
175
+ }
176
+
177
+ const { row } = state
178
+ row &&
179
+ Object.keys(item).forEach((key) => {
180
+ o(row.data).set(key, o(item).get(key))
181
+ })
182
+ })
183
+
184
+ const handleDelete = runWithLoading(async (row: TableRow) => {
185
+ const { deleteMethod } = props
186
+
187
+ if (deleteMethod) {
188
+ await deleteMethod([row.data])
189
+ }
190
+
191
+ const data = [...(props.data ?? [])]
192
+
193
+ const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value)
194
+
195
+ if (parent) {
196
+ const children = parent[childrenKey.value]
197
+ if (children) {
198
+ children.splice(row.index, 1)
199
+ } else {
200
+ console.error('行路径不正确')
201
+ }
202
+ } else {
203
+ data.splice(row.index, 1)
204
+ }
205
+
206
+ if (state.row === row) {
207
+ resetState()
208
+ }
209
+
210
+ emit('update:data', data)
211
+ })
212
+
213
+ function handleClose() {
214
+ resetState()
215
+ }
216
+
217
+ return {
218
+ handleDelete,
219
+ handleInsertChild,
220
+ handleInsertToNext,
221
+ handleInsertToPrev,
222
+ handleCreate,
223
+ handleClose,
224
+ handleSave
225
+ }
226
+ }
@@ -0,0 +1,58 @@
1
+ import { shallowRef } from 'vue'
2
+
3
+ import type { BatchEditProps, BatchEditStates } from '../../types'
4
+
5
+ interface Options {
6
+ props: BatchEditProps
7
+ onSave: () => void
8
+ onClose: () => void
9
+ state: BatchEditStates
10
+ }
11
+ export function useShortcutKey(options: Options) {
12
+ const { props, onClose, onSave, state } = options
13
+ const focused = shallowRef(false)
14
+
15
+ function handleFocusIn() {
16
+ focused.value = true
17
+ }
18
+
19
+ function handleFocusOut(e: FocusEvent) {
20
+ const root = e.currentTarget as HTMLElement
21
+ if (!root.contains(e.relatedTarget as Node | null)) {
22
+ focused.value = false
23
+ }
24
+ }
25
+
26
+ function runEscape(e: KeyboardEvent) {
27
+ e.preventDefault()
28
+ onClose()
29
+ }
30
+
31
+ function runSave(e: KeyboardEvent) {
32
+ if (!state.formVisible) return
33
+ if (props.quickEdit && state.formActionType === 'update') return
34
+ e.preventDefault()
35
+ onSave()
36
+ }
37
+
38
+ /**
39
+ * 键盘快捷键(仅组件获焦时生效):
40
+ * - Esc 关闭表单
41
+ * - ⌘/Ctrl + S 保存(快速编辑模式下编辑行时除外)
42
+ */
43
+ function handleKeydown(e: KeyboardEvent) {
44
+ if (!focused.value) return
45
+ if (props.readonly && e.key !== 'Escape') return
46
+
47
+ if (e.key === 'Escape') return runEscape(e)
48
+
49
+ const meta = e.metaKey || e.ctrlKey
50
+ if (!meta) return
51
+
52
+ const key = e.key.toLowerCase()
53
+
54
+ if (key === 's') return runSave(e)
55
+ }
56
+
57
+ return { focused, handleFocusIn, handleFocusOut, handleKeydown }
58
+ }
@@ -27,9 +27,9 @@
27
27
  import { o } from '@cat-kit/core'
28
28
  import { bem } from '@veltra/utils'
29
29
  import { provideFormContext } from '@veltra/utils'
30
- import { nextTick, shallowRef, toRef, useTemplateRef } from 'vue'
30
+ import { nextTick, toRef, useTemplateRef } from 'vue'
31
31
 
32
- import type { BreakCols, GridExposed, FormProps, _FormExposed } from '../../types'
32
+ import type { BreakCols, FormProps, _FormExposed, FormEmits } from '../../types'
33
33
  import { UFormItem } from '../form-item'
34
34
  import { UGrid } from '../grid'
35
35
  import { useFormFields } from './use-form-fields'
@@ -39,6 +39,8 @@ defineOptions({ name: 'Form' })
39
39
 
40
40
  const props = defineProps<FormProps>()
41
41
 
42
+ const emit = defineEmits<FormEmits>()
43
+
42
44
  defineSlots<{ default(props?: { data: Record<string, any> | undefined }): any }>()
43
45
 
44
46
  const cls = bem('form')
@@ -68,12 +70,17 @@ async function validate(keys?: string[]) {
68
70
  return valid
69
71
  }
70
72
 
73
+ function handleFieldChange(field: string, value: any) {
74
+ emit('field:change', field, value)
75
+ }
76
+
71
77
  provideFormContext({
72
78
  formProps: props,
73
79
  registerField,
74
80
  unregisterField,
75
81
  validateFields: validate,
76
- shouldValidate
82
+ shouldValidate,
83
+ handleFieldChange
77
84
  })
78
85
 
79
86
  const { getSlotsNodes } = useNodeInterceptor()
@@ -56,7 +56,8 @@ defineSlots<{
56
56
  }>()
57
57
 
58
58
  /** 表单组件上下文 */
59
- const { formProps, registerField, unregisterField, shouldValidate } = injectFormContext()
59
+ const { formProps, registerField, unregisterField, shouldValidate, handleFieldChange } =
60
+ injectFormContext()
60
61
 
61
62
  const { config } = useConfig()
62
63
 
@@ -127,7 +128,8 @@ watch(
127
128
 
128
129
  stopWatchFieldValue = watch(
129
130
  () => o(model).get(field),
130
- () => {
131
+ (value) => {
132
+ handleFieldChange?.(field, value)
131
133
  fieldItem.validate()
132
134
  }
133
135
  )
@@ -1,25 +1,32 @@
1
1
  import type { DeconstructValue } from '@veltra/utils'
2
2
 
3
3
  import type { ActionProps } from './action'
4
- import type { TableColumn, TableEmits, TableProps, TableRow } from './table'
4
+ import type { TableColumn, TableColumnSlotsScope, TableEmits, TableProps, TableRow } from './table'
5
5
 
6
6
  /** 批量编辑列 */
7
- export interface BatchEditColumn extends TableColumn {
8
- // /**
9
- // * 是否在列中显示
10
- // * @default true
11
- // */
12
- // visible?: boolean
13
- // /** 校验规则 */
14
- // rules?: ValidateRule
15
- // /** 默认值 */
16
- // defaultValue?: any | (() => any)
17
- }
7
+ export interface BatchEditColumn extends TableColumn {}
8
+
9
+ export type BatchEditFeature = 'create' | 'update' | 'delete' | 'view' | 'createChild'
18
10
 
19
- export type BatchEditFeature = 'create' | 'update' | 'copy' | 'delete' | 'view' | 'createChild'
11
+ export type BatchEditFormStatus = 'hidden'
20
12
 
21
- /** 批量编辑模式 */
22
- export type BatchEditMode = 'normal' | 'quick'
13
+ /** 批量编辑状态 */
14
+ export interface BatchEditStates {
15
+ /** 层级 */
16
+ depth: number
17
+ /** 表单可见性 */
18
+ formVisible: boolean
19
+ /** 表单操作类型 */
20
+ formActionType: 'create' | 'update' | 'view' | 'createChild'
21
+ /** 加载状态 */
22
+ loading: boolean
23
+ /** 当前编辑行 */
24
+ row?: TableRow
25
+ /** 当前编辑或者新增的父级行 */
26
+ parentRow?: TableRow
27
+ /** 行索引路径, */
28
+ indexPath: number[]
29
+ }
23
30
 
24
31
  /** 批量编辑组件属性 */
25
32
  export interface BatchEditProps extends TableProps {
@@ -37,14 +44,13 @@ export interface BatchEditProps extends TableProps {
37
44
  /** 只读模式 */
38
45
  readonly?: boolean
39
46
  /**
40
- * 编辑模式
41
- * @default 'normal'
42
- * @description `quick` 模式下编辑行时表单实时写回 `row.data`(经 `model` 中转),且不调用 `saveMethod`
47
+ * 开启快速编辑
48
+ * @description 开启后,编辑行时表单实时写回 `row.data`(经 `model` 中转),且不调用 `saveMethod`
43
49
  */
44
- mode?: BatchEditMode
50
+ quickEdit?: boolean
45
51
  /**
46
52
  * 新增前的钩子
47
- * @description 仅作用于 create 类操作。`normal` 模式在保存时调用;`quick` 模式在点击新增类按钮时立即调用。可直接修改传入的 draft 对象
53
+ * @description 仅作用于 create 类操作,在保存时调用。可直接修改传入的 draft 对象
48
54
  */
49
55
  beforeCreate?: (
50
56
  data: Record<string, any>,
@@ -56,14 +62,14 @@ export interface BatchEditProps extends TableProps {
56
62
  deleteMethod?: (data: Record<string, any>[]) => any
57
63
  /**
58
64
  * 保存方法
59
- * @description 仅在 `normal` 模式下、保存时调用(create / update)
65
+ * @description 保存时调用。`quick` 模式下编辑行时实时写回,不调用此方法;新增时与 `normal` 模式一致
60
66
  * @returns 如果返回一个值,那么这个值会被插入,否则插入的为表单值
61
67
  */
62
68
  saveMethod?: (
63
69
  /** 表单数据 */
64
70
  data: Record<string, any>,
65
71
  /** 操作类型 */
66
- actionType: 'create' | 'update',
72
+ actionType: BatchEditStates['formActionType'],
67
73
  /** 父级数据 */
68
74
  parentData?: Record<string, any>
69
75
  ) => any
@@ -109,6 +115,21 @@ export interface BatchEditEmits extends TableEmits {
109
115
  (e: 'update:data', value: Record<string, any>[]): void
110
116
  }
111
117
 
118
+ export type BatchEditSlots = {
119
+ form?: (props: {
120
+ /** 当前编辑的层级 */
121
+ depth?: number
122
+ /** 当前编辑的行 */
123
+ row?: TableRow
124
+ /** 当前编辑的行索引 */
125
+ index?: number
126
+ /** 操作的目标行索引路径 */
127
+ indexes?: number[]
128
+ }) => any
129
+
130
+ header?: () => any
131
+ } & Partial<{ [key: `column:${string}`]: (props: TableColumnSlotsScope) => any }>
132
+
112
133
  /** 批量编辑组件暴露的属性和方法(组件内部使用) */
113
134
  export interface _BatchEditExposed {}
114
135
 
package/src/types/form.ts CHANGED
@@ -21,6 +21,10 @@ export interface FormProps extends ComponentProps {
21
21
  disabled?: boolean
22
22
  }
23
23
 
24
+ export interface FormEmits {
25
+ (e: 'field:change', field: string, value: any): void
26
+ }
27
+
24
28
  export interface _FormExposed {
25
29
  el: ShallowRef<HTMLElement | null | undefined>
26
30
  validate: (keys?: string[]) => Promise<boolean>
@@ -1,231 +0,0 @@
1
- import { computed, nextTick, shallowReactive, shallowRef, watch } from "vue";
2
- import { last, o, safeRun } from "@cat-kit/core";
3
- //#region src/components/batch-edit/use-edit.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 snapshotData(data) {
22
- return safeRun(() => JSON.parse(JSON.stringify(data ?? {})), {});
23
- }
24
- function replaceModelData(model, data) {
25
- for (const key of Object.keys(model)) delete model[key];
26
- Object.assign(model, data);
27
- }
28
- function useEdit(options) {
29
- const { props, emit, formRef } = options;
30
- /** 组件状态 */
31
- const state = shallowReactive({
32
- depth: void 0,
33
- visible: false,
34
- type: "create",
35
- loading: false
36
- });
37
- const insertIndexes = shallowRef([]);
38
- function snapshotModelData() {
39
- return snapshotData(props.model);
40
- }
41
- function setFormData(formData) {
42
- if (!props.model) return;
43
- replaceModelData(props.model, snapshotData(formData));
44
- }
45
- const isQuickMode = () => props.mode === "quick";
46
- async function runBeforeCreate(data) {
47
- await props.beforeCreate?.(data, state.parentRow?.data);
48
- }
49
- watch(() => state.row, (row) => {
50
- if (row) {
51
- state.type = "update";
52
- state.visible = true;
53
- setFormData(row.data);
54
- } else state.visible = false;
55
- }, { flush: "sync" });
56
- /**
57
- * quick + update:model 实时写回 row.data,让表格显示同步更新。
58
- * normal 模式不写回,需等待保存。
59
- *
60
- * 监听源仅限 props.model(deep 遍历仅作用于 model,不触及 row 树);
61
- * mode/type/row 在回调中读取但不作为依赖,避免行切换等场景误触发。
62
- */
63
- watch(() => props.model, (model) => {
64
- const { row, type } = state;
65
- if (props.mode !== "quick" || type !== "update" || !row || !model) return;
66
- const data = row.data;
67
- Object.keys(model).forEach((key) => {
68
- const next = o(model).get(key);
69
- if (o(data).get(key) !== next) o(data).set(key, next);
70
- });
71
- }, { deep: true });
72
- const childrenKey = computed(() => {
73
- return typeof props.tree === "string" ? props.tree : "children";
74
- });
75
- /**
76
- * 插入数据
77
- * @param item 数据
78
- */
79
- function insert(item) {
80
- const data = [...props.data ?? []];
81
- const parent = visitDataByIndexPath(data ?? [], insertIndexes.value.slice(0, -1), childrenKey.value);
82
- if (parent) {
83
- const children = parent[childrenKey.value];
84
- if (!children) parent[childrenKey.value] = [item];
85
- else children.splice(last(insertIndexes.value), 0, item);
86
- } else data.splice(last(insertIndexes.value), 0, item);
87
- emit("update:data", data);
88
- return item;
89
- }
90
- function getInsertData() {
91
- return snapshotModelData();
92
- }
93
- async function runCreate(cb, initialData) {
94
- state.parentRow = void 0;
95
- if (state.row) state.row.isCurrent = false;
96
- state.row = void 0;
97
- state.type = "create";
98
- cb();
99
- await nextTick();
100
- setFormData(initialData);
101
- if (isQuickMode() && props.model) await runBeforeCreate(props.model);
102
- state.visible = true;
103
- }
104
- /**
105
- * 点击新增按钮
106
- */
107
- async function handleCreate() {
108
- const { data } = props;
109
- await runCreate(() => {
110
- state.depth = 1;
111
- insertIndexes.value = [data?.length ?? 0];
112
- });
113
- }
114
- async function handleInsertToPrev(row) {
115
- await runCreate(() => {
116
- state.depth = row.depth;
117
- insertIndexes.value = [...row.indexes];
118
- });
119
- }
120
- async function handleInsertToNext(row) {
121
- await runCreate(() => {
122
- state.depth = row.depth;
123
- insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1];
124
- });
125
- }
126
- async function handleInsertChild(row) {
127
- await runCreate(() => {
128
- state.parentRow = row;
129
- state.depth = row.depth + 1;
130
- row.expanded = true;
131
- insertIndexes.value = [...row.indexes, row.children?.length ?? 0];
132
- });
133
- }
134
- /** 编辑指定行(等同于点击行) */
135
- function handleEdit(row) {
136
- state.type = "update";
137
- state.row = row;
138
- state.depth = row.depth;
139
- }
140
- /** 复制指定行:在其下方以 create 模式打开表单,并预填副本数据 */
141
- async function handleCopy(row) {
142
- await runCreate(() => {
143
- state.depth = row.depth;
144
- insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1];
145
- }, row.data);
146
- }
147
- function runWithLoading(fn) {
148
- return async (...args) => {
149
- state.loading = true;
150
- if (state.row) state.row.operating = true;
151
- try {
152
- await fn.apply(null, args);
153
- } catch (error) {
154
- console.error(error);
155
- }
156
- state.loading = false;
157
- if (state.row) state.row.operating = false;
158
- };
159
- }
160
- /** 保存 */
161
- const handleSave = runWithLoading(async () => {
162
- const { model, saveMethod } = props;
163
- if (!model) return;
164
- if (isQuickMode() && state.type === "update") return;
165
- formRef.value?.clearValidate();
166
- if (!await formRef.value?.validate()) return;
167
- if (isQuickMode() && state.type === "create") {
168
- insert(getInsertData());
169
- return;
170
- }
171
- let item = getInsertData();
172
- if (state.type === "create") {
173
- await runBeforeCreate(item);
174
- if (saveMethod) {
175
- const result = await saveMethod(item, state.type, state.parentRow?.data);
176
- if (result) item = result;
177
- }
178
- insert(item);
179
- return;
180
- }
181
- if (saveMethod) {
182
- const result = await saveMethod(item, state.type, state.parentRow?.data);
183
- if (result) item = result;
184
- }
185
- const { row } = state;
186
- row && Object.keys(item).forEach((key) => {
187
- o(row.data).set(key, o(item).get(key));
188
- });
189
- });
190
- const handleDelete = runWithLoading(async (row) => {
191
- const { deleteMethod } = props;
192
- if (deleteMethod) await deleteMethod([row.data]);
193
- const data = [...props.data ?? []];
194
- const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value);
195
- if (parent) {
196
- const children = parent[childrenKey.value];
197
- if (children) children.splice(row.index, 1);
198
- else console.error("行路径不正确");
199
- } else data.splice(row.index, 1);
200
- if (state.row === row) {
201
- state.row = void 0;
202
- state.depth = void 0;
203
- }
204
- emit("update:data", data);
205
- });
206
- function handleClose() {
207
- state.visible = false;
208
- if (state.row) state.row.isCurrent = false;
209
- state.row = void 0;
210
- state.parentRow = void 0;
211
- state.depth = void 0;
212
- insertIndexes.value = [];
213
- }
214
- return {
215
- state,
216
- insertIndexes,
217
- handleDelete,
218
- handleInsertChild,
219
- handleInsertToNext,
220
- handleInsertToPrev,
221
- handleCreate,
222
- handleEdit,
223
- handleCopy,
224
- handleClose,
225
- handleSave
226
- };
227
- }
228
- //#endregion
229
- export { useEdit };
230
-
231
- //# sourceMappingURL=use-edit.js.map