@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.
- package/dist/components/action/action-group.vue.d.ts +1 -1
- package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
- package/dist/components/batch-edit/batch-edit-form.js +40 -74
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/batch-edit-list.js +17 -29
- package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
- package/dist/components/batch-edit/batch-edit.js +37 -60
- package/dist/components/batch-edit/batch-edit.js.map +1 -1
- package/dist/components/batch-edit/batch-edit.vue.d.ts +3 -14
- package/dist/components/batch-edit/di.js.map +1 -1
- package/dist/components/batch-edit/form-action-header.js +25 -0
- package/dist/components/batch-edit/form-action-header.js.map +1 -0
- package/dist/components/batch-edit/style2.css +0 -13
- package/dist/components/batch-edit/use-edit-state.js +44 -0
- package/dist/components/batch-edit/use-edit-state.js.map +1 -0
- package/dist/components/batch-edit/use-handlers.js +146 -0
- package/dist/components/batch-edit/use-handlers.js.map +1 -0
- package/dist/components/batch-edit/use-shortcut-key.js +44 -0
- package/dist/components/batch-edit/use-shortcut-key.js.map +1 -0
- package/dist/components/cascade/cascade.vue.d.ts +2 -2
- package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
- package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
- package/dist/components/form/form.js.map +1 -1
- package/dist/components/form/form.vue.d.ts +5 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form-item/form-item.js.map +1 -1
- package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +3 -2
- package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/input/input.vue.d.ts +2 -2
- package/dist/components/multi-select/multi-select.vue.d.ts +2 -2
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +3 -3
- package/dist/components/number-input/number-input.vue.d.ts +2 -2
- package/dist/components/number-range-input/number-range-input.vue.d.ts +1 -1
- package/dist/components/password-input/password-input.vue.d.ts +1 -1
- package/dist/components/progress-nodes/progress-nodes.vue.d.ts +1 -1
- package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +1 -1
- package/dist/components/select/select.vue.d.ts +2 -2
- package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
- package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
- package/dist/components/tabs/tabs.vue.d.ts +2 -2
- package/dist/components/textarea/textarea.vue.d.ts +2 -2
- package/dist/components/tree/tree.vue.d.ts +1 -1
- package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
- package/dist/components/watermark/watermark.vue.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/types/batch-edit.d.ts +38 -12
- package/dist/types/form.d.ts +4 -1
- package/dist/types/index.d.ts +3 -3
- package/package.json +6 -6
- package/src/components/batch-edit/batch-edit-form.vue +39 -79
- package/src/components/batch-edit/batch-edit-list.vue +13 -29
- package/src/components/batch-edit/batch-edit.vue +27 -92
- package/src/components/batch-edit/di.ts +6 -5
- package/src/components/batch-edit/form-action-header.ts +21 -0
- package/src/components/batch-edit/style.scss +0 -14
- package/src/components/batch-edit/use-dialog-effect.ts +21 -0
- package/src/components/batch-edit/use-edit-state.ts +61 -0
- package/src/components/batch-edit/use-handlers.ts +226 -0
- package/src/components/batch-edit/use-shortcut-key.ts +58 -0
- package/src/components/form/form.vue +10 -3
- package/src/components/form-item/form-item.vue +4 -2
- package/src/types/batch-edit.ts +43 -22
- package/src/types/form.ts +4 -0
- package/dist/components/batch-edit/use-edit.js +0 -231
- package/dist/components/batch-edit/use-edit.js.map +0 -1
- package/src/components/batch-edit/use-edit.ts +0 -365
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<aside :class="cls.e('form')" v-if="state.
|
|
2
|
+
<aside :class="cls.e('form')" v-if="state.formVisible && !!props.model">
|
|
3
3
|
<header :class="cls.e('form-header')">
|
|
4
|
-
<span :class="[cls.e('form-icon'), bem.is(
|
|
4
|
+
<span :class="[cls.e('form-icon'), bem.is(state.formActionType)]">
|
|
5
5
|
<u-icon>
|
|
6
6
|
<component :is="headerInfo.icon" />
|
|
7
7
|
</u-icon>
|
|
8
8
|
</span>
|
|
9
9
|
<div :class="cls.e('form-title')">
|
|
10
10
|
<h3 :class="cls.e('form-title-text')">{{ headerInfo.title }}</h3>
|
|
11
|
-
<span v-if="headerInfo.chip" :class="cls.e('form-chip')">
|
|
12
|
-
{{ headerInfo.chip }}
|
|
13
|
-
</span>
|
|
14
11
|
</div>
|
|
15
12
|
<u-button
|
|
16
13
|
:class="cls.e('form-close')"
|
|
@@ -30,12 +27,13 @@
|
|
|
30
27
|
:model="props.model"
|
|
31
28
|
:readonly="props.readonly"
|
|
32
29
|
:label-width="props.labelWidth"
|
|
30
|
+
@field:change="handleFieldChange"
|
|
33
31
|
>
|
|
34
32
|
<slot
|
|
35
33
|
v-bind="{
|
|
36
34
|
row: state.row,
|
|
37
35
|
depth: state.depth,
|
|
38
|
-
indexes:
|
|
36
|
+
indexes: state.indexPath,
|
|
39
37
|
index: state.row?.index
|
|
40
38
|
}"
|
|
41
39
|
/>
|
|
@@ -44,27 +42,20 @@
|
|
|
44
42
|
</u-scroll>
|
|
45
43
|
|
|
46
44
|
<footer :class="cls.e('form-footer')">
|
|
47
|
-
<span :class="cls.e('form-hint')"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<span :class="{ [bem.is('concealed')]: !focused }"> · <u-kbd>Esc</u-kbd> 关闭</span>
|
|
51
|
-
</span>
|
|
52
|
-
<span :class="cls.e('form-hint')" v-else>
|
|
45
|
+
<span :class="cls.e('form-hint')">
|
|
46
|
+
<template v-if="props.readonly"> 只读模式 </template>
|
|
47
|
+
|
|
53
48
|
<span :class="{ [bem.is('concealed')]: !focused }">
|
|
54
|
-
<
|
|
49
|
+
<template v-if="!(state.formActionType === 'update' && props.quickEdit)">
|
|
50
|
+
<u-kbd>Ctrl + S</u-kbd> 保存 ·
|
|
51
|
+
</template>
|
|
52
|
+
<u-kbd>Esc</u-kbd> 关闭
|
|
55
53
|
</span>
|
|
56
54
|
</span>
|
|
57
55
|
|
|
58
56
|
<div :class="cls.e('form-actions')">
|
|
59
57
|
<u-button text :loading="state.loading" @click="handleClose"> 取消 </u-button>
|
|
60
|
-
<u-button
|
|
61
|
-
v-if="
|
|
62
|
-
!props.readonly && ((isNormal && (creatable || updatable)) || (isQuick && creatable))
|
|
63
|
-
"
|
|
64
|
-
:type="state.type === 'create' ? 'success' : 'primary'"
|
|
65
|
-
:loading="state.loading"
|
|
66
|
-
@click="handleSave"
|
|
67
|
-
>
|
|
58
|
+
<u-button v-if="showSaveBtn" type="primary" :loading="state.loading" @click="handleSave">
|
|
68
59
|
保存
|
|
69
60
|
</u-button>
|
|
70
61
|
</div>
|
|
@@ -73,9 +64,10 @@
|
|
|
73
64
|
</template>
|
|
74
65
|
|
|
75
66
|
<script lang="ts" setup>
|
|
76
|
-
import {
|
|
67
|
+
import { o } from '@cat-kit/core'
|
|
68
|
+
import { Close } from '@veltra/icons/normal'
|
|
77
69
|
import { bem } from '@veltra/utils'
|
|
78
|
-
import { computed, inject, shallowRef, toRef
|
|
70
|
+
import { computed, inject, shallowRef, toRef } from 'vue'
|
|
79
71
|
|
|
80
72
|
import type { _FormExposed, FormExposed } from '../../types/form'
|
|
81
73
|
import { UButton } from '../button'
|
|
@@ -84,21 +76,13 @@ import { UIcon } from '../icon'
|
|
|
84
76
|
import { UKbd } from '../kbd'
|
|
85
77
|
import { UScroll } from '../scroll'
|
|
86
78
|
import { BatchEditDIKey } from './di'
|
|
79
|
+
import { FORM_ACTION_HEADER_MAP } from './form-action-header'
|
|
87
80
|
|
|
88
81
|
defineOptions({ name: 'BatchEditForm' })
|
|
89
82
|
|
|
90
83
|
const batchEditCtx = inject(BatchEditDIKey)!
|
|
91
84
|
|
|
92
|
-
const {
|
|
93
|
-
cls,
|
|
94
|
-
props,
|
|
95
|
-
state,
|
|
96
|
-
insertIndexes,
|
|
97
|
-
handleSave,
|
|
98
|
-
handleClose,
|
|
99
|
-
staticFeatures,
|
|
100
|
-
dynamicFeatures
|
|
101
|
-
} = batchEditCtx
|
|
85
|
+
const { cls, props, state, handleClose, handleSave, staticFeatures, dynamicFeatures } = batchEditCtx
|
|
102
86
|
|
|
103
87
|
const focused = toRef(batchEditCtx, 'focused')
|
|
104
88
|
|
|
@@ -111,61 +95,37 @@ defineExpose<_FormExposed>({
|
|
|
111
95
|
reset: () => formComponentRef.value?.reset()
|
|
112
96
|
})
|
|
113
97
|
|
|
98
|
+
function handleFieldChange(field: string, value: any) {
|
|
99
|
+
if (state.row && props.quickEdit) {
|
|
100
|
+
o(state.row.data).set(field, value)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
114
104
|
const creatable = computed(() => {
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
(staticFeatures.value.has('create') || dynamicFeatures.value.create?.(state.row))
|
|
118
|
-
)
|
|
105
|
+
if (state.formActionType !== 'create') return false
|
|
106
|
+
return staticFeatures.value.has('create') || dynamicFeatures.value.create?.(state.row)
|
|
119
107
|
})
|
|
120
108
|
|
|
121
|
-
const
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
(staticFeatures.value.has('update') || dynamicFeatures.value.update?.(state.row))
|
|
125
|
-
)
|
|
109
|
+
const creatableChild = computed(() => {
|
|
110
|
+
if (state.formActionType !== 'createChild') return false
|
|
111
|
+
return staticFeatures.value.has('createChild') || dynamicFeatures.value.createChild?.(state.row)
|
|
126
112
|
})
|
|
127
113
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
interface HeaderInfo {
|
|
133
|
-
icon: Component
|
|
134
|
-
title: string
|
|
135
|
-
chip?: string
|
|
136
|
-
tone: 'create' | 'update' | 'view' | 'child'
|
|
137
|
-
}
|
|
114
|
+
const updatable = computed(() => {
|
|
115
|
+
if (state.formActionType !== 'update') return false
|
|
116
|
+
return staticFeatures.value.has('update') || dynamicFeatures.value.update?.(state.row)
|
|
117
|
+
})
|
|
138
118
|
|
|
139
|
-
const
|
|
140
|
-
if (props.readonly)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
title: '查看详情',
|
|
144
|
-
chip: state.row ? `第 ${state.row.index + 1} 行` : undefined,
|
|
145
|
-
tone: 'view'
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (state.parentRow) {
|
|
149
|
-
return {
|
|
150
|
-
icon: AddChild,
|
|
151
|
-
title: '新增子级',
|
|
152
|
-
chip: `归属于第 ${state.parentRow.index + 1} 行`,
|
|
153
|
-
tone: 'child'
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (state.type === 'create') {
|
|
157
|
-
return { icon: Plus, title: '新增', tone: 'create' }
|
|
158
|
-
}
|
|
159
|
-
return {
|
|
160
|
-
icon: EditPen,
|
|
161
|
-
title: '编辑',
|
|
162
|
-
chip: state.row ? `第 ${state.row.index + 1} 行` : undefined,
|
|
163
|
-
tone: 'update'
|
|
164
|
-
}
|
|
119
|
+
const showSaveBtn = computed(() => {
|
|
120
|
+
if (props.readonly) return false
|
|
121
|
+
if (props.quickEdit && state.formActionType === 'update') return false
|
|
122
|
+
return creatable.value || updatable.value || creatableChild.value
|
|
165
123
|
})
|
|
166
124
|
|
|
125
|
+
const headerInfo = computed(() => FORM_ACTION_HEADER_MAP[state.formActionType])
|
|
126
|
+
|
|
167
127
|
/** 通过 row 切换 / 类型切换驱动表单内容淡出淡入 */
|
|
168
128
|
const bodyKey = computed(() => {
|
|
169
|
-
return `${state.
|
|
129
|
+
return `${state.formActionType}-${state.row?.uid ?? 'create'}-${state.parentRow?.uid ?? 'root'}`
|
|
170
130
|
})
|
|
171
131
|
</script>
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
<u-table
|
|
3
3
|
v-bind="tableProps"
|
|
4
4
|
:slots="slots"
|
|
5
|
-
:class="[cls.e('table')
|
|
5
|
+
:class="[cls.e('table')]"
|
|
6
6
|
:columns="columns"
|
|
7
7
|
highlight-current
|
|
8
8
|
:current="state.row"
|
|
9
9
|
@update:current="handleUpdateCurrentRow"
|
|
10
10
|
@update:checked="emit('update:checked', $event)"
|
|
11
11
|
@update:selected="emit('update:selected', $event)"
|
|
12
|
-
ref="tableRef"
|
|
13
12
|
>
|
|
14
13
|
<template #column:__action__="{ row }">
|
|
15
14
|
<u-action-group :class="cls.e('row-actions')" :max="5" @click.stop circle>
|
|
@@ -31,13 +30,6 @@
|
|
|
31
30
|
title="添加子级"
|
|
32
31
|
@run="handleInsertChild(row)"
|
|
33
32
|
/>
|
|
34
|
-
<u-action
|
|
35
|
-
v-if="allowed('copy', row)"
|
|
36
|
-
:icon="Copy"
|
|
37
|
-
title="复制此行"
|
|
38
|
-
@run="handleCopy(row)"
|
|
39
|
-
/>
|
|
40
|
-
|
|
41
33
|
<u-action
|
|
42
34
|
v-if="canDelete(row)"
|
|
43
35
|
:icon="Delete"
|
|
@@ -49,16 +41,8 @@
|
|
|
49
41
|
</u-action-group>
|
|
50
42
|
</template>
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<template
|
|
55
|
-
#append
|
|
56
|
-
v-if="
|
|
57
|
-
!props.readonly &&
|
|
58
|
-
(staticFeatures.has('create') || dynamicFeatures.create?.()) &&
|
|
59
|
-
((state.type === 'create' && !state.visible) || state.type === 'update')
|
|
60
|
-
"
|
|
61
|
-
>
|
|
44
|
+
<!-- 新增按钮 -->
|
|
45
|
+
<template #append v-if="showCreateBtn">
|
|
62
46
|
<div :class="cls.e('add')">
|
|
63
47
|
<u-button
|
|
64
48
|
:class="cls.e('add-btn')"
|
|
@@ -77,8 +61,7 @@
|
|
|
77
61
|
|
|
78
62
|
<script setup lang="ts">
|
|
79
63
|
import { o } from '@cat-kit/core'
|
|
80
|
-
import { AddChild,
|
|
81
|
-
import { bem } from '@veltra/utils'
|
|
64
|
+
import { AddChild, Delete, InsertToNext, InsertToPrev, Plus } from '@veltra/icons/normal'
|
|
82
65
|
import { computed, inject, type Slots } from 'vue'
|
|
83
66
|
|
|
84
67
|
import type { BatchEditFeature, TableRow } from '../../types'
|
|
@@ -94,13 +77,11 @@ defineProps<{ slots: Slots }>()
|
|
|
94
77
|
const {
|
|
95
78
|
cls,
|
|
96
79
|
state,
|
|
97
|
-
tableRef,
|
|
98
80
|
props,
|
|
99
81
|
emit,
|
|
100
82
|
staticFeatures,
|
|
101
83
|
dynamicFeatures,
|
|
102
84
|
handleCreate,
|
|
103
|
-
handleCopy,
|
|
104
85
|
handleDelete,
|
|
105
86
|
handleInsertToNext,
|
|
106
87
|
handleInsertToPrev,
|
|
@@ -128,8 +109,7 @@ const allowed = (feature: BatchEditFeature, row?: TableRow) => {
|
|
|
128
109
|
}
|
|
129
110
|
|
|
130
111
|
const columns = computed(() => {
|
|
131
|
-
if (props.readonly || hasNot(['create', 'delete', 'createChild', 'update'
|
|
132
|
-
return props.columns
|
|
112
|
+
if (props.readonly || hasNot(['create', 'delete', 'createChild', 'update'])) return props.columns
|
|
133
113
|
|
|
134
114
|
return (props.columns ?? []).concat({
|
|
135
115
|
name: '操作',
|
|
@@ -143,15 +123,19 @@ const columns = computed(() => {
|
|
|
143
123
|
|
|
144
124
|
function handleUpdateCurrentRow(row?: TableRow) {
|
|
145
125
|
if (allowed('update', row)) {
|
|
146
|
-
if (row) {
|
|
147
|
-
state.type = 'update'
|
|
148
|
-
}
|
|
149
126
|
state.row = row
|
|
150
|
-
state.depth = row?.depth
|
|
151
127
|
}
|
|
152
128
|
}
|
|
153
129
|
|
|
154
130
|
function canDelete(row: TableRow) {
|
|
155
131
|
return !props.readonly && allowed('delete', row)
|
|
156
132
|
}
|
|
133
|
+
|
|
134
|
+
const showCreateBtn = computed(() => {
|
|
135
|
+
if (props.readonly) return false
|
|
136
|
+
|
|
137
|
+
const hasFeature = staticFeatures.value.has('create') || dynamicFeatures.value.create?.()
|
|
138
|
+
const createActionVisible = state.formActionType === 'create' && state.formVisible
|
|
139
|
+
return hasFeature && !createActionVisible
|
|
140
|
+
})
|
|
157
141
|
</script>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<u-layout
|
|
3
3
|
:class="cls.b"
|
|
4
4
|
:cols="cols"
|
|
5
|
-
rows="minmax(0, 1fr)"
|
|
5
|
+
rows="minmax(0, 1fr) "
|
|
6
6
|
gap="8px"
|
|
7
7
|
resizable
|
|
8
8
|
tabindex="-1"
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
@keydown.capture="handleKeydown"
|
|
12
12
|
>
|
|
13
13
|
<!-- 编辑列表 -->
|
|
14
|
-
<BatchEditList :slots="slots"
|
|
14
|
+
<BatchEditList :slots="slots" />
|
|
15
15
|
|
|
16
16
|
<!-- 编辑表单 -->
|
|
17
|
-
<BatchEditForm ref="
|
|
17
|
+
<BatchEditForm ref="form" v-slot="scoped">
|
|
18
18
|
<slot name="form" v-bind="scoped" />
|
|
19
19
|
</BatchEditForm>
|
|
20
20
|
</u-layout>
|
|
@@ -22,121 +22,56 @@
|
|
|
22
22
|
|
|
23
23
|
<script lang="ts" setup>
|
|
24
24
|
import { bem } from '@veltra/utils'
|
|
25
|
-
import { computed,
|
|
25
|
+
import { computed, provide, useTemplateRef } from 'vue'
|
|
26
26
|
|
|
27
|
-
import type {
|
|
28
|
-
BatchEditEmits,
|
|
29
|
-
BatchEditProps,
|
|
30
|
-
TableColumnSlotsScope,
|
|
31
|
-
TableExposed,
|
|
32
|
-
TableRow
|
|
33
|
-
} from '../../types'
|
|
34
|
-
import type { FormExposed } from '../../types/form'
|
|
35
|
-
import { DialogDIKey } from '../dialog/di'
|
|
27
|
+
import type { BatchEditEmits, BatchEditProps, BatchEditSlots, FormExposed } from '../../types'
|
|
36
28
|
import { ULayout } from '../layout'
|
|
37
29
|
import BatchEditForm from './batch-edit-form.vue'
|
|
38
30
|
import BatchEditList from './batch-edit-list.vue'
|
|
39
31
|
import { BatchEditDIKey } from './di'
|
|
40
|
-
import {
|
|
32
|
+
import { useEditState } from './use-edit-state'
|
|
41
33
|
import { useFeatures } from './use-features'
|
|
34
|
+
import { useHandlers } from './use-handlers'
|
|
35
|
+
import { useShortcutKey } from './use-shortcut-key'
|
|
42
36
|
|
|
43
37
|
defineOptions({ name: 'BatchEdit' })
|
|
44
38
|
|
|
45
|
-
const props = withDefaults(defineProps<BatchEditProps>(), {
|
|
46
|
-
cols: () => ['1fr', '420px'],
|
|
47
|
-
mode: 'normal'
|
|
48
|
-
})
|
|
39
|
+
const props = withDefaults(defineProps<BatchEditProps>(), { cols: () => ['1fr', '420px'] })
|
|
49
40
|
|
|
50
41
|
const emit = defineEmits<BatchEditEmits>()
|
|
51
42
|
|
|
52
|
-
const slots = defineSlots<
|
|
53
|
-
{
|
|
54
|
-
form?: (props: {
|
|
55
|
-
/** 当前编辑的层级 */
|
|
56
|
-
depth?: number
|
|
57
|
-
/** 当前编辑的行 */
|
|
58
|
-
row?: TableRow
|
|
59
|
-
/** 当前编辑的行索引 */
|
|
60
|
-
index?: number
|
|
61
|
-
/** 操作的目标行索引路径 */
|
|
62
|
-
indexes?: number[]
|
|
63
|
-
}) => any
|
|
64
|
-
|
|
65
|
-
header?: () => any
|
|
66
|
-
} & Partial<{ [key: `column:${string}`]: (props: TableColumnSlotsScope) => any }>
|
|
67
|
-
>()
|
|
43
|
+
const slots = defineSlots<BatchEditSlots>()
|
|
68
44
|
|
|
69
45
|
const cls = bem('batch-edit')
|
|
70
46
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const
|
|
47
|
+
const formRef = useTemplateRef<FormExposed>('form')
|
|
48
|
+
|
|
49
|
+
const { state, resetState } = useEditState({ props, formRef })
|
|
74
50
|
|
|
75
51
|
const { staticFeatures, dynamicFeatures } = useFeatures({ props })
|
|
76
52
|
|
|
77
|
-
const
|
|
53
|
+
const handlers = useHandlers({ props, emit, state, resetState, formRef })
|
|
54
|
+
|
|
55
|
+
// 快捷键处理
|
|
56
|
+
const { handleFocusIn, handleFocusOut, handleKeydown, focused } = useShortcutKey({
|
|
57
|
+
props,
|
|
58
|
+
onSave: handlers.handleSave,
|
|
59
|
+
onClose: handlers.handleClose,
|
|
60
|
+
state
|
|
61
|
+
})
|
|
78
62
|
|
|
79
|
-
const
|
|
63
|
+
const cols = computed(() => {
|
|
64
|
+
return state.formVisible ? props.cols : undefined
|
|
65
|
+
})
|
|
80
66
|
|
|
81
67
|
provide(BatchEditDIKey, {
|
|
82
68
|
cls,
|
|
83
69
|
props,
|
|
84
70
|
emit,
|
|
85
|
-
|
|
71
|
+
state,
|
|
86
72
|
staticFeatures,
|
|
87
73
|
dynamicFeatures,
|
|
88
74
|
focused,
|
|
89
|
-
...
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
const dialogCtx = inject(DialogDIKey, undefined)
|
|
93
|
-
|
|
94
|
-
dialogCtx &&
|
|
95
|
-
watch(dialogCtx.visible, (visible) => {
|
|
96
|
-
!visible && editCtx.handleClose()
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const cols = computed(() => {
|
|
100
|
-
return !!state.row || state.visible ? props.cols : undefined
|
|
75
|
+
...handlers
|
|
101
76
|
})
|
|
102
|
-
|
|
103
|
-
function handleFocusIn() {
|
|
104
|
-
focused.value = true
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function handleFocusOut(e: FocusEvent) {
|
|
108
|
-
const root = e.currentTarget as HTMLElement
|
|
109
|
-
if (!root.contains(e.relatedTarget as Node | null)) {
|
|
110
|
-
focused.value = false
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 键盘快捷键(仅组件获焦时生效):
|
|
116
|
-
* - Esc 关闭表单
|
|
117
|
-
* - ⌘/Ctrl + S 保存(快速编辑模式下编辑行时除外)
|
|
118
|
-
*/
|
|
119
|
-
function handleKeydown(e: KeyboardEvent) {
|
|
120
|
-
if (!focused.value) return
|
|
121
|
-
if (props.readonly && e.key !== 'Escape') return
|
|
122
|
-
|
|
123
|
-
if (e.key === 'Escape') {
|
|
124
|
-
if (state.visible) {
|
|
125
|
-
e.preventDefault()
|
|
126
|
-
handleClose()
|
|
127
|
-
}
|
|
128
|
-
return
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const meta = e.metaKey || e.ctrlKey
|
|
132
|
-
if (!meta) return
|
|
133
|
-
|
|
134
|
-
const key = e.key.toLowerCase()
|
|
135
|
-
|
|
136
|
-
if (key === 's' && state.visible) {
|
|
137
|
-
if (props.mode === 'quick' && state.type === 'update') return
|
|
138
|
-
e.preventDefault()
|
|
139
|
-
handleSave()
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
77
|
</script>
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import type { BEM } from '@veltra/utils'
|
|
2
|
-
import type { ComputedRef, InjectionKey, ShallowRef
|
|
2
|
+
import type { ComputedRef, InjectionKey, ShallowRef } from 'vue'
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
5
|
BatchEditEmits,
|
|
6
6
|
BatchEditFeature,
|
|
7
7
|
BatchEditProps,
|
|
8
|
-
|
|
8
|
+
BatchEditStates,
|
|
9
9
|
TableRow
|
|
10
10
|
} from '../../types'
|
|
11
|
-
import type { EditReturned } from './use-
|
|
11
|
+
import type { EditReturned } from './use-handlers'
|
|
12
|
+
|
|
12
13
|
export const BatchEditDIKey: InjectionKey<
|
|
13
14
|
{
|
|
14
15
|
cls: BEM<'batch-edit', 'u-'>
|
|
15
16
|
props: BatchEditProps
|
|
16
17
|
emit: BatchEditEmits
|
|
17
|
-
|
|
18
|
+
state: BatchEditStates
|
|
18
19
|
staticFeatures: ComputedRef<Set<BatchEditFeature>>
|
|
19
20
|
dynamicFeatures: ComputedRef<
|
|
20
21
|
Record<BatchEditFeature, ((row?: TableRow) => boolean) | undefined>
|
|
21
22
|
>
|
|
22
23
|
/** 组件或其子节点是否处于聚焦状态 */
|
|
23
|
-
focused:
|
|
24
|
+
focused: ShallowRef<boolean>
|
|
24
25
|
} & EditReturned
|
|
25
26
|
> = Symbol('BatchEditDIKey')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AddChild, EditPen, Plus, View } from '@veltra/icons/normal'
|
|
2
|
+
import type { Component } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { BatchEditStates } from '../../types'
|
|
5
|
+
|
|
6
|
+
/** 表单操作类型对应的头部展示信息 */
|
|
7
|
+
export interface FormActionHeaderInfo {
|
|
8
|
+
icon: Component
|
|
9
|
+
title: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** 按 formActionType 映射的表单头部信息 */
|
|
13
|
+
export const FORM_ACTION_HEADER_MAP: Record<
|
|
14
|
+
BatchEditStates['formActionType'],
|
|
15
|
+
FormActionHeaderInfo
|
|
16
|
+
> = {
|
|
17
|
+
create: { icon: Plus, title: '新增' },
|
|
18
|
+
update: { icon: EditPen, title: '编辑' },
|
|
19
|
+
view: { icon: View, title: '查看详情' },
|
|
20
|
+
createChild: { icon: AddChild, title: '新增子级' }
|
|
21
|
+
}
|
|
@@ -121,20 +121,6 @@ $root-name: batch-edit;
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
@include m.e(form-chip) {
|
|
125
|
-
display: inline-block;
|
|
126
|
-
padding: 1px 8px;
|
|
127
|
-
font-size: 12px;
|
|
128
|
-
line-height: 18px;
|
|
129
|
-
border-radius: fn.use-var(radius, default);
|
|
130
|
-
background-color: fn.use-var(bg-color, default);
|
|
131
|
-
color: fn.use-var(text-color, second);
|
|
132
|
-
white-space: nowrap;
|
|
133
|
-
overflow: hidden;
|
|
134
|
-
text-overflow: ellipsis;
|
|
135
|
-
max-width: 60%;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
124
|
@include m.e(form-close) {
|
|
139
125
|
flex-shrink: 0;
|
|
140
126
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { inject, watch } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { DialogDIKey } from '../dialog/di'
|
|
4
|
+
|
|
5
|
+
interface Options {
|
|
6
|
+
/** 关闭表单 */
|
|
7
|
+
closeForm: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 在表单上的副作用。
|
|
12
|
+
* 包括:关闭表单
|
|
13
|
+
*/
|
|
14
|
+
export function useDialogEffect(options: Options) {
|
|
15
|
+
const dialogCtx = inject(DialogDIKey, undefined)
|
|
16
|
+
if (!dialogCtx) return
|
|
17
|
+
|
|
18
|
+
watch(dialogCtx.visible, (visible) => {
|
|
19
|
+
!visible && options.closeForm?.()
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { o } from '@cat-kit/core'
|
|
2
|
+
import { nextTick, shallowReactive, watch, type ShallowRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { BatchEditProps, BatchEditStates, FormExposed } from '../../types'
|
|
5
|
+
|
|
6
|
+
interface Options {
|
|
7
|
+
props: BatchEditProps
|
|
8
|
+
formRef: ShallowRef<FormExposed | null>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useEditState(options: Options) {
|
|
12
|
+
const { props, formRef } = options
|
|
13
|
+
|
|
14
|
+
const defaultState: BatchEditStates = {
|
|
15
|
+
depth: -1,
|
|
16
|
+
formVisible: false,
|
|
17
|
+
formActionType: 'create',
|
|
18
|
+
loading: false,
|
|
19
|
+
indexPath: []
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const state = shallowReactive({ ...defaultState })
|
|
23
|
+
|
|
24
|
+
function resetState() {
|
|
25
|
+
if (state.row) {
|
|
26
|
+
state.row.isCurrent = false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Object.keys(state).forEach((key) => {
|
|
30
|
+
delete state[key]
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
Object.assign(state, defaultState)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
watch(
|
|
37
|
+
() => state.row,
|
|
38
|
+
(row) => {
|
|
39
|
+
formRef.value?.reset()
|
|
40
|
+
|
|
41
|
+
if (row) {
|
|
42
|
+
/** 延迟设置 model,防止表单初始状态丢失 */
|
|
43
|
+
nextTick(() => {
|
|
44
|
+
if (props.model) {
|
|
45
|
+
o(props.model).deepExtend(row.data)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
state.formActionType = props.readonly ? 'view' : 'update'
|
|
49
|
+
state.formVisible = true
|
|
50
|
+
state.depth = row.depth
|
|
51
|
+
state.indexPath = [...row.indexes]
|
|
52
|
+
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
resetState()
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return { state, resetState }
|
|
61
|
+
}
|