@veltra/desktop 1.1.35 → 1.2.0

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 (198) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/auto-complete/auto-complete.js +1 -0
  4. package/dist/components/auto-complete/auto-complete.js.map +1 -1
  5. package/dist/components/batch-edit/batch-edit-form.js +12 -5
  6. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  7. package/dist/components/batch-edit/batch-edit.js +8 -3
  8. package/dist/components/batch-edit/batch-edit.js.map +1 -1
  9. package/dist/components/batch-edit/batch-edit.vue.d.ts +40 -35
  10. package/dist/components/batch-edit/di.js.map +1 -1
  11. package/dist/components/batch-edit/use-edit.js +41 -23
  12. package/dist/components/batch-edit/use-edit.js.map +1 -1
  13. package/dist/components/cascade/cascade.js +1 -0
  14. package/dist/components/cascade/cascade.js.map +1 -1
  15. package/dist/components/cascade/cascade.vue.d.ts +5 -5
  16. package/dist/components/checkbox/checkbox-button.js +1 -0
  17. package/dist/components/checkbox/checkbox-button.js.map +1 -1
  18. package/dist/components/checkbox/checkbox-button.vue.d.ts +1 -1
  19. package/dist/components/checkbox/checkbox.js +1 -0
  20. package/dist/components/checkbox/checkbox.js.map +1 -1
  21. package/dist/components/checkbox-group/checkbox-group.js +1 -0
  22. package/dist/components/checkbox-group/checkbox-group.js.map +1 -1
  23. package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
  24. package/dist/components/code-editor/code-editor.js +1 -0
  25. package/dist/components/code-editor/code-editor.js.map +1 -1
  26. package/dist/components/condition-editor/core/evaluator.js +3 -3
  27. package/dist/components/condition-editor/core/evaluator.js.map +1 -1
  28. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  29. package/dist/components/date-picker/date-picker.js +1 -0
  30. package/dist/components/date-picker/date-picker.js.map +1 -1
  31. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  32. package/dist/components/date-range-picker/date-range-picker.js +2 -0
  33. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  34. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  35. package/dist/components/dialog/use-maximum.js +7 -1
  36. package/dist/components/dialog/use-maximum.js.map +1 -1
  37. package/dist/components/empty/empty.vue.d.ts +1 -1
  38. package/dist/components/expression-editor/expression-editor.js +1 -0
  39. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  40. package/dist/components/file-picker/file-picker.js +1 -0
  41. package/dist/components/file-picker/file-picker.js.map +1 -1
  42. package/dist/components/form/form.js.map +1 -1
  43. package/dist/components/form/form.vue.d.ts +11 -19
  44. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +24 -26
  45. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
  46. package/dist/components/form/index.d.ts +0 -3
  47. package/dist/components/form/use-form-fields.js +88 -0
  48. package/dist/components/form/use-form-fields.js.map +1 -0
  49. package/dist/components/form/use-node-interceptor.js +19 -30
  50. package/dist/components/form/use-node-interceptor.js.map +1 -1
  51. package/dist/components/form-item/form-item.js.map +1 -1
  52. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +44 -18
  53. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/form-item/helper.js +4 -1
  55. package/dist/components/form-item/helper.js.map +1 -1
  56. package/dist/components/form-item/validate.d.ts +26 -0
  57. package/dist/components/form-item/validate.js +97 -0
  58. package/dist/components/form-item/validate.js.map +1 -0
  59. package/dist/components/group-input/group-input.js +1 -0
  60. package/dist/components/group-input/group-input.js.map +1 -1
  61. package/dist/components/index.d.ts +0 -3
  62. package/dist/components/index.js +0 -7
  63. package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js +1 -0
  64. package/dist/components/input/input.vue_vue_type_script_setup_true_lang.js.map +1 -1
  65. package/dist/components/loading/style2.css +90 -159
  66. package/dist/components/multi-select/multi-select.js +1 -0
  67. package/dist/components/multi-select/multi-select.js.map +1 -1
  68. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  69. package/dist/components/multi-tree-select/multi-tree-select.js +1 -0
  70. package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
  71. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
  72. package/dist/components/number-input/number-input.js +1 -0
  73. package/dist/components/number-input/number-input.js.map +1 -1
  74. package/dist/components/number-range-input/number-range-input.js +1 -0
  75. package/dist/components/number-range-input/number-range-input.js.map +1 -1
  76. package/dist/components/palette/palette.js +1 -0
  77. package/dist/components/palette/palette.js.map +1 -1
  78. package/dist/components/password-input/password-input.js +1 -0
  79. package/dist/components/password-input/password-input.js.map +1 -1
  80. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  81. package/dist/components/radio/radio.js +1 -0
  82. package/dist/components/radio/radio.js.map +1 -1
  83. package/dist/components/radio-group/radio-group.js +1 -0
  84. package/dist/components/radio-group/radio-group.js.map +1 -1
  85. package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
  86. package/dist/components/rich-text-editor/rich-text-editor.js +3 -2
  87. package/dist/components/rich-text-editor/rich-text-editor.js.map +1 -1
  88. package/dist/components/rich-text-editor/toolbar.js +10 -7
  89. package/dist/components/rich-text-editor/toolbar.js.map +1 -1
  90. package/dist/components/select/select.js +6 -3
  91. package/dist/components/select/select.js.map +1 -1
  92. package/dist/components/select/select.vue.d.ts +2 -2
  93. package/dist/components/slider/slider.js +15 -2
  94. package/dist/components/slider/slider.js.map +1 -1
  95. package/dist/components/steps/steps.vue.d.ts +1 -1
  96. package/dist/components/switch/switch.js +1 -0
  97. package/dist/components/switch/switch.js.map +1 -1
  98. package/dist/components/table/style2.css +4 -7
  99. package/dist/components/textarea/textarea.js +1 -0
  100. package/dist/components/textarea/textarea.js.map +1 -1
  101. package/dist/components/tip/tip.vue.d.ts +1 -1
  102. package/dist/components/tree-select/tree-select.js +1 -0
  103. package/dist/components/tree-select/tree-select.js.map +1 -1
  104. package/dist/components/tree-select/tree-select.vue.d.ts +4 -4
  105. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  106. package/dist/index.d.ts +5 -6
  107. package/dist/index.js +1 -4
  108. package/dist/types/auto-complete.d.ts +3 -2
  109. package/dist/types/batch-edit.d.ts +4 -5
  110. package/dist/types/cascade.d.ts +3 -2
  111. package/dist/types/checkbox-group.d.ts +2 -2
  112. package/dist/types/checkbox.d.ts +4 -3
  113. package/dist/types/code-editor.d.ts +3 -2
  114. package/dist/types/date-picker.d.ts +3 -2
  115. package/dist/types/date-range-picker.d.ts +3 -2
  116. package/dist/types/expression-editor.d.ts +3 -2
  117. package/dist/types/file-picker.d.ts +3 -2
  118. package/dist/types/form-field.d.ts +12 -0
  119. package/dist/types/form-item.d.ts +2 -2
  120. package/dist/types/form.d.ts +9 -48
  121. package/dist/types/group-input.d.ts +3 -2
  122. package/dist/types/index.d.ts +4 -2
  123. package/dist/types/input.d.ts +3 -2
  124. package/dist/types/loading.d.ts +1 -1
  125. package/dist/types/multi-select.d.ts +3 -2
  126. package/dist/types/multi-tree-select.d.ts +3 -2
  127. package/dist/types/palette.d.ts +3 -2
  128. package/dist/types/radio-group.d.ts +3 -2
  129. package/dist/types/radio.d.ts +3 -2
  130. package/dist/types/rich-text-editor.d.ts +3 -2
  131. package/dist/types/select.d.ts +3 -2
  132. package/dist/types/slider.d.ts +3 -2
  133. package/dist/types/switch.d.ts +3 -2
  134. package/dist/types/text-editor.d.ts +3 -2
  135. package/dist/types/textarea.d.ts +3 -2
  136. package/dist/types/tree-select.d.ts +3 -2
  137. package/package.json +6 -6
  138. package/src/components/batch-edit/batch-edit-form.vue +20 -13
  139. package/src/components/batch-edit/batch-edit.vue +7 -8
  140. package/src/components/batch-edit/di.ts +1 -2
  141. package/src/components/batch-edit/use-edit.ts +58 -36
  142. package/src/components/date-range-picker/date-range-picker.vue +2 -0
  143. package/src/components/dialog/use-maximum.ts +8 -1
  144. package/src/components/form/form.vue +22 -38
  145. package/src/components/form/index.ts +0 -6
  146. package/src/components/form/use-form-fields.ts +126 -0
  147. package/src/components/form/use-node-interceptor.ts +27 -56
  148. package/src/components/form-item/form-item.vue +69 -18
  149. package/src/components/form-item/helper.ts +10 -4
  150. package/src/components/form-item/validate.ts +155 -0
  151. package/src/components/loading/style.scss +126 -355
  152. package/src/components/rich-text-editor/rich-text-editor.vue +28 -25
  153. package/src/components/rich-text-editor/toolbar.vue +23 -19
  154. package/src/components/select/select.vue +6 -4
  155. package/src/components/slider/slider.vue +12 -2
  156. package/src/components/table/style.scss +4 -14
  157. package/src/types/auto-complete.ts +4 -2
  158. package/src/types/batch-edit.ts +4 -5
  159. package/src/types/cascade.ts +4 -2
  160. package/src/types/checkbox-group.ts +2 -2
  161. package/src/types/checkbox.ts +5 -3
  162. package/src/types/code-editor.ts +4 -2
  163. package/src/types/date-picker.ts +4 -2
  164. package/src/types/date-range-picker.ts +4 -2
  165. package/src/types/expression-editor.ts +4 -2
  166. package/src/types/file-picker.ts +4 -2
  167. package/src/types/form-field.ts +11 -0
  168. package/src/types/form-item.ts +2 -2
  169. package/src/types/form.ts +9 -82
  170. package/src/types/group-input.ts +4 -2
  171. package/src/types/index.ts +1 -0
  172. package/src/types/input.ts +4 -2
  173. package/src/types/loading.ts +1 -1
  174. package/src/types/multi-select.ts +4 -2
  175. package/src/types/multi-tree-select.ts +5 -2
  176. package/src/types/palette.ts +4 -2
  177. package/src/types/radio-group.ts +4 -2
  178. package/src/types/radio.ts +4 -2
  179. package/src/types/rich-text-editor.ts +4 -2
  180. package/src/types/select.ts +4 -2
  181. package/src/types/slider.ts +4 -2
  182. package/src/types/switch.ts +4 -2
  183. package/src/types/text-editor.ts +4 -2
  184. package/src/types/textarea.ts +4 -2
  185. package/src/types/tree-select.ts +5 -2
  186. package/dist/components/form/dynamic-form-model.d.ts +0 -73
  187. package/dist/components/form/dynamic-form-model.js +0 -184
  188. package/dist/components/form/dynamic-form-model.js.map +0 -1
  189. package/dist/components/form/form-model.d.ts +0 -73
  190. package/dist/components/form/form-model.js +0 -179
  191. package/dist/components/form/form-model.js.map +0 -1
  192. package/dist/components/form/helper.d.ts +0 -16
  193. package/dist/components/form/helper.js +0 -20
  194. package/dist/components/form/helper.js.map +0 -1
  195. package/src/components/form/__test__/form-model.test.ts +0 -190
  196. package/src/components/form/dynamic-form-model.ts +0 -258
  197. package/src/components/form/form-model.ts +0 -255
  198. package/src/components/form/helper.ts +0 -23
