hy-app 0.5.11 → 0.5.12

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.
@@ -1,220 +1,220 @@
1
- <template>
2
- <view class="hy-form">
3
- <slot></slot>
4
- </view>
5
- </template>
6
-
7
- <script lang="ts">
8
- export default {
9
- name: 'hy-form',
10
- options: {
11
- addGlobalClass: true,
12
- virtualHost: true,
13
- styleIsolation: 'shared'
14
- }
15
- }
16
- </script>
17
-
18
- <script setup lang="ts">
19
- import { provide, reactive, ref, toRefs } from 'vue'
20
- import { clearVal, isArray } from '../../libs'
21
- import formProps from './props'
22
- import type { IFormEmits } from './typing'
23
-
24
- /**
25
- * 表单组件父组件,需要搭配hy-form-item
26
- * @displayName hy-form
27
- */
28
- defineOptions({})
29
-
30
- const props = defineProps(formProps)
31
- const emit = defineEmits<IFormEmits>()
32
-
33
- // 表单数据
34
- const formData = reactive(props.model || {})
35
- const formItems = ref<any[]>([])
36
- const errors = reactive<Record<string, string>>({})
37
-
38
- // 表单上下文
39
- const formContext = {
40
- ...toRefs(props),
41
- formData,
42
- errors,
43
- addFormItem: (item: any) => {
44
- formItems.value.push(item)
45
- },
46
- removeFormItem: (item: any) => {
47
- const index = formItems.value.indexOf(item)
48
- if (index > -1) {
49
- formItems.value.splice(index, 1)
50
- }
51
- },
52
- validateField: (field: string, value: any, trigger?: 'blur' | 'change') => {
53
- const fieldRules = props.rules?.[field]
54
- if (!fieldRules) return true
55
-
56
- const rules = isArray(fieldRules) ? fieldRules : [fieldRules]
57
- let isValid = true
58
- let errorMessage = ''
59
-
60
- for (const rule of rules) {
61
- // 检查触发时机
62
- if (
63
- (trigger && !rule.trigger) ||
64
- (trigger && rule.trigger && !rule.trigger.includes(trigger))
65
- ) {
66
- continue
67
- }
68
-
69
- // 必填校验
70
- if (rule.required && (!value || value === '')) {
71
- errorMessage = rule.message || `${field} 是必填项`
72
- isValid = false
73
- break
74
- }
75
-
76
- // 长度校验
77
- if (rule.min && String(value).length < rule.min) {
78
- errorMessage = rule.message || `${field} 长度不能少于 ${rule.min} 个字符`
79
- isValid = false
80
- break
81
- }
82
-
83
- if (rule.max && String(value).length > rule.max) {
84
- errorMessage = rule.message || `${field} 长度不能超过 ${rule.max} 个字符`
85
- isValid = false
86
- break
87
- }
88
-
89
- // 类型校验
90
- if (rule.type === 'phone') {
91
- const phoneRegex = /^1[3-9]\d{9}$/
92
- if (!phoneRegex.test(String(value))) {
93
- errorMessage = rule.message || '请输入正确的手机号'
94
- isValid = false
95
- break
96
- }
97
- }
98
-
99
- if (rule.type === 'email') {
100
- const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/
101
- if (!emailRegex.test(String(value))) {
102
- errorMessage = rule.message || '请输入正确的邮箱格式'
103
- isValid = false
104
- break
105
- }
106
- }
107
-
108
- if (rule.type === 'password') {
109
- const passwordRegex =
110
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/
111
- if (!passwordRegex.test(String(value))) {
112
- errorMessage = rule.message || '密码至少8个字符,包含大小写字母、数字和特殊符号'
113
- isValid = false
114
- break
115
- }
116
- }
117
-
118
- // 自定义校验
119
- if (rule.validator) {
120
- const result = rule.validator(value)
121
- if (result === false || typeof result === 'string') {
122
- errorMessage =
123
- typeof result === 'string' ? result : rule.message || `${field} 校验失败`
124
- isValid = false
125
- break
126
- }
127
- }
128
- }
129
-
130
- if (isValid) {
131
- delete errors[field]
132
- } else {
133
- errors[field] = errorMessage
134
- }
135
-
136
- return isValid
137
- },
138
- setFieldValue: (field: string, value: any) => {
139
- formData[field] = value
140
- },
141
- getFieldValue: (field: string) => {
142
- return formData[field]
143
- }
144
- }
145
-
146
- // 提供表单上下文给子组件
147
- provide('formContext', formContext)
148
-
149
- // 验证所有字段
150
- const validate = () => {
151
- return new Promise((resolve, reject) => {
152
- let isValid = true
153
- const allErrors: Record<string, string> = {}
154
-
155
- formItems.value.forEach((item) => {
156
- const field = item.props?.prop
157
- if (field) {
158
- const value = formData[field]
159
- const fieldValid = formContext.validateField(field, value)
160
- if (!fieldValid) {
161
- isValid = false
162
- allErrors[field] = errors[field]
163
- }
164
- }
165
- })
166
-
167
- emit('validate', isValid, allErrors)
168
- if (isValid) {
169
- resolve(isValid)
170
- } else {
171
- reject(allErrors)
172
- }
173
- })
174
- }
175
-
176
- // 重置表单
177
- const resetFields = () => {
178
- clearVal(formData)
179
- Object.keys(errors).forEach((key) => {
180
- delete errors[key]
181
- })
182
- }
183
-
184
- // 清除验证
185
- const clearValidate = (fields?: string[]) => {
186
- if (fields) {
187
- fields.forEach((field) => {
188
- delete errors[field]
189
- })
190
- } else {
191
- Object.keys(errors).forEach((key) => {
192
- delete errors[key]
193
- })
194
- }
195
- }
196
-
197
- // 提交表单
198
- const submit = async () => {
199
- if (await validate()) {
200
- emit('submit', { ...formData })
201
- return formData
202
- }
203
- return false
204
- }
205
-
206
- defineExpose({
207
- validate,
208
- resetFields,
209
- clearValidate,
210
- submit,
211
- formData,
212
- errors
213
- })
214
- </script>
215
-
216
- <style lang="scss" scoped>
217
- .hy-form {
218
- width: 100%;
219
- }
220
- </style>
1
+ <template>
2
+ <view class="hy-form">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script lang="ts">
8
+ export default {
9
+ name: 'hy-form',
10
+ options: {
11
+ addGlobalClass: true,
12
+ virtualHost: true,
13
+ styleIsolation: 'shared'
14
+ }
15
+ }
16
+ </script>
17
+
18
+ <script setup lang="ts">
19
+ import { provide, reactive, ref, toRefs } from 'vue'
20
+ import { clearVal, isArray } from '../../libs'
21
+ import formProps from './props'
22
+ import type { IFormEmits } from './typing'
23
+
24
+ /**
25
+ * 表单组件父组件,需要搭配hy-form-item
26
+ * @displayName hy-form
27
+ */
28
+ defineOptions({})
29
+
30
+ const props = defineProps(formProps)
31
+ const emit = defineEmits<IFormEmits>()
32
+
33
+ // 表单数据
34
+ const formData = reactive(props.model || {})
35
+ const formItems = ref<any[]>([])
36
+ const errors = reactive<Record<string, string>>({})
37
+
38
+ // 表单上下文
39
+ const formContext = {
40
+ ...toRefs(props),
41
+ formData,
42
+ errors,
43
+ addFormItem: (item: any) => {
44
+ formItems.value.push(item)
45
+ },
46
+ removeFormItem: (item: any) => {
47
+ const index = formItems.value.indexOf(item)
48
+ if (index > -1) {
49
+ formItems.value.splice(index, 1)
50
+ }
51
+ },
52
+ validateField: (field: string, value: any, trigger?: 'blur' | 'change') => {
53
+ const fieldRules = props.rules?.[field]
54
+ if (!fieldRules) return true
55
+
56
+ const rules = isArray(fieldRules) ? fieldRules : [fieldRules]
57
+ let isValid = true
58
+ let errorMessage = ''
59
+
60
+ for (const rule of rules) {
61
+ // 检查触发时机
62
+ if (
63
+ (trigger && !rule.trigger) ||
64
+ (trigger && rule.trigger && !rule.trigger.includes(trigger))
65
+ ) {
66
+ continue
67
+ }
68
+
69
+ // 必填校验
70
+ if (rule.required && (!value || value === '')) {
71
+ errorMessage = rule.message || `${field} 是必填项`
72
+ isValid = false
73
+ break
74
+ }
75
+
76
+ // 长度校验
77
+ if (rule.min && String(value).length < rule.min) {
78
+ errorMessage = rule.message || `${field} 长度不能少于 ${rule.min} 个字符`
79
+ isValid = false
80
+ break
81
+ }
82
+
83
+ if (rule.max && String(value).length > rule.max) {
84
+ errorMessage = rule.message || `${field} 长度不能超过 ${rule.max} 个字符`
85
+ isValid = false
86
+ break
87
+ }
88
+
89
+ // 类型校验
90
+ if (rule.type === 'phone') {
91
+ const phoneRegex = /^1[3-9]\d{9}$/
92
+ if (!phoneRegex.test(String(value))) {
93
+ errorMessage = rule.message || '请输入正确的手机号'
94
+ isValid = false
95
+ break
96
+ }
97
+ }
98
+
99
+ if (rule.type === 'email') {
100
+ const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/
101
+ if (!emailRegex.test(String(value))) {
102
+ errorMessage = rule.message || '请输入正确的邮箱格式'
103
+ isValid = false
104
+ break
105
+ }
106
+ }
107
+
108
+ if (rule.type === 'password') {
109
+ const passwordRegex =
110
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/
111
+ if (!passwordRegex.test(String(value))) {
112
+ errorMessage = rule.message || '密码至少8个字符,包含大小写字母、数字和特殊符号'
113
+ isValid = false
114
+ break
115
+ }
116
+ }
117
+
118
+ // 自定义校验
119
+ if (rule.validator) {
120
+ const result = rule.validator(value)
121
+ if (result === false || typeof result === 'string') {
122
+ errorMessage =
123
+ typeof result === 'string' ? result : rule.message || `${field} 校验失败`
124
+ isValid = false
125
+ break
126
+ }
127
+ }
128
+ }
129
+
130
+ if (isValid) {
131
+ delete errors[field]
132
+ } else {
133
+ errors[field] = errorMessage
134
+ }
135
+
136
+ return isValid
137
+ },
138
+ setFieldValue: (field: string, value: any) => {
139
+ formData[field] = value
140
+ },
141
+ getFieldValue: (field: string) => {
142
+ return formData[field]
143
+ }
144
+ }
145
+
146
+ // 提供表单上下文给子组件
147
+ provide('formContext', formContext)
148
+
149
+ // 验证所有字段
150
+ const validate = () => {
151
+ return new Promise((resolve, reject) => {
152
+ let isValid = true
153
+ const allErrors: Record<string, string> = {}
154
+
155
+ formItems.value.forEach((item) => {
156
+ const field = item.props?.prop
157
+ if (field) {
158
+ const value = formData[field]
159
+ const fieldValid = formContext.validateField(field, value)
160
+ if (!fieldValid) {
161
+ isValid = false
162
+ allErrors[field] = errors[field]
163
+ }
164
+ }
165
+ })
166
+
167
+ emit('validate', isValid, allErrors)
168
+ if (isValid) {
169
+ resolve(isValid)
170
+ } else {
171
+ reject(allErrors)
172
+ }
173
+ })
174
+ }
175
+
176
+ // 重置表单
177
+ const resetFields = () => {
178
+ clearVal(formData)
179
+ Object.keys(errors).forEach((key) => {
180
+ delete errors[key]
181
+ })
182
+ }
183
+
184
+ // 清除验证
185
+ const clearValidate = (fields?: string[]) => {
186
+ if (fields) {
187
+ fields.forEach((field) => {
188
+ delete errors[field]
189
+ })
190
+ } else {
191
+ Object.keys(errors).forEach((key) => {
192
+ delete errors[key]
193
+ })
194
+ }
195
+ }
196
+
197
+ // 提交表单
198
+ const submit = async () => {
199
+ if (await validate()) {
200
+ emit('submit', { ...formData })
201
+ return formData
202
+ }
203
+ return false
204
+ }
205
+
206
+ defineExpose({
207
+ validate,
208
+ resetFields,
209
+ clearValidate,
210
+ submit,
211
+ formData,
212
+ errors
213
+ })
214
+ </script>
215
+
216
+ <style lang="scss" scoped>
217
+ .hy-form {
218
+ width: 100%;
219
+ }
220
+ </style>