@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
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-edit.js","names":[],"sources":["../../../src/components/batch-edit/use-edit.ts"],"sourcesContent":["import { last, o, safeRun } from '@cat-kit/core'\nimport { computed, nextTick, shallowReactive, shallowRef, watch, type ShallowRef } from 'vue'\n\nimport type { TableRow, BatchEditEmits, BatchEditProps } from '../../types'\nimport type { FormExposed } from '../../types/form'\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 formRef: ShallowRef<FormExposed | undefined>\n}\n\nexport interface BatchEditStates {\n /** 层级 */\n depth?: number\n visible: boolean\n type: 'create' | 'update'\n loading: boolean\n row?: TableRow\n /** 父级,只在添加子级时会有值 */\n parentRow?: TableRow\n}\n\nexport interface EditReturned {\n state: BatchEditStates\n insertIndexes: ShallowRef<number[]>\n handleSave: () => Promise<void>\n handleClose: () => void\n handleCreate: () => Promise<void>\n handleEdit: (row: TableRow) => void\n handleCopy: (row: TableRow) => Promise<void>\n handleDelete: (row: TableRow) => Promise<void>\n handleInsertToNext: (row: TableRow) => Promise<void>\n handleInsertToPrev: (row: TableRow) => Promise<void>\n handleInsertChild: (row: TableRow) => Promise<void>\n}\n\nfunction snapshotData(data?: Record<string, any>) {\n return safeRun(() => JSON.parse(JSON.stringify(data ?? {})), {})\n}\n\nfunction replaceModelData(model: Record<string, any>, data: Record<string, any>) {\n for (const key of Object.keys(model)) {\n delete model[key]\n }\n Object.assign(model, data)\n}\n\nexport function useEdit(options: Options): EditReturned {\n const { props, emit, formRef } = options\n\n /** 组件状态 */\n const state = shallowReactive<BatchEditStates>({\n depth: undefined,\n visible: false,\n type: 'create',\n loading: false\n })\n\n const insertIndexes = shallowRef<number[]>([])\n\n function snapshotModelData() {\n return snapshotData(props.model)\n }\n\n function setFormData(formData?: Record<string, any>) {\n if (!props.model) return\n replaceModelData(props.model, snapshotData(formData))\n }\n\n const isQuickMode = () => props.mode === 'quick'\n\n async function runBeforeCreate(data: Record<string, any>) {\n await props.beforeCreate?.(data, state.parentRow?.data)\n }\n\n watch(\n () => state.row,\n (row) => {\n if (row) {\n state.type = 'update'\n state.visible = true\n // 所有模式下都回填 model,保证表单内条件字段(依赖 model)能响应当前编辑行\n setFormData(row.data)\n } else {\n state.visible = false\n }\n },\n { flush: 'sync' }\n )\n\n /**\n * quick + update:model 实时写回 row.data,让表格显示同步更新。\n * normal 模式不写回,需等待保存。\n *\n * 监听源仅限 props.model(deep 遍历仅作用于 model,不触及 row 树);\n * mode/type/row 在回调中读取但不作为依赖,避免行切换等场景误触发。\n */\n watch(\n () => props.model,\n (model) => {\n const { row, type } = state\n if (props.mode !== 'quick' || type !== 'update' || !row || !model) return\n const data = row.data\n Object.keys(model).forEach((key) => {\n const next = o(model).get(key)\n if (o(data).get(key) !== next) {\n o(data).set(key, next)\n }\n })\n },\n { deep: true }\n )\n\n const childrenKey = computed(() => {\n return typeof props.tree === 'string' ? props.tree : 'children'\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(\n data ?? [],\n insertIndexes.value.slice(0, -1),\n childrenKey.value\n )\n\n if (parent) {\n const children = parent[childrenKey.value]\n if (!children) {\n parent[childrenKey.value] = [item]\n } else {\n children.splice(last(insertIndexes.value), 0, item)\n }\n } else {\n data.splice(last(insertIndexes.value), 0, item)\n }\n\n emit('update:data', data)\n\n return item\n }\n\n function getInsertData(): Record<string, any> {\n return snapshotModelData()\n }\n\n async function runCreate(cb: () => void, initialData?: Record<string, any>) {\n state.parentRow = undefined\n if (state.row) {\n state.row.isCurrent = false\n }\n state.row = undefined\n state.type = 'create'\n cb()\n\n await nextTick()\n\n setFormData(initialData)\n\n if (isQuickMode() && props.model) {\n await runBeforeCreate(props.model)\n }\n\n state.visible = true\n }\n\n /**\n * 点击新增按钮\n */\n async function handleCreate() {\n const { data } = props\n await runCreate(() => {\n state.depth = 1\n insertIndexes.value = [data?.length ?? 0]\n })\n }\n\n async function handleInsertToPrev(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes]\n })\n }\n\n async function handleInsertToNext(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]\n })\n }\n\n async function handleInsertChild(row: TableRow) {\n await runCreate(() => {\n state.parentRow = row\n state.depth = row.depth + 1\n row.expanded = true\n\n insertIndexes.value = [...row.indexes, row.children?.length ?? 0]\n })\n }\n\n /** 编辑指定行(等同于点击行) */\n function handleEdit(row: TableRow) {\n state.type = 'update'\n state.row = row\n state.depth = row.depth\n }\n\n /** 复制指定行:在其下方以 create 模式打开表单,并预填副本数据 */\n async function handleCopy(row: TableRow) {\n await runCreate(() => {\n state.depth = row.depth\n insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]\n }, row.data)\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\n if (!model) return\n\n if (isQuickMode() && state.type === 'update') return\n\n formRef.value?.clearValidate()\n const valid = await formRef.value?.validate()\n\n if (!valid) return\n\n // quick + create:仅校验 + insert\n if (isQuickMode() && state.type === 'create') {\n insert(getInsertData())\n return\n }\n\n let item = getInsertData()\n\n // normal + create:beforeCreate → saveMethod → insert\n if (state.type === 'create') {\n await runBeforeCreate(item)\n if (saveMethod) {\n const result = await saveMethod(item, state.type, state.parentRow?.data)\n if (result) {\n item = result\n }\n }\n insert(item)\n return\n }\n\n // normal + update\n if (saveMethod) {\n const result = await saveMethod(item, state.type, 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 state.row = undefined\n state.depth = undefined\n }\n\n emit('update:data', data)\n })\n\n function handleClose() {\n state.visible = false\n if (state.row) {\n state.row.isCurrent = false\n }\n state.row = undefined\n state.parentRow = undefined\n state.depth = undefined\n insertIndexes.value = []\n }\n\n return {\n state,\n insertIndexes,\n handleDelete,\n handleInsertChild,\n handleInsertToNext,\n handleInsertToPrev,\n handleCreate,\n handleEdit,\n handleCopy,\n handleClose,\n handleSave\n }\n}\n"],"mappings":"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"}
@@ -1,365 +0,0 @@
1
- import { last, o, safeRun } from '@cat-kit/core'
2
- import { computed, nextTick, shallowReactive, shallowRef, watch, type ShallowRef } from 'vue'
3
-
4
- import type { TableRow, BatchEditEmits, BatchEditProps } from '../../types'
5
- import type { FormExposed } from '../../types/form'
6
-
7
- /** 按索引路径在原始数据树中定位节点(等价于历史 Forest.visit 路径访问) */
8
- function visitDataByIndexPath<T extends Record<string, unknown>>(
9
- roots: T[],
10
- indexPath: number[],
11
- childrenKey: string
12
- ): T | undefined {
13
- if (!indexPath.length) return undefined
14
- let currentList: T[] | undefined = roots
15
- let node: T | undefined
16
- for (let i = 0; i < indexPath.length; i++) {
17
- const idx = indexPath[i]!
18
- if (!currentList) return undefined
19
- node = currentList[idx]
20
- if (node === undefined) return undefined
21
- if (i < indexPath.length - 1) {
22
- const next = node[childrenKey]
23
- currentList = Array.isArray(next) ? (next as T[]) : undefined
24
- }
25
- }
26
- return node
27
- }
28
-
29
- interface Options {
30
- props: BatchEditProps
31
- emit: BatchEditEmits
32
- formRef: ShallowRef<FormExposed | undefined>
33
- }
34
-
35
- export interface BatchEditStates {
36
- /** 层级 */
37
- depth?: number
38
- visible: boolean
39
- type: 'create' | 'update'
40
- loading: boolean
41
- row?: TableRow
42
- /** 父级,只在添加子级时会有值 */
43
- parentRow?: TableRow
44
- }
45
-
46
- export interface EditReturned {
47
- state: BatchEditStates
48
- insertIndexes: ShallowRef<number[]>
49
- handleSave: () => Promise<void>
50
- handleClose: () => void
51
- handleCreate: () => Promise<void>
52
- handleEdit: (row: TableRow) => void
53
- handleCopy: (row: TableRow) => Promise<void>
54
- handleDelete: (row: TableRow) => Promise<void>
55
- handleInsertToNext: (row: TableRow) => Promise<void>
56
- handleInsertToPrev: (row: TableRow) => Promise<void>
57
- handleInsertChild: (row: TableRow) => Promise<void>
58
- }
59
-
60
- function snapshotData(data?: Record<string, any>) {
61
- return safeRun(() => JSON.parse(JSON.stringify(data ?? {})), {})
62
- }
63
-
64
- function replaceModelData(model: Record<string, any>, data: Record<string, any>) {
65
- for (const key of Object.keys(model)) {
66
- delete model[key]
67
- }
68
- Object.assign(model, data)
69
- }
70
-
71
- export function useEdit(options: Options): EditReturned {
72
- const { props, emit, formRef } = options
73
-
74
- /** 组件状态 */
75
- const state = shallowReactive<BatchEditStates>({
76
- depth: undefined,
77
- visible: false,
78
- type: 'create',
79
- loading: false
80
- })
81
-
82
- const insertIndexes = shallowRef<number[]>([])
83
-
84
- function snapshotModelData() {
85
- return snapshotData(props.model)
86
- }
87
-
88
- function setFormData(formData?: Record<string, any>) {
89
- if (!props.model) return
90
- replaceModelData(props.model, snapshotData(formData))
91
- }
92
-
93
- const isQuickMode = () => props.mode === 'quick'
94
-
95
- async function runBeforeCreate(data: Record<string, any>) {
96
- await props.beforeCreate?.(data, state.parentRow?.data)
97
- }
98
-
99
- watch(
100
- () => state.row,
101
- (row) => {
102
- if (row) {
103
- state.type = 'update'
104
- state.visible = true
105
- // 所有模式下都回填 model,保证表单内条件字段(依赖 model)能响应当前编辑行
106
- setFormData(row.data)
107
- } else {
108
- state.visible = false
109
- }
110
- },
111
- { flush: 'sync' }
112
- )
113
-
114
- /**
115
- * quick + update:model 实时写回 row.data,让表格显示同步更新。
116
- * normal 模式不写回,需等待保存。
117
- *
118
- * 监听源仅限 props.model(deep 遍历仅作用于 model,不触及 row 树);
119
- * mode/type/row 在回调中读取但不作为依赖,避免行切换等场景误触发。
120
- */
121
- watch(
122
- () => props.model,
123
- (model) => {
124
- const { row, type } = state
125
- if (props.mode !== 'quick' || type !== 'update' || !row || !model) return
126
- const data = row.data
127
- Object.keys(model).forEach((key) => {
128
- const next = o(model).get(key)
129
- if (o(data).get(key) !== next) {
130
- o(data).set(key, next)
131
- }
132
- })
133
- },
134
- { deep: true }
135
- )
136
-
137
- const childrenKey = computed(() => {
138
- return typeof props.tree === 'string' ? props.tree : 'children'
139
- })
140
-
141
- /**
142
- * 插入数据
143
- * @param item 数据
144
- */
145
- function insert(item: Record<string, any>) {
146
- const data = [...(props.data ?? [])]
147
-
148
- const parent = visitDataByIndexPath(
149
- data ?? [],
150
- insertIndexes.value.slice(0, -1),
151
- childrenKey.value
152
- )
153
-
154
- if (parent) {
155
- const children = parent[childrenKey.value]
156
- if (!children) {
157
- parent[childrenKey.value] = [item]
158
- } else {
159
- children.splice(last(insertIndexes.value), 0, item)
160
- }
161
- } else {
162
- data.splice(last(insertIndexes.value), 0, item)
163
- }
164
-
165
- emit('update:data', data)
166
-
167
- return item
168
- }
169
-
170
- function getInsertData(): Record<string, any> {
171
- return snapshotModelData()
172
- }
173
-
174
- async function runCreate(cb: () => void, initialData?: Record<string, any>) {
175
- state.parentRow = undefined
176
- if (state.row) {
177
- state.row.isCurrent = false
178
- }
179
- state.row = undefined
180
- state.type = 'create'
181
- cb()
182
-
183
- await nextTick()
184
-
185
- setFormData(initialData)
186
-
187
- if (isQuickMode() && props.model) {
188
- await runBeforeCreate(props.model)
189
- }
190
-
191
- state.visible = true
192
- }
193
-
194
- /**
195
- * 点击新增按钮
196
- */
197
- async function handleCreate() {
198
- const { data } = props
199
- await runCreate(() => {
200
- state.depth = 1
201
- insertIndexes.value = [data?.length ?? 0]
202
- })
203
- }
204
-
205
- async function handleInsertToPrev(row: TableRow) {
206
- await runCreate(() => {
207
- state.depth = row.depth
208
- insertIndexes.value = [...row.indexes]
209
- })
210
- }
211
-
212
- async function handleInsertToNext(row: TableRow) {
213
- await runCreate(() => {
214
- state.depth = row.depth
215
- insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]
216
- })
217
- }
218
-
219
- async function handleInsertChild(row: TableRow) {
220
- await runCreate(() => {
221
- state.parentRow = row
222
- state.depth = row.depth + 1
223
- row.expanded = true
224
-
225
- insertIndexes.value = [...row.indexes, row.children?.length ?? 0]
226
- })
227
- }
228
-
229
- /** 编辑指定行(等同于点击行) */
230
- function handleEdit(row: TableRow) {
231
- state.type = 'update'
232
- state.row = row
233
- state.depth = row.depth
234
- }
235
-
236
- /** 复制指定行:在其下方以 create 模式打开表单,并预填副本数据 */
237
- async function handleCopy(row: TableRow) {
238
- await runCreate(() => {
239
- state.depth = row.depth
240
- insertIndexes.value = [...row.indexes.slice(0, -1), row.index + 1]
241
- }, row.data)
242
- }
243
-
244
- function runWithLoading<Arg extends any[]>(fn: (...args: Arg) => Promise<void> | void) {
245
- return async (...args: Arg) => {
246
- state.loading = true
247
- if (state.row) {
248
- state.row.operating = true
249
- }
250
- try {
251
- await fn.apply(null, args)
252
- } catch (error) {
253
- console.error(error)
254
- }
255
- state.loading = false
256
- if (state.row) {
257
- state.row.operating = false
258
- }
259
- }
260
- }
261
-
262
- /** 保存 */
263
- const handleSave = runWithLoading(async () => {
264
- const { model, saveMethod } = props
265
-
266
- if (!model) return
267
-
268
- if (isQuickMode() && state.type === 'update') return
269
-
270
- formRef.value?.clearValidate()
271
- const valid = await formRef.value?.validate()
272
-
273
- if (!valid) return
274
-
275
- // quick + create:仅校验 + insert
276
- if (isQuickMode() && state.type === 'create') {
277
- insert(getInsertData())
278
- return
279
- }
280
-
281
- let item = getInsertData()
282
-
283
- // normal + create:beforeCreate → saveMethod → insert
284
- if (state.type === 'create') {
285
- await runBeforeCreate(item)
286
- if (saveMethod) {
287
- const result = await saveMethod(item, state.type, state.parentRow?.data)
288
- if (result) {
289
- item = result
290
- }
291
- }
292
- insert(item)
293
- return
294
- }
295
-
296
- // normal + update
297
- if (saveMethod) {
298
- const result = await saveMethod(item, state.type, state.parentRow?.data)
299
- if (result) {
300
- item = result
301
- }
302
- }
303
-
304
- const { row } = state
305
- row &&
306
- Object.keys(item).forEach((key) => {
307
- o(row.data).set(key, o(item).get(key))
308
- })
309
- })
310
-
311
- const handleDelete = runWithLoading(async (row: TableRow) => {
312
- const { deleteMethod } = props
313
-
314
- if (deleteMethod) {
315
- await deleteMethod([row.data])
316
- }
317
-
318
- const data = [...(props.data ?? [])]
319
-
320
- const parent = visitDataByIndexPath(data, row.indexes.slice(0, -1), childrenKey.value)
321
-
322
- if (parent) {
323
- const children = parent[childrenKey.value]
324
- if (children) {
325
- children.splice(row.index, 1)
326
- } else {
327
- console.error('行路径不正确')
328
- }
329
- } else {
330
- data.splice(row.index, 1)
331
- }
332
-
333
- if (state.row === row) {
334
- state.row = undefined
335
- state.depth = undefined
336
- }
337
-
338
- emit('update:data', data)
339
- })
340
-
341
- function handleClose() {
342
- state.visible = false
343
- if (state.row) {
344
- state.row.isCurrent = false
345
- }
346
- state.row = undefined
347
- state.parentRow = undefined
348
- state.depth = undefined
349
- insertIndexes.value = []
350
- }
351
-
352
- return {
353
- state,
354
- insertIndexes,
355
- handleDelete,
356
- handleInsertChild,
357
- handleInsertToNext,
358
- handleInsertToPrev,
359
- handleCreate,
360
- handleEdit,
361
- handleCopy,
362
- handleClose,
363
- handleSave
364
- }
365
- }