@veltra/desktop 1.1.24 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/action/action-group.vue.d.ts +2 -2
- package/dist/components/action/action.js.map +1 -1
- package/dist/components/action/action.vue.d.ts +1 -1
- package/dist/components/badge/badge.js.map +1 -1
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
- package/dist/components/batch-edit/batch-edit.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/button/button-group.js.map +1 -1
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/calendar/calendar.js.map +1 -1
- package/dist/components/card/card-action.js.map +1 -1
- package/dist/components/card/card-content.js.map +1 -1
- package/dist/components/card/card-cover.js.map +1 -1
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/cascade/cascade-panel-item.js.map +1 -1
- package/dist/components/cascade/cascade.js.map +1 -1
- package/dist/components/cascade/cascade.vue.d.ts +1 -1
- package/dist/components/check-tag/check-tag.js.map +1 -1
- package/dist/components/checkbox/checkbox-button.js.map +1 -1
- package/dist/components/checkbox/checkbox.js.map +1 -1
- package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
- package/dist/components/code-editor/code-editor.js.map +1 -1
- package/dist/components/collapse/collapse-item.js.map +1 -1
- package/dist/components/collapse/collapse.js.map +1 -1
- package/dist/components/condition-editor/components/condition-row.js.map +1 -1
- package/dist/components/condition-editor/condition-editor.js.map +1 -1
- package/dist/components/context-menu/context-menu-item.js.map +1 -1
- package/dist/components/context-menu/context-menu.js.map +1 -1
- package/dist/components/date-panel/date-panel.js.map +1 -1
- package/dist/components/date-panel/headers/day.js.map +1 -1
- package/dist/components/date-panel/headers/month.js.map +1 -1
- package/dist/components/date-panel/headers/year.js.map +1 -1
- package/dist/components/date-panel/panels/day.js.map +1 -1
- package/dist/components/date-panel/panels/month.js.map +1 -1
- package/dist/components/date-panel/panels/year.js.map +1 -1
- package/dist/components/date-picker/date-picker.js.map +1 -1
- package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
- package/dist/components/dialog/dialog.js.map +1 -1
- package/dist/components/drawer/drawer.js.map +1 -1
- package/dist/components/empty/empty.js.map +1 -1
- package/dist/components/expression-editor/components/path-preview.js.map +1 -1
- package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
- package/dist/components/expression-editor/expression-editor.js.map +1 -1
- package/dist/components/file-picker/file-picker.js.map +1 -1
- package/dist/components/file-viewer/file-viewer.js.map +1 -1
- package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -1
- package/dist/components/file-viewer/previewers/pdf-viewport-scroll.js.map +1 -1
- package/dist/components/file-viewer/previewers/pdf-zoom-bridge.js.map +1 -1
- package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -1
- package/dist/components/float-button/float-button.js.map +1 -1
- package/dist/components/form/dynamic-form-model.d.ts +7 -5
- package/dist/components/form/dynamic-form-model.js +31 -12
- package/dist/components/form/dynamic-form-model.js.map +1 -1
- package/dist/components/form/form-model.d.ts +8 -8
- package/dist/components/form/form-model.js +17 -3
- package/dist/components/form/form-model.js.map +1 -1
- package/dist/components/form/form.js.map +1 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/helper.d.ts +6 -1
- package/dist/components/form/helper.js +7 -1
- package/dist/components/form/helper.js.map +1 -1
- package/dist/components/form/index.d.ts +2 -2
- package/dist/components/gantt-chart/gantt-chart.js.map +1 -1
- package/dist/components/grid/grid-item.js.map +1 -1
- package/dist/components/grid/grid.js.map +1 -1
- package/dist/components/grid-input/grid-input.js.map +1 -1
- package/dist/components/group-input/group-input.js.map +1 -1
- package/dist/components/icon/icon.js.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +2 -1
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/layout/layout-resizer.js.map +1 -1
- package/dist/components/layout/layout.js.map +1 -1
- package/dist/components/list/list-item.js.map +1 -1
- package/dist/components/list/list.js.map +1 -1
- package/dist/components/loading/loading.js.map +1 -1
- package/dist/components/menu/menu-icon.js.map +1 -1
- package/dist/components/menu/menu-sub-collapsed.js.map +1 -1
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/message/message-box.js.map +1 -1
- package/dist/components/message/message.js.map +1 -1
- package/dist/components/message-confirm/message-confirm.js.map +1 -1
- package/dist/components/multi-select/multi-select-option.js.map +1 -1
- package/dist/components/multi-select/multi-select.vue.d.ts +1 -1
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +1 -1
- package/dist/components/notification/notification.js.map +1 -1
- package/dist/components/notification/notification.vue.d.ts +2 -2
- package/dist/components/number/number.js.map +1 -1
- package/dist/components/number-input/number-input.js.map +1 -1
- package/dist/components/number-range-input/number-range-input.js.map +1 -1
- package/dist/components/paginator/paginator.js.map +1 -1
- package/dist/components/palette/palette-alpha.js.map +1 -1
- package/dist/components/palette/palette-color-switch.js.map +1 -1
- package/dist/components/palette/palette-hue.js.map +1 -1
- package/dist/components/palette/palette-sv.js.map +1 -1
- package/dist/components/palette/palette.js.map +1 -1
- package/dist/components/pop-confirm/pop-confirm.js.map +1 -1
- package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
- package/dist/components/progress/progress.js.map +1 -1
- package/dist/components/progress-nodes/progress-nodes.js.map +1 -1
- package/dist/components/radio/radio.js.map +1 -1
- package/dist/components/radio-group/radio-group.js.map +1 -1
- package/dist/components/scroll/scroll-bar.js.map +1 -1
- package/dist/components/scroll/scroll.js.map +1 -1
- package/dist/components/slider/slider-thumb.js.map +1 -1
- package/dist/components/slider/slider.js.map +1 -1
- package/dist/components/steps/steps.js.map +1 -1
- package/dist/components/switch/switch.js.map +1 -1
- package/dist/components/table/table-body.js.map +1 -1
- package/dist/components/table/table-cell.js.map +1 -1
- package/dist/components/table/table-foot.js.map +1 -1
- package/dist/components/table-editor/table-editor.js.map +1 -1
- package/dist/components/tabs/tabs-horizontal.js.map +1 -1
- package/dist/components/tabs/tabs-horizontal.vue.d.ts +4 -4
- package/dist/components/tabs/tabs-vertical.js.map +1 -1
- package/dist/components/tabs/tabs-vertical.vue.d.ts +3 -3
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/tabs/tabs.vue.d.ts +4 -4
- package/dist/components/tag/tag.js.map +1 -1
- package/dist/components/text/text.js.map +1 -1
- package/dist/components/textarea/textarea.js.map +1 -1
- package/dist/components/textarea/textarea.vue.d.ts +1 -1
- package/dist/components/theme/theme.js.map +1 -1
- package/dist/components/tip/tip.js.map +1 -1
- package/dist/components/tip/tip.vue.d.ts +1 -1
- package/dist/components/tree/tree-node.js.map +1 -1
- package/dist/components/tree/tree.js.map +1 -1
- package/dist/components/tree-select/tree-select.vue.d.ts +1 -1
- package/dist/components/watermark/watermark.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/types/form.d.ts +12 -9
- package/dist/types/index.d.ts +2 -2
- package/package.json +6 -6
- package/src/components/action/action.vue +1 -4
- package/src/components/badge/badge.vue +3 -9
- package/src/components/batch-edit/batch-edit-form.vue +1 -5
- package/src/components/batch-edit/batch-edit-list.vue +2 -6
- package/src/components/batch-edit/batch-edit.vue +2 -12
- package/src/components/breadcrumb/breadcrumb.vue +4 -13
- package/src/components/button/button-group.vue +2 -6
- package/src/components/button/button.vue +3 -9
- package/src/components/calendar/calendar.vue +1 -3
- package/src/components/card/card-action.vue +1 -3
- package/src/components/card/card-content.vue +1 -6
- package/src/components/card/card-cover.vue +1 -3
- package/src/components/card/card.vue +2 -6
- package/src/components/cascade/cascade-panel-item.vue +2 -8
- package/src/components/cascade/cascade.vue +5 -37
- package/src/components/check-tag/check-tag.vue +1 -3
- package/src/components/checkbox/checkbox-button.vue +2 -6
- package/src/components/checkbox/checkbox.vue +1 -3
- package/src/components/checkbox-group/checkbox-group.vue +1 -3
- package/src/components/code-editor/code-editor.vue +4 -12
- package/src/components/collapse/collapse-item.vue +1 -3
- package/src/components/collapse/collapse.vue +2 -8
- package/src/components/condition-editor/components/condition-row.vue +2 -8
- package/src/components/condition-editor/condition-editor.vue +2 -6
- package/src/components/context-menu/context-menu-item.vue +3 -10
- package/src/components/context-menu/context-menu.vue +5 -17
- package/src/components/date-panel/date-panel.vue +3 -11
- package/src/components/date-panel/headers/day.vue +1 -3
- package/src/components/date-panel/headers/month.vue +1 -3
- package/src/components/date-panel/headers/year.vue +1 -3
- package/src/components/date-panel/panels/day.vue +1 -4
- package/src/components/date-panel/panels/month.vue +1 -3
- package/src/components/date-panel/panels/year.vue +1 -3
- package/src/components/date-picker/date-picker.vue +1 -3
- package/src/components/date-range-picker/date-range-picker.vue +1 -3
- package/src/components/dialog/dialog.vue +7 -24
- package/src/components/drawer/drawer.vue +3 -11
- package/src/components/empty/empty.vue +2 -7
- package/src/components/expression-editor/components/path-preview.vue +2 -6
- package/src/components/expression-editor/components/variable-picker.vue +1 -3
- package/src/components/expression-editor/expression-editor.vue +2 -6
- package/src/components/expression-editor/style.scss +1 -1
- package/src/components/file-picker/file-picker.vue +2 -7
- package/src/components/file-viewer/file-viewer.vue +4 -22
- package/src/components/file-viewer/previewers/pdf-previewer.vue +1 -5
- package/src/components/file-viewer/previewers/pdf-viewport-scroll.vue +1 -7
- package/src/components/file-viewer/previewers/pdf-zoom-bridge.vue +2 -7
- package/src/components/file-viewer/previewers/sheet-previewer.vue +4 -10
- package/src/components/float-button/float-button.vue +1 -3
- package/src/components/form/__test__/form-model.test.ts +190 -0
- package/src/components/form/dynamic-form-model.ts +47 -19
- package/src/components/form/form-model.ts +40 -28
- package/src/components/form/form.vue +3 -13
- package/src/components/form/helper.ts +9 -0
- package/src/components/form/index.ts +2 -1
- package/src/components/gantt-chart/gantt-chart.vue +1 -3
- package/src/components/grid/grid-item.vue +3 -9
- package/src/components/grid/grid.vue +5 -18
- package/src/components/grid-input/grid-input.vue +2 -6
- package/src/components/group-input/group-input.vue +2 -7
- package/src/components/icon/icon.vue +2 -6
- package/src/components/input/input.vue +2 -6
- package/src/components/layout/layout-resizer.vue +1 -3
- package/src/components/layout/layout.vue +3 -9
- package/src/components/list/list-item.vue +1 -3
- package/src/components/list/list.vue +2 -6
- package/src/components/loading/loading.vue +3 -9
- package/src/components/menu/menu-icon.vue +2 -6
- package/src/components/menu/menu-sub-collapsed.vue +2 -8
- package/src/components/menu/menu.vue +3 -9
- package/src/components/menu/style.scss +1 -1
- package/src/components/message/message-box.vue +3 -10
- package/src/components/message/message.vue +3 -9
- package/src/components/message-confirm/message-confirm.vue +2 -6
- package/src/components/multi-select/multi-select-option.vue +3 -9
- package/src/components/notification/notification.vue +3 -11
- package/src/components/number/number.vue +4 -14
- package/src/components/number-input/number-input.vue +7 -36
- package/src/components/number-range-input/number-range-input.vue +4 -17
- package/src/components/paginator/paginator.vue +7 -22
- package/src/components/palette/palette-alpha.vue +2 -6
- package/src/components/palette/palette-color-switch.vue +1 -3
- package/src/components/palette/palette-hue.vue +2 -6
- package/src/components/palette/palette-sv.vue +5 -17
- package/src/components/palette/palette.vue +2 -12
- package/src/components/pop-confirm/pop-confirm.vue +2 -6
- package/src/components/progress/progress.vue +2 -7
- package/src/components/progress-nodes/progress-nodes.vue +2 -7
- package/src/components/radio/radio.vue +2 -6
- package/src/components/radio-group/radio-group.vue +1 -3
- package/src/components/scroll/scroll-bar.vue +5 -17
- package/src/components/scroll/scroll.vue +5 -16
- package/src/components/slider/slider-thumb.vue +4 -12
- package/src/components/slider/slider.vue +3 -14
- package/src/components/steps/steps.vue +3 -10
- package/src/components/switch/switch.vue +2 -7
- package/src/components/table/table-body.vue +2 -8
- package/src/components/table/table-cell.vue +2 -7
- package/src/components/table/table-foot.vue +2 -9
- package/src/components/table-editor/table-editor.vue +1 -3
- package/src/components/tabs/tabs-horizontal.vue +2 -6
- package/src/components/tabs/tabs-vertical.vue +1 -3
- package/src/components/tabs/tabs.vue +1 -3
- package/src/components/tag/tag.vue +2 -6
- package/src/components/text/text.vue +3 -9
- package/src/components/textarea/textarea.vue +1 -3
- package/src/components/theme/group-item.vue +1 -3
- package/src/components/theme/group.vue +1 -3
- package/src/components/theme/theme.vue +6 -26
- package/src/components/tip/tip.vue +1 -3
- package/src/components/tree/tree-node.vue +1 -3
- package/src/components/tree/tree.vue +7 -30
- package/src/components/watermark/watermark.vue +4 -15
- package/src/types/form.ts +29 -11
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { DynamicFormModel } from '../dynamic-form-model'
|
|
4
|
+
import { FormModel } from '../form-model'
|
|
5
|
+
import { formField, nestField } from '../helper'
|
|
6
|
+
|
|
7
|
+
describe('FormModel with Nested Fields', () => {
|
|
8
|
+
it('should flatten fields and initialize data correctly', () => {
|
|
9
|
+
const model = new FormModel({
|
|
10
|
+
name: formField({ value: 'Alice' }),
|
|
11
|
+
contact: nestField({
|
|
12
|
+
email: formField({ value: 'alice@example.com' }),
|
|
13
|
+
phone: formField({ value: '12345678901' })
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Verify allKeys
|
|
18
|
+
expect(model.allKeys).toEqual(['name', 'contact.email', 'contact.phone'])
|
|
19
|
+
|
|
20
|
+
// Verify initialData and data
|
|
21
|
+
expect(model.data).toEqual({
|
|
22
|
+
name: 'Alice',
|
|
23
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
24
|
+
})
|
|
25
|
+
expect(model.initialData).toEqual({
|
|
26
|
+
name: 'Alice',
|
|
27
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should validate flat and nested fields correctly', async () => {
|
|
32
|
+
const model = new FormModel({
|
|
33
|
+
name: formField({ value: '', required: 'Name is required' }),
|
|
34
|
+
contact: nestField({
|
|
35
|
+
email: formField({ value: 'invalid-email', preset: 'email' }),
|
|
36
|
+
phone: formField({ value: '12345678901', required: true })
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Expect initial validation to fail on name and contact.email
|
|
41
|
+
await expect(model.validate()).rejects.toBe(false)
|
|
42
|
+
expect(model.errors.get('name')).toEqual(['Name is required'])
|
|
43
|
+
expect(model.errors.get('contact.email')).toEqual(['邮箱格式不正确'])
|
|
44
|
+
expect(model.errors.get('contact.phone')).toBeUndefined()
|
|
45
|
+
|
|
46
|
+
// Fix name and contact.email, and trigger validation
|
|
47
|
+
model.data.name = 'Bob'
|
|
48
|
+
model.data.contact.email = 'bob@example.com'
|
|
49
|
+
|
|
50
|
+
// We need to wait for watch effects/nextTick if reactive triggers validation on change
|
|
51
|
+
// Since validate() is an async function, let's call it directly
|
|
52
|
+
const valid = await model.validate()
|
|
53
|
+
expect(valid).toBe(true)
|
|
54
|
+
expect(model.errors.size).toBe(0)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should set data and reset data correctly', () => {
|
|
58
|
+
const model = new FormModel({
|
|
59
|
+
name: formField({ value: 'Alice' }),
|
|
60
|
+
contact: nestField({
|
|
61
|
+
email: formField({ value: 'alice@example.com' }),
|
|
62
|
+
phone: formField({ value: '12345678901' })
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Update data via setData
|
|
67
|
+
model.setData({ name: 'Bob', contact: { email: 'bob@example.com', phone: '98765432109' } })
|
|
68
|
+
|
|
69
|
+
expect(model.data).toEqual({
|
|
70
|
+
name: 'Bob',
|
|
71
|
+
contact: { email: 'bob@example.com', phone: '98765432109' }
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Reset data
|
|
75
|
+
model.resetData()
|
|
76
|
+
expect(model.data).toEqual({
|
|
77
|
+
name: 'Alice',
|
|
78
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('DynamicFormModel with Nested Fields', () => {
|
|
84
|
+
it('should flatten fields and initialize data correctly', () => {
|
|
85
|
+
const model = new DynamicFormModel({
|
|
86
|
+
name: formField({ value: 'Alice' }),
|
|
87
|
+
contact: nestField({
|
|
88
|
+
email: formField({ value: 'alice@example.com' }),
|
|
89
|
+
phone: formField({ value: '12345678901' })
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Verify allKeys
|
|
94
|
+
expect(model.allKeys).toEqual(['name', 'contact.email', 'contact.phone'])
|
|
95
|
+
|
|
96
|
+
// Verify initialData and data
|
|
97
|
+
expect(model.data).toEqual({
|
|
98
|
+
name: 'Alice',
|
|
99
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
100
|
+
})
|
|
101
|
+
expect(model.initialData).toEqual({
|
|
102
|
+
name: 'Alice',
|
|
103
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should support dynamic add and delete on nested fields', () => {
|
|
108
|
+
const model = new DynamicFormModel({ name: formField({ value: 'Alice' }) })
|
|
109
|
+
|
|
110
|
+
expect(model.allKeys).toEqual(['name'])
|
|
111
|
+
|
|
112
|
+
// Dynamically add nested fields
|
|
113
|
+
model.add(
|
|
114
|
+
'contact',
|
|
115
|
+
nestField({
|
|
116
|
+
email: formField({ value: 'alice@example.com' }),
|
|
117
|
+
phone: formField({ value: '12345678901' })
|
|
118
|
+
})
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
expect(model.allKeys).toEqual(['name', 'contact.email', 'contact.phone'])
|
|
122
|
+
expect(model.data).toEqual({
|
|
123
|
+
name: 'Alice',
|
|
124
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
125
|
+
})
|
|
126
|
+
expect(model.initialData).toEqual({
|
|
127
|
+
name: 'Alice',
|
|
128
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Dynamically delete nested fields
|
|
132
|
+
model.delete('contact')
|
|
133
|
+
|
|
134
|
+
expect(model.allKeys).toEqual(['name'])
|
|
135
|
+
expect(model.data.name).toBe('Alice')
|
|
136
|
+
expect(model.fields.contact).toBeUndefined()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('should validate flat and nested fields correctly', async () => {
|
|
140
|
+
const model = new DynamicFormModel()
|
|
141
|
+
|
|
142
|
+
model.add('name', formField({ value: '', required: 'Name is required' }))
|
|
143
|
+
model.add(
|
|
144
|
+
'contact',
|
|
145
|
+
nestField({
|
|
146
|
+
email: formField({ value: 'invalid-email', preset: 'email' }),
|
|
147
|
+
phone: formField({ value: '12345678901', required: true })
|
|
148
|
+
})
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
// Expect initial validation to fail on name and contact.email
|
|
152
|
+
await expect(model.validate()).resolves.toBe(false)
|
|
153
|
+
expect(model.errors.get('name')).toEqual(['Name is required'])
|
|
154
|
+
expect(model.errors.get('contact.email')).toEqual(['邮箱格式不正确'])
|
|
155
|
+
expect(model.errors.get('contact.phone')).toBeUndefined()
|
|
156
|
+
|
|
157
|
+
// Fix name and contact.email, and trigger validation
|
|
158
|
+
model.data.name = 'Bob'
|
|
159
|
+
model.data.contact.email = 'bob@example.com'
|
|
160
|
+
|
|
161
|
+
const valid = await model.validate()
|
|
162
|
+
expect(valid).toBe(true)
|
|
163
|
+
expect(model.errors.size).toBe(0)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('should set data and reset data correctly for nested fields', () => {
|
|
167
|
+
const model = new DynamicFormModel({
|
|
168
|
+
name: formField({ value: 'Alice' }),
|
|
169
|
+
contact: nestField({
|
|
170
|
+
email: formField({ value: 'alice@example.com' }),
|
|
171
|
+
phone: formField({ value: '12345678901' })
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Update data via setData
|
|
176
|
+
model.setData({ name: 'Bob', contact: { email: 'bob@example.com', phone: '98765432109' } })
|
|
177
|
+
|
|
178
|
+
expect(model.data).toEqual({
|
|
179
|
+
name: 'Bob',
|
|
180
|
+
contact: { email: 'bob@example.com', phone: '98765432109' }
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// Reset data
|
|
184
|
+
model.resetData()
|
|
185
|
+
expect(model.data).toEqual({
|
|
186
|
+
name: 'Alice',
|
|
187
|
+
contact: { email: 'alice@example.com', phone: '12345678901' }
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
})
|
|
@@ -2,7 +2,21 @@ import { o } from '@cat-kit/core'
|
|
|
2
2
|
import { Validator } from '@veltra/utils'
|
|
3
3
|
import { isReactive, reactive, shallowReactive, watch } from 'vue'
|
|
4
4
|
|
|
5
|
-
import type { FormModelItem, DataSettingConfig } from '../../types'
|
|
5
|
+
import type { FormModelField, FormModelItem, DataSettingConfig } from '../../types'
|
|
6
|
+
|
|
7
|
+
function flattenFields(fields: Record<string, any>, prefix = ''): Record<string, FormModelItem> {
|
|
8
|
+
const result: Record<string, FormModelItem> = {}
|
|
9
|
+
for (const key in fields) {
|
|
10
|
+
const item = fields[key]
|
|
11
|
+
const fullPath = prefix ? `${prefix}.${key}` : key
|
|
12
|
+
if (item && typeof item === 'object' && '__isNested' in item && item.__isNested) {
|
|
13
|
+
Object.assign(result, flattenFields((item as any).fields, fullPath))
|
|
14
|
+
} else {
|
|
15
|
+
result[fullPath] = item
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result
|
|
19
|
+
}
|
|
6
20
|
|
|
7
21
|
/**
|
|
8
22
|
* 动态表单模型
|
|
@@ -25,10 +39,12 @@ export class DynamicFormModel {
|
|
|
25
39
|
}
|
|
26
40
|
|
|
27
41
|
/** 表单规则 */
|
|
28
|
-
readonly fields: Record<string,
|
|
29
|
-
{} as Record<string,
|
|
42
|
+
readonly fields: Record<string, FormModelField> = shallowReactive(
|
|
43
|
+
{} as Record<string, FormModelField>
|
|
30
44
|
)
|
|
31
45
|
|
|
46
|
+
private flatFields: Record<string, FormModelItem>
|
|
47
|
+
|
|
32
48
|
private validator: Validator
|
|
33
49
|
|
|
34
50
|
/**
|
|
@@ -52,8 +68,17 @@ export class DynamicFormModel {
|
|
|
52
68
|
return this._allKeys
|
|
53
69
|
}
|
|
54
70
|
|
|
71
|
+
private updateFlatFields(): void {
|
|
72
|
+
for (const key in this.flatFields) {
|
|
73
|
+
delete this.flatFields[key]
|
|
74
|
+
}
|
|
75
|
+
const flatted = flattenFields(this.fields)
|
|
76
|
+
Object.assign(this.flatFields, flatted)
|
|
77
|
+
this.getAllKeys()
|
|
78
|
+
}
|
|
79
|
+
|
|
55
80
|
private getAllKeys(): void {
|
|
56
|
-
this._allKeys = Object.keys(this.
|
|
81
|
+
this._allKeys = Object.keys(this.flatFields)
|
|
57
82
|
}
|
|
58
83
|
|
|
59
84
|
/** 初始数据 */
|
|
@@ -64,10 +89,10 @@ export class DynamicFormModel {
|
|
|
64
89
|
*/
|
|
65
90
|
private readonly oldData: Record<string, any> = {}
|
|
66
91
|
|
|
67
|
-
constructor(fields?: Record<string,
|
|
92
|
+
constructor(fields?: Record<string, FormModelField>) {
|
|
93
|
+
this.flatFields = shallowReactive({} as Record<string, FormModelItem>)
|
|
94
|
+
this.validator = new Validator(this.flatFields)
|
|
68
95
|
this.append(fields ?? {})
|
|
69
|
-
this.validator = new Validator(this.fields)
|
|
70
|
-
this.getAllKeys()
|
|
71
96
|
this.watchData()
|
|
72
97
|
}
|
|
73
98
|
|
|
@@ -94,9 +119,9 @@ export class DynamicFormModel {
|
|
|
94
119
|
})
|
|
95
120
|
}
|
|
96
121
|
|
|
97
|
-
append(fields: Record<string,
|
|
122
|
+
append(fields: Record<string, FormModelField>): void {
|
|
98
123
|
for (const key in fields) {
|
|
99
|
-
this.add(key, fields[key]
|
|
124
|
+
this.add(key, fields[key]!)
|
|
100
125
|
}
|
|
101
126
|
}
|
|
102
127
|
|
|
@@ -105,17 +130,20 @@ export class DynamicFormModel {
|
|
|
105
130
|
* @param field 字段
|
|
106
131
|
* @param item 字段配置项
|
|
107
132
|
*/
|
|
108
|
-
add(field: string, item:
|
|
133
|
+
add(field: string, item: FormModelField): void {
|
|
109
134
|
this.fields[field] = item
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
const flatItem = flattenFields({ [field]: item })
|
|
136
|
+
for (const key in flatItem) {
|
|
137
|
+
const fieldItem = flatItem[key]!
|
|
138
|
+
const { value } = fieldItem
|
|
139
|
+
const v = typeof value === 'function' ? value() : value
|
|
140
|
+
if (v !== undefined) {
|
|
141
|
+
o(this.initialData).set(key, JSON.parse(JSON.stringify(v)))
|
|
142
|
+
}
|
|
143
|
+
o(this.data).set(key, v)
|
|
144
|
+
o(this.oldData).set(key, v)
|
|
115
145
|
}
|
|
116
|
-
|
|
117
|
-
o(this.oldData).set(field, v)
|
|
118
|
-
this.getAllKeys()
|
|
146
|
+
this.updateFlatFields()
|
|
119
147
|
}
|
|
120
148
|
|
|
121
149
|
/**
|
|
@@ -124,7 +152,7 @@ export class DynamicFormModel {
|
|
|
124
152
|
*/
|
|
125
153
|
delete(field: string): void {
|
|
126
154
|
delete this.fields[field]
|
|
127
|
-
this.
|
|
155
|
+
this.updateFlatFields()
|
|
128
156
|
}
|
|
129
157
|
|
|
130
158
|
/**
|
|
@@ -2,19 +2,30 @@ import { o } from '@cat-kit/core'
|
|
|
2
2
|
import { middleProxy, Validator } from '@veltra/utils'
|
|
3
3
|
import { nextTick, reactive, shallowReactive, watch, type Reactive } from 'vue'
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
import type { ModelData, DataSettingConfig, IFormModel, AllKeys, FormModelItem } from '../../types'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 递归展平字段逻辑
|
|
9
|
+
*/
|
|
10
|
+
function flattenFields(fields: Record<string, any>, prefix = ''): Record<string, FormModelItem> {
|
|
11
|
+
const result: Record<string, FormModelItem> = {}
|
|
12
|
+
for (const key in fields) {
|
|
13
|
+
const item = fields[key]
|
|
14
|
+
const fullPath = prefix ? `${prefix}.${key}` : key
|
|
15
|
+
if (item && typeof item === 'object' && '__isNested' in item && item.__isNested) {
|
|
16
|
+
Object.assign(result, flattenFields((item as any).fields, fullPath))
|
|
17
|
+
} else {
|
|
18
|
+
result[fullPath] = item
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
/**
|
|
14
25
|
* 表单模型
|
|
15
26
|
*/
|
|
16
27
|
export class FormModel<
|
|
17
|
-
Fields extends Record<string,
|
|
28
|
+
Fields extends Record<string, any> = Record<string, any>
|
|
18
29
|
> implements IFormModel<Fields> {
|
|
19
30
|
/** 表单数据 */
|
|
20
31
|
data!: ModelData<Fields>
|
|
@@ -30,14 +41,14 @@ export class FormModel<
|
|
|
30
41
|
* @description
|
|
31
42
|
* 这个值会在表单组件渲染时由表单设置,因为只有真正渲染的组件才应该被校验
|
|
32
43
|
*/
|
|
33
|
-
formKeys: Map<number,
|
|
44
|
+
formKeys: Map<number, AllKeys<Fields>[]> = new Map()
|
|
34
45
|
|
|
35
46
|
/** 初始数据 */
|
|
36
47
|
readonly initialData: ModelData<Fields>
|
|
37
48
|
|
|
38
|
-
readonly errors: Map<
|
|
49
|
+
readonly errors: Map<AllKeys<Fields>, string[] | undefined> = shallowReactive(new Map())
|
|
39
50
|
|
|
40
|
-
private validator: Validator<
|
|
51
|
+
private validator: Validator<Record<string, FormModelItem>>
|
|
41
52
|
|
|
42
53
|
/**
|
|
43
54
|
* 是否在表单值更新时校验
|
|
@@ -48,11 +59,12 @@ export class FormModel<
|
|
|
48
59
|
|
|
49
60
|
constructor(fields: Fields) {
|
|
50
61
|
this.fields = fields
|
|
62
|
+
const flatFields = flattenFields(fields)
|
|
51
63
|
const rawData = {} as ModelData<Fields>
|
|
52
64
|
const allKeys: string[] = []
|
|
53
65
|
|
|
54
|
-
for (const key in
|
|
55
|
-
const fieldItem =
|
|
66
|
+
for (const key in flatFields) {
|
|
67
|
+
const fieldItem = flatFields[key]!
|
|
56
68
|
allKeys.push(key)
|
|
57
69
|
const { value } = fieldItem
|
|
58
70
|
let v = value
|
|
@@ -72,7 +84,7 @@ export class FormModel<
|
|
|
72
84
|
|
|
73
85
|
this.setProxyData(reactive(rawData))
|
|
74
86
|
|
|
75
|
-
this.validator = new Validator(
|
|
87
|
+
this.validator = new Validator(flatFields)
|
|
76
88
|
|
|
77
89
|
return shallowReactive(this)
|
|
78
90
|
}
|
|
@@ -93,23 +105,23 @@ export class FormModel<
|
|
|
93
105
|
this.validateOnFieldChange = true
|
|
94
106
|
return
|
|
95
107
|
}
|
|
96
|
-
this.validate(fields)
|
|
108
|
+
this.validate(fields as any)
|
|
97
109
|
}
|
|
98
110
|
})
|
|
99
111
|
|
|
100
112
|
this.data = data as ModelData<Fields>
|
|
101
113
|
}
|
|
102
114
|
|
|
103
|
-
private getValidateFields(fields?:
|
|
115
|
+
private getValidateFields(fields?: AllKeys<Fields> | AllKeys<Fields>[]) {
|
|
104
116
|
if (!fields) {
|
|
105
117
|
if (this.formKeys.size) {
|
|
106
|
-
let _fields:
|
|
118
|
+
let _fields: AllKeys<Fields>[] = []
|
|
107
119
|
this.formKeys.forEach((fields) => {
|
|
108
120
|
_fields = _fields.concat(fields)
|
|
109
121
|
})
|
|
110
122
|
return _fields
|
|
111
123
|
} else {
|
|
112
|
-
return this.allKeys
|
|
124
|
+
return this.allKeys as AllKeys<Fields>[]
|
|
113
125
|
}
|
|
114
126
|
}
|
|
115
127
|
|
|
@@ -124,23 +136,23 @@ export class FormModel<
|
|
|
124
136
|
* @param fields 需要校验的字段, 不传则校验所有字段
|
|
125
137
|
* @returns
|
|
126
138
|
*/
|
|
127
|
-
async validate(fields?:
|
|
139
|
+
async validate(fields?: AllKeys<Fields> | AllKeys<Fields>[]): Promise<boolean> {
|
|
128
140
|
const { errors, validator, data } = this
|
|
129
141
|
|
|
130
|
-
const results = await validator.validate(data, this.getValidateFields(fields))
|
|
142
|
+
const results = await validator.validate(data, this.getValidateFields(fields) as any)
|
|
131
143
|
|
|
132
144
|
// 全量校验
|
|
133
145
|
if (!fields) {
|
|
134
146
|
errors.clear()
|
|
135
147
|
|
|
136
148
|
for (const field in results) {
|
|
137
|
-
errors.set(field, results[field])
|
|
149
|
+
errors.set(field as any, results[field])
|
|
138
150
|
}
|
|
139
151
|
}
|
|
140
152
|
// 局部校验
|
|
141
153
|
else {
|
|
142
154
|
~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {
|
|
143
|
-
const errs = results[field]
|
|
155
|
+
const errs = results[field as string]
|
|
144
156
|
if (errs?.length) {
|
|
145
157
|
errors.set(field, errs)
|
|
146
158
|
} else {
|
|
@@ -165,12 +177,12 @@ export class FormModel<
|
|
|
165
177
|
* 重置数据
|
|
166
178
|
* @param fields 需要重置的字段
|
|
167
179
|
*/
|
|
168
|
-
resetData(keys?:
|
|
180
|
+
resetData(keys?: AllKeys<Fields> | AllKeys<Fields>[]): void {
|
|
169
181
|
if (typeof keys === 'string') {
|
|
170
182
|
keys = [keys]
|
|
171
183
|
} else if (Array.isArray(keys)) {
|
|
172
184
|
} else {
|
|
173
|
-
keys = this.allKeys
|
|
185
|
+
keys = this.allKeys as AllKeys<Fields>[]
|
|
174
186
|
}
|
|
175
187
|
|
|
176
188
|
this.clearValidate()
|
|
@@ -229,12 +241,12 @@ export class FormModel<
|
|
|
229
241
|
* 监听值变更
|
|
230
242
|
* @param cb 回调
|
|
231
243
|
*/
|
|
232
|
-
onChange(cb: (field:
|
|
233
|
-
this.modelChangeCallback.add(cb)
|
|
244
|
+
onChange(cb: (field: AllKeys<Fields>, val: any) => void): void {
|
|
245
|
+
this.modelChangeCallback.add(cb as any)
|
|
234
246
|
}
|
|
235
247
|
|
|
236
248
|
/** 关闭监听值变更 */
|
|
237
|
-
offChange(cb: (field:
|
|
238
|
-
this.modelChangeCallback.delete(cb)
|
|
249
|
+
offChange(cb: (field: AllKeys<Fields>, val: any) => void): void {
|
|
250
|
+
this.modelChangeCallback.delete(cb as any)
|
|
239
251
|
}
|
|
240
252
|
}
|
|
@@ -42,9 +42,7 @@ import type { DynamicFormModel } from './dynamic-form-model'
|
|
|
42
42
|
import type { FormModel } from './form-model'
|
|
43
43
|
import { useNodeInterceptor } from './use-node-interceptor'
|
|
44
44
|
|
|
45
|
-
defineOptions({
|
|
46
|
-
name: 'Form'
|
|
47
|
-
})
|
|
45
|
+
defineOptions({ name: 'Form' })
|
|
48
46
|
|
|
49
47
|
const props = defineProps<FormProps<Model>>()
|
|
50
48
|
|
|
@@ -59,13 +57,7 @@ defineSlots<{
|
|
|
59
57
|
|
|
60
58
|
const cls = bem('form')
|
|
61
59
|
|
|
62
|
-
const breakpointCols: BreakCols = {
|
|
63
|
-
xs: 1,
|
|
64
|
-
md: 2,
|
|
65
|
-
lg: 3,
|
|
66
|
-
xl: 4,
|
|
67
|
-
default: 4
|
|
68
|
-
}
|
|
60
|
+
const breakpointCols: BreakCols = { xs: 1, md: 2, lg: 3, xl: 4, default: 4 }
|
|
69
61
|
|
|
70
62
|
provideFormContext(props)
|
|
71
63
|
|
|
@@ -89,7 +81,5 @@ function showInitialNode(field: string) {
|
|
|
89
81
|
|
|
90
82
|
const gridRef = shallowRef<GridExposed>()
|
|
91
83
|
|
|
92
|
-
defineExpose<_FormExposed>({
|
|
93
|
-
el: toRef(() => gridRef.value?.el)
|
|
94
|
-
})
|
|
84
|
+
defineExpose<_FormExposed>({ el: toRef(() => gridRef.value?.el) })
|
|
95
85
|
</script>
|
|
@@ -12,3 +12,12 @@ export function formField<Val = unknown>(item?: FormModelItem<Val>): FormModelIt
|
|
|
12
12
|
|
|
13
13
|
return item
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
export interface NestedFieldMarker<T extends Record<string, any> = Record<string, any>> {
|
|
17
|
+
__isNested: true
|
|
18
|
+
fields: T
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function nestField<T extends Record<string, any>>(fields: T): NestedFieldMarker<T> {
|
|
22
|
+
return { __isNested: true, fields }
|
|
23
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { default as UForm } from './form.vue'
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export { formField, nestField } from './helper'
|
|
4
|
+
export type { NestedFieldMarker } from './helper'
|
|
4
5
|
|
|
5
6
|
export { FormModel } from './form-model'
|
|
6
7
|
export { DynamicFormModel } from './dynamic-form-model'
|
|
@@ -11,13 +11,9 @@ import { computed, inject, type CSSProperties, watch } from 'vue'
|
|
|
11
11
|
import type { GridItemProps } from '../../types'
|
|
12
12
|
import { GridDIKey } from './di'
|
|
13
13
|
|
|
14
|
-
defineOptions({
|
|
15
|
-
name: 'GridItem'
|
|
16
|
-
})
|
|
14
|
+
defineOptions({ name: 'GridItem' })
|
|
17
15
|
|
|
18
|
-
const props = withDefaults(defineProps<GridItemProps>(), {
|
|
19
|
-
tag: 'div'
|
|
20
|
-
})
|
|
16
|
+
const props = withDefaults(defineProps<GridItemProps>(), { tag: 'div' })
|
|
21
17
|
|
|
22
18
|
const cls = bem('grid-item')
|
|
23
19
|
|
|
@@ -41,9 +37,7 @@ const span = computed<number | 'full'>(() => {
|
|
|
41
37
|
})
|
|
42
38
|
|
|
43
39
|
const style = computed<CSSProperties>(() => {
|
|
44
|
-
return {
|
|
45
|
-
gridColumn: span.value === 'full' ? '1 / -1' : `span ${span.value}`
|
|
46
|
-
}
|
|
40
|
+
return { gridColumn: span.value === 'full' ? '1 / -1' : `span ${span.value}` }
|
|
47
41
|
})
|
|
48
42
|
|
|
49
43
|
watch(
|
|
@@ -13,13 +13,9 @@ import { getBreakpointCols } from './breakpoint'
|
|
|
13
13
|
import { GridDIKey } from './di'
|
|
14
14
|
import { useResponsive } from './use-responsive'
|
|
15
15
|
|
|
16
|
-
defineOptions({
|
|
17
|
-
name: 'Grid'
|
|
18
|
-
})
|
|
16
|
+
defineOptions({ name: 'Grid' })
|
|
19
17
|
|
|
20
|
-
const props = withDefaults(defineProps<GridProps>(), {
|
|
21
|
-
tag: 'div'
|
|
22
|
-
})
|
|
18
|
+
const props = withDefaults(defineProps<GridProps>(), { tag: 'div' })
|
|
23
19
|
|
|
24
20
|
const emit = defineEmits<GridEmits>()
|
|
25
21
|
|
|
@@ -27,11 +23,7 @@ const cls = bem('grid')
|
|
|
27
23
|
|
|
28
24
|
const gridRef = useTemplateRef<HTMLElement>('grid')
|
|
29
25
|
|
|
30
|
-
const { currentBreakpoint, gridItemsProps } = useResponsive({
|
|
31
|
-
props,
|
|
32
|
-
emit,
|
|
33
|
-
gridRef
|
|
34
|
-
})
|
|
26
|
+
const { currentBreakpoint, gridItemsProps } = useResponsive({ props, emit, gridRef })
|
|
35
27
|
|
|
36
28
|
const styles = computed(() => {
|
|
37
29
|
const { gap, cols } = props
|
|
@@ -77,12 +69,7 @@ watch([styles, gridRef], ([styles, dom]) => {
|
|
|
77
69
|
setStyles(dom, styles)
|
|
78
70
|
})
|
|
79
71
|
|
|
80
|
-
provide(GridDIKey, {
|
|
81
|
-
currentBreakpoint,
|
|
82
|
-
gridItemsProps
|
|
83
|
-
})
|
|
72
|
+
provide(GridDIKey, { currentBreakpoint, gridItemsProps })
|
|
84
73
|
|
|
85
|
-
defineExpose<_GridExposed>({
|
|
86
|
-
el: gridRef
|
|
87
|
-
})
|
|
74
|
+
defineExpose<_GridExposed>({ el: gridRef })
|
|
88
75
|
</script>
|
|
@@ -26,9 +26,7 @@ import { computed, ref, getCurrentInstance } from 'vue'
|
|
|
26
26
|
|
|
27
27
|
import type { GridInputProps, GridInputEmits, GridInputExposed } from '../../types'
|
|
28
28
|
|
|
29
|
-
defineOptions({
|
|
30
|
-
name: 'GridInput'
|
|
31
|
-
})
|
|
29
|
+
defineOptions({ name: 'GridInput' })
|
|
32
30
|
|
|
33
31
|
const emit = defineEmits<GridInputEmits>()
|
|
34
32
|
|
|
@@ -38,9 +36,7 @@ const props = withDefaults(defineProps<GridInputProps>(), {
|
|
|
38
36
|
separator: '-'
|
|
39
37
|
})
|
|
40
38
|
|
|
41
|
-
const { size } = useFallbackProps([props], {
|
|
42
|
-
size: 'default'
|
|
43
|
-
})
|
|
39
|
+
const { size } = useFallbackProps([props], { size: 'default' })
|
|
44
40
|
|
|
45
41
|
const cls = bem('grid-input')
|
|
46
42
|
|
|
@@ -51,9 +51,7 @@ import type { GroupInputEmits, GroupInputProps } from '../../types'
|
|
|
51
51
|
import { UButton } from '../button'
|
|
52
52
|
import { useGroupItems } from './use-group-items'
|
|
53
53
|
|
|
54
|
-
defineOptions({
|
|
55
|
-
name: 'GroupInput'
|
|
56
|
-
})
|
|
54
|
+
defineOptions({ name: 'GroupInput' })
|
|
57
55
|
|
|
58
56
|
const props = withDefaults(defineProps<GroupInputProps<GroupItem>>(), {
|
|
59
57
|
modelValue: () => [],
|
|
@@ -73,10 +71,7 @@ const { disabled, size, readonly } = useFormFallbackProps([formProps ?? {}, prop
|
|
|
73
71
|
readonly: false
|
|
74
72
|
})
|
|
75
73
|
|
|
76
|
-
const { items, createItem, runByEvent } = useGroupItems({
|
|
77
|
-
props,
|
|
78
|
-
emit
|
|
79
|
-
})
|
|
74
|
+
const { items, createItem, runByEvent } = useGroupItems({ props, emit })
|
|
80
75
|
|
|
81
76
|
const isEmpty = computed(() => {
|
|
82
77
|
return !items.value.length
|
|
@@ -10,17 +10,13 @@ import { type CSSProperties, computed } from 'vue'
|
|
|
10
10
|
|
|
11
11
|
import type { IconProps } from '../../types'
|
|
12
12
|
|
|
13
|
-
defineOptions({
|
|
14
|
-
name: 'Icon'
|
|
15
|
-
})
|
|
13
|
+
defineOptions({ name: 'Icon' })
|
|
16
14
|
|
|
17
15
|
const props = defineProps<IconProps>()
|
|
18
16
|
|
|
19
17
|
const cls = bem('icon')
|
|
20
18
|
|
|
21
19
|
const style = computed<CSSProperties>(() => {
|
|
22
|
-
return {
|
|
23
|
-
fontSize: withUnit(props.size, 'px')
|
|
24
|
-
}
|
|
20
|
+
return { fontSize: withUnit(props.size, 'px') }
|
|
25
21
|
})
|
|
26
22
|
</script>
|