@@ -1,190 +0,0 @@
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
- })
@@ -1,258 +0,0 @@
1
- import { o } from '@cat-kit/core'
2
- import { Validator } from '@veltra/utils'
3
- import { isReactive, reactive, shallowReactive, watch } from 'vue'
4
-
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
- }
20
-
21
- /**
22
- * 动态表单模型
23
- */
24
- export class DynamicFormModel {
25
- /** 表单数据 */
26
- private _data: Record<string, any> = reactive({})
27
-
28
- set data(v: Record<string, any>) {
29
- if (!isReactive(v)) {
30
- console.error(`data不是一个响应式对象`)
31
- return
32
- }
33
- this._data = v
34
- this.watchData()
35
- }
36
-
37
- get data() {
38
- return this._data
39
- }
40
-
41
- /** 表单规则 */
42
- readonly fields: Record<string, FormModelField> = shallowReactive(
43
- {} as Record<string, FormModelField>
44
- )
45
-
46
- private flatFields: Record<string, FormModelItem>
47
-
48
- private validator: Validator
49
-
50
- /**
51
- * 不同表单对应的key
52
- */
53
- formKeys: Map<number, string[]> = new Map()
54
-
55
- readonly errors: Map<string, string[] | undefined> = shallowReactive(new Map())
56
-
57
- /**
58
- * 是否在表单值更新时校验
59
- */
60
- private validateOnFieldChange = true
61
-
62
- private modelChangeCallback: Set<(fields: string, val: any) => void> = new Set()
63
-
64
- private _allKeys: string[] = []
65
-
66
- /** 所有的键 */
67
- get allKeys(): string[] {
68
- return this._allKeys
69
- }
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
-
80
- private getAllKeys(): void {
81
- this._allKeys = Object.keys(this.flatFields)
82
- }
83
-
84
- /** 初始数据 */
85
- readonly initialData: Record<string, any> = {}
86
-
87
- /**
88
- * 变更前的值
89
- */
90
- private readonly oldData: Record<string, any> = {}
91
-
92
- constructor(fields?: Record<string, FormModelField>) {
93
- this.flatFields = shallowReactive({} as Record<string, FormModelItem>)
94
- this.validator = new Validator(this.flatFields)
95
- this.append(fields ?? {})
96
- this.watchData()
97
- }
98
-
99
- private watchData(): void {
100
- const { oldData } = this
101
- watch(this.data, (data) => {
102
- let changedKeys: string[] = []
103
-
104
- this.allKeys.forEach((key) => {
105
- const oldVal = o(oldData).get(key)
106
- const newVal = o(data).get(key)
107
- if (oldVal !== newVal) {
108
- this.modelChangeCallback.forEach((cb) => cb(key, newVal))
109
- changedKeys.push(key)
110
- o(oldData).set(key, newVal)
111
- }
112
- })
113
-
114
- if (this.validateOnFieldChange) {
115
- this.validate(changedKeys)
116
- } else {
117
- this.validateOnFieldChange = true
118
- }
119
- })
120
- }
121
-
122
- append(fields: Record<string, FormModelField>): void {
123
- for (const key in fields) {
124
- this.add(key, fields[key]!)
125
- }
126
- }
127
-
128
- /**
129
- * 添加字段
130
- * @param field 字段
131
- * @param item 字段配置项
132
- */
133
- add(field: string, item: FormModelField): void {
134
- this.fields[field] = item
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)
145
- }
146
- this.updateFlatFields()
147
- }
148
-
149
- /**
150
- * 移除字段
151
- * @param field 字段
152
- */
153
- delete(field: string): void {
154
- delete this.fields[field]
155
- this.updateFlatFields()
156
- }
157
-
158
- /**
159
- * 监听值变更
160
- * @param cb 回调
161
- */
162
- onChange(cb: (field: string, val: any) => void): void {
163
- this.modelChangeCallback.add(cb)
164
- }
165
-
166
- offChange(cb: (field: string, val: any) => void): void {
167
- this.modelChangeCallback.delete(cb)
168
- }
169
-
170
- async validate(fields?: string | string[]): Promise<boolean> {
171
- const { errors, validator, data } = this
172
-
173
- const results = await validator.validate(data, this.getValidateFields(fields))
174
-
175
- // 设置错误提示
176
- if (!fields) {
177
- errors.clear()
178
- for (const field in results) {
179
- errors.set(field, results[field])
180
- }
181
- } else {
182
- ~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {
183
- const errs = results[field]
184
- if (errs?.length) {
185
- errors.set(field, errs)
186
- } else {
187
- errors.delete(field)
188
- }
189
- })
190
- }
191
-
192
- return errors.size === 0
193
- }
194
-
195
- private getValidateFields(fields?: string | string[]) {
196
- if (!fields) {
197
- if (this.formKeys.size) {
198
- let _fields: string[] = []
199
- this.formKeys.forEach((fields) => {
200
- _fields = _fields.concat(fields)
201
- })
202
- return _fields
203
- } else {
204
- return this.allKeys
205
- }
206
- }
207
-
208
- if (!Array.isArray(fields)) {
209
- return [fields]
210
- }
211
- return fields
212
- }
213
-
214
- /**
215
- * 重置数据
216
- * @param fields 需要重置的字段
217
- */
218
- resetData(keys?: string | string[]): void {
219
- if (typeof keys === 'string') {
220
- keys = [keys]
221
- } else if (Array.isArray(keys)) {
222
- } else {
223
- keys = this.allKeys
224
- }
225
-
226
- this.clearValidate()
227
-
228
- this.validateOnFieldChange = false
229
-
230
- keys.forEach((field) => {
231
- o(this.data).set(field as string, o(this.initialData).get(field as string))
232
- })
233
- }
234
-
235
- /**
236
- * 设置值
237
- * @param formData 表单值
238
- * @param options 配置
239
- */
240
- setData(formData: Record<string, any>, config?: DataSettingConfig): void {
241
- const { validate = true } = config || {}
242
-
243
- if (!validate) {
244
- this.validateOnFieldChange = false
245
- }
246
-
247
- this.allKeys.forEach((key) => {
248
- const value = o(formData).get(key)
249
- if (value !== undefined) {
250
- o(this.data).set(key, value)
251
- }
252
- })
253
- }
254
-
255
- clearValidate(): void {
256
- this.errors.clear()
257
- }
258
- }
@@ -1,255 +0,0 @@
1
- import { o } from '@cat-kit/core'
2
- import { middleProxy, Validator } from '@veltra/utils'
3
- import { nextTick, reactive, shallowReactive, watch, type Reactive } from 'vue'
4
-
5
- import type {
6
- ModelData,
7
- DataSettingConfig,
8
- IFormModel,
9
- FormModelItem,
10
- FormModelField
11
- } from '../../types'
12
-
13
- /**
14
- * 递归展平字段逻辑
15
- */
16
- function flattenFields(fields: Record<string, any>, prefix = ''): Record<string, FormModelItem> {
17
- const result: Record<string, FormModelItem> = {}
18
- for (const key in fields) {
19
- const item = fields[key]
20
- const fullPath = prefix ? `${prefix}.${key}` : key
21
- if (item && typeof item === 'object' && '__isNested' in item && item.__isNested) {
22
- Object.assign(result, flattenFields((item as any).fields, fullPath))
23
- } else {
24
- result[fullPath] = item
25
- }
26
- }
27
- return result
28
- }
29
-
30
- /**
31
- * 表单模型
32
- */
33
- export class FormModel<
34
- Fields extends Record<string, FormModelField> = Record<string, FormModelField>
35
- > implements IFormModel<Fields> {
36
- /** 表单数据 */
37
- data!: ModelData<Fields>
38
-
39
- /** 表单规则 */
40
- readonly fields: Fields
41
-
42
- /** 所有的键 */
43
- readonly allKeys: string[]
44
-
45
- /**
46
- * 不同表单所需要校验的字段
47
- * @description
48
- * 这个值会在表单组件渲染时由表单设置,因为只有真正渲染的组件才应该被校验
49
- */
50
- formKeys: Map<number, string[]> = new Map()
51
-
52
- /** 初始数据 */
53
- readonly initialData: ModelData<Fields>
54
-
55
- readonly errors: Map<string, string[] | undefined> = shallowReactive(new Map())
56
-
57
- private validator: Validator<Record<string, FormModelItem>>
58
-
59
- /**
60
- * 是否在表单值更新时校验
61
- */
62
- private validateOnFieldChange = true
63
-
64
- private modelChangeCallback: Set<(fields: string, val: any) => void> = new Set()
65
-
66
- constructor(fields: Fields) {
67
- this.fields = fields
68
- const flatFields = flattenFields(fields)
69
- const rawData = {} as ModelData<Fields>
70
- const allKeys: string[] = []
71
-
72
- for (const key in flatFields) {
73
- const fieldItem = flatFields[key]!
74
- allKeys.push(key)
75
- const { value } = fieldItem
76
- let v = value
77
-
78
- if (typeof value === 'function') {
79
- watch(value, (v) => {
80
- o(this.initialData).set(key, v)
81
- })
82
- v = value()
83
- }
84
-
85
- o(rawData).set(key, v)
86
- }
87
-
88
- this.allKeys = allKeys
89
- this.initialData = JSON.parse(JSON.stringify(rawData))
90
-
91
- this.setProxyData(reactive(rawData))
92
-
93
- this.validator = new Validator(flatFields)
94
-
95
- return shallowReactive(this)
96
- }
97
-
98
- /**
99
- * 设置响应式值, 替换掉原有的数据
100
- * @description 设置响应式值时,会自动监听值的变化,并进行校验
101
- * @param proxyData 响应式的值
102
- */
103
- setProxyData(proxyData: ModelData<Fields> | Reactive<ModelData<Fields>>): void {
104
- const data = middleProxy(proxyData, {
105
- set: (field, val) => {
106
- this.modelChangeCallback.forEach((cb) => cb(field, val))
107
- },
108
-
109
- changed: (fields) => {
110
- if (!this.validateOnFieldChange) {
111
- this.validateOnFieldChange = true
112
- return
113
- }
114
- this.validate(fields as any)
115
- }
116
- })
117
-
118
- this.data = data as ModelData<Fields>
119
- }
120
-
121
- private getValidateFields(fields?: string | string[]) {
122
- if (!fields) {
123
- if (this.formKeys.size) {
124
- let _fields: string[] = []
125
- this.formKeys.forEach((keys) => {
126
- _fields = _fields.concat(keys)
127
- })
128
- return _fields
129
- } else {
130
- return this.allKeys
131
- }
132
- }
133
-
134
- if (!Array.isArray(fields)) {
135
- return [fields]
136
- }
137
- return fields
138
- }
139
-
140
- /**
141
- * 模型校验
142
- * @param fields 需要校验的字段, 不传则校验所有字段
143
- * @returns
144
- */
145
- async validate(fields?: string | string[]): Promise<boolean> {
146
- const { errors, validator, data } = this
147
-
148
- const results = await validator.validate(data, this.getValidateFields(fields))
149
-
150
- // 全量校验
151
- if (!fields) {
152
- errors.clear()
153
-
154
- for (const field in results) {
155
- errors.set(field as any, results[field])
156
- }
157
- }
158
- // 局部校验
159
- else {
160
- ~(Array.isArray(fields) ? fields : [fields]).forEach((field) => {
161
- const errs = results[field as string]
162
- if (errs?.length) {
163
- errors.set(field, errs)
164
- } else {
165
- errors.delete(field)
166
- }
167
- })
168
- }
169
-
170
- if (errors.size > 0) {
171
- if (!fields) {
172
- nextTick(() => {
173
- document.querySelector('.u-form-item.is-error')?.scrollIntoView({ block: 'nearest' })
174
- })
175
- }
176
- return Promise.reject(false)
177
- }
178
-
179
- return true
180
- }
181
-
182
- /**
183
- * 重置数据
184
- * @param fields 需要重置的字段
185
- */
186
- resetData(keys?: string | string[]): void {
187
- if (typeof keys === 'string') {
188
- keys = [keys]
189
- } else if (Array.isArray(keys)) {
190
- } else {
191
- keys = this.allKeys
192
- }
193
-
194
- this.clearValidate()
195
-
196
- this.validateOnFieldChange = false
197
-
198
- keys.forEach((field) => {
199
- o(this.data).set(field as string, o(this.initialData).get(field as string))
200
- })
201
- }
202
-
203
- /**
204
- * 设置值
205
- * @param formData 表单值
206
- * @param options 配置
207
- */
208
- setData(formData: Partial<Record<string, any>>, config?: DataSettingConfig): FormModel<Fields> {
209
- const { validate = true } = config || {}
210
-
211
- if (!validate) {
212
- this.validateOnFieldChange = false
213
- }
214
-
215
- this.allKeys.forEach((key) => {
216
- const value = o(formData).get(key)
217
- if (value !== undefined) {
218
- o(this.data).set(key, value)
219
- }
220
- })
221
-
222
- return this
223
- }
224
-
225
- /**
226
- * 设置初始值
227
- * @description 初始值在重置数据和查看原始数据时会用到
228
- * @param data 初始值
229
- */
230
- setInitialData(data: Partial<ModelData<Fields>>): FormModel<Fields> {
231
- this.allKeys.forEach((key) => {
232
- o(this.initialData).set(key, o(data).get(key))
233
- })
234
-
235
- return this
236
- }
237
-
238
- /** 清除校验 */
239
- clearValidate(): void {
240
- this.errors.clear()
241
- }
242
-
243
- /**
244
- * 监听值变更
245
- * @param cb 回调
246
- */
247
- onChange(cb: (field: string, val: any) => void): void {
248
- this.modelChangeCallback.add(cb)
249
- }
250
-
251
- /** 关闭监听值变更 */
252
- offChange(cb: (field: string, val: any) => void): void {
253
- this.modelChangeCallback.delete(cb)
254
- }
255
- }