hy-app 0.6.4 → 0.6.6
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/attributes.json +1 -1
- package/components/hy-address-picker/hy-address-picker.vue +249 -249
- package/components/hy-address-picker/props.ts +103 -103
- package/components/hy-button/hy-button.vue +320 -289
- package/components/hy-button/props.ts +143 -143
- package/components/hy-button/typing.d.ts +43 -35
- package/components/hy-calendar/header.vue +58 -58
- package/components/hy-calendar/hy-calendar.vue +8 -6
- package/components/hy-calendar/month.vue +402 -402
- package/components/hy-calendar/props.ts +169 -169
- package/components/hy-calendar/typing.d.ts +47 -45
- package/components/hy-cell-item/hy-cell-item.vue +161 -161
- package/components/hy-cell-item/props.ts +59 -59
- package/components/hy-check-button/hy-check-button.vue +135 -135
- package/components/hy-code-input/hy-code-input.vue +231 -231
- package/components/hy-code-input/props.ts +90 -90
- package/components/hy-config-provider/hy-config-provider.vue +53 -53
- package/components/hy-config-provider/props.ts +30 -30
- package/components/hy-coupon/hy-coupon.vue +183 -183
- package/components/hy-coupon/props.ts +108 -108
- package/components/hy-datetime-picker/hy-datetime-picker.vue +41 -55
- package/components/hy-datetime-picker/props.ts +144 -144
- package/components/hy-datetime-picker/typing.d.ts +2 -0
- package/components/hy-divider/props.ts +83 -83
- package/components/hy-empty/icon.ts +72 -72
- package/components/hy-folding-panel/hy-folding-panel-group.vue +162 -162
- package/components/hy-form/hy-form.vue +220 -220
- package/components/hy-icon/hy-icon.vue +112 -112
- package/components/hy-index-bar/hy-index-bar.vue +185 -185
- package/components/hy-index-bar/index.scss +64 -64
- package/components/hy-index-bar/props.ts +94 -94
- package/components/hy-index-bar/typing.d.ts +36 -36
- package/components/hy-input/hy-input.vue +333 -333
- package/components/hy-input/props.ts +186 -186
- package/components/hy-modal/hy-modal.vue +211 -211
- package/components/hy-modal/props.ts +94 -94
- package/components/hy-modal/typing.d.ts +16 -16
- package/components/hy-notice-bar/hy-row-notice.vue +121 -121
- package/components/hy-notify/hy-notify.vue +174 -174
- package/components/hy-number-step/hy-number-step.vue +367 -367
- package/components/hy-overlay/hy-overlay.vue +61 -61
- package/components/hy-overlay/props.ts +38 -38
- package/components/hy-pagination/hy-pagination.vue +136 -136
- package/components/hy-pagination/props.ts +58 -58
- package/components/hy-parse/hy-parse.vue +550 -550
- package/components/hy-parse/node/node.vue +781 -781
- package/components/hy-parse/parser.js +1455 -1455
- package/components/hy-parse/props.ts +19 -19
- package/components/hy-parse/typing.d.ts +68 -68
- package/components/hy-picker/hy-picker.vue +435 -435
- package/components/hy-picker/props.ts +122 -122
- package/components/hy-picker/typing.d.ts +38 -38
- package/components/hy-qrcode/props.ts +72 -72
- package/components/hy-qrcode/qrcode.js.bak +1433 -1433
- package/components/hy-radio/props.ts +97 -97
- package/components/hy-read-more/props.ts +48 -48
- package/components/hy-search/props.ts +133 -133
- package/components/hy-signature/canvasHelper.ts +51 -51
- package/components/hy-signature/props.ts +121 -121
- package/components/hy-skeleton/hy-skeleton.vue +142 -142
- package/components/hy-skeleton/props.ts +46 -46
- package/components/hy-skeleton/typing.d.ts +31 -31
- package/components/hy-steps/hy-steps.vue +275 -275
- package/components/hy-steps/typing.d.ts +25 -25
- package/components/hy-swiper/hy-swiper.vue +3 -3
- package/components/hy-swiper/index.scss +5 -5
- package/components/hy-swiper/props.ts +0 -1
- package/components/hy-table/hy-table.vue +630 -630
- package/components/hy-table/props.ts +62 -62
- package/components/hy-table/typing.d.ts +29 -29
- package/components/hy-tabs/hy-tabs.vue +336 -335
- package/components/hy-tabs/props.ts +84 -77
- package/components/hy-tag/hy-tag.vue +173 -173
- package/components/hy-tag/props.ts +89 -89
- package/components/hy-text/hy-text.vue +237 -237
- package/components/hy-text/props.ts +115 -115
- package/components/hy-textarea/hy-textarea.vue +198 -198
- package/components/hy-toast/hy-toast.vue +200 -200
- package/components/hy-toast/props.ts +3 -3
- package/components/hy-transition/hy-transition.vue +157 -157
- package/components/hy-transition/props.ts +32 -32
- package/components/hy-upload/hy-upload.vue +384 -384
- package/components/hy-watermark/hy-watermark.vue +1058 -1058
- package/components/hy-watermark/props.ts +109 -109
- package/global.d.ts +94 -94
- package/libs/api/http.ts +119 -119
- package/libs/composables/index.ts +8 -8
- package/libs/composables/useMessage.ts +149 -149
- package/libs/composables/useToast.ts +45 -45
- package/libs/composables/useTranslate.ts +10 -10
- package/libs/css/_config.scss +5 -5
- package/libs/index.ts +8 -8
- package/libs/locale/index.ts +32 -32
- package/libs/locale/lang/en-US.ts +84 -84
- package/libs/locale/lang/zh-CN.ts +87 -87
- package/libs/typing/index.ts +2 -2
- package/libs/typing/modules/common.d.ts +139 -139
- package/libs/typing/modules/form.ts +176 -176
- package/libs/typing/modules/http.d.ts +19 -19
- package/libs/typing/modules/index.d.ts +12 -12
- package/libs/utils/inside.ts +340 -340
- package/libs/utils/inspect.ts +140 -140
- package/libs/utils/utils.ts +525 -525
- package/package.json +81 -81
- package/tags.json +1 -1
- package/web-types.json +1 -1
|
@@ -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>
|
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<view :class="`hy-icon ${customClass} hy-icon--${labelPos}`" @tap="clickHandler">
|
|
3
|
-
<image
|
|
4
|
-
class="hy-icon__img"
|
|
5
|
-
v-if="isImg"
|
|
6
|
-
:src="name"
|
|
7
|
-
:mode="imgMode"
|
|
8
|
-
:style="[imgStyle, customStyle]"
|
|
9
|
-
></image>
|
|
10
|
-
<text v-else :class="iconClass" :style="[iconStyle, customStyle]"></text>
|
|
11
|
-
<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
|
|
12
|
-
<text
|
|
13
|
-
v-if="label"
|
|
14
|
-
class="hy-icon__label"
|
|
15
|
-
:style="{
|
|
16
|
-
color: labelColor,
|
|
17
|
-
fontSize: addUnit(labelSize),
|
|
18
|
-
marginLeft: labelPos == 'right' ? addUnit(space) : 0,
|
|
19
|
-
marginTop: labelPos == 'bottom' ? addUnit(space) : 0
|
|
20
|
-
}"
|
|
21
|
-
>
|
|
22
|
-
{{ label }}
|
|
23
|
-
</text>
|
|
24
|
-
</view>
|
|
25
|
-
</template>
|
|
26
|
-
|
|
27
|
-
<script lang="ts">
|
|
28
|
-
export default {
|
|
29
|
-
name: 'hy-icon',
|
|
30
|
-
options: {
|
|
31
|
-
addGlobalClass: true,
|
|
32
|
-
virtualHost: true,
|
|
33
|
-
styleIsolation: 'shared'
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
</script>
|
|
37
|
-
|
|
38
|
-
<script setup lang="ts">
|
|
39
|
-
import { computed } from 'vue'
|
|
40
|
-
import type { CSSProperties } from 'vue'
|
|
41
|
-
import { addUnit } from '../../libs'
|
|
42
|
-
import type { IIconEmits } from './typing'
|
|
43
|
-
import iconProps from './props'
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* 基于字体的图标集,包含了大多数常见场景的图标,使用简单,开箱即用,无需自己再写每个图标的样式,直接简单配置即可。支持自定义图标。
|
|
47
|
-
* @displayName hy-icon
|
|
48
|
-
*/
|
|
49
|
-
defineOptions({})
|
|
50
|
-
|
|
51
|
-
const props = defineProps(iconProps)
|
|
52
|
-
const emit = defineEmits<IIconEmits>()
|
|
53
|
-
|
|
54
|
-
const iconClass = computed(() => {
|
|
55
|
-
let classes: string | string[] = [
|
|
56
|
-
'hy-icon__icon',
|
|
57
|
-
props.customPrefix,
|
|
58
|
-
`${props.customPrefix}-${props.name}`
|
|
59
|
-
]
|
|
60
|
-
if (props.isRotate) classes.push('hy-rotate')
|
|
61
|
-
if (props.color)
|
|
62
|
-
// 主题色,通过类配置
|
|
63
|
-
classes.push('hy-icon__icon--' + props.color)
|
|
64
|
-
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
|
|
65
|
-
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
|
|
66
|
-
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
|
|
67
|
-
classes = classes.join(' ')
|
|
68
|
-
//#endif
|
|
69
|
-
return classes
|
|
70
|
-
})
|
|
71
|
-
const iconStyle = computed<CSSProperties>(() => {
|
|
72
|
-
const style: CSSProperties = {
|
|
73
|
-
fontSize: addUnit(props.size),
|
|
74
|
-
lineHeight: addUnit(props.size),
|
|
75
|
-
fontWeight: props.bold ? 'bold' : 'normal',
|
|
76
|
-
// 某些特殊情况需要设置一个到顶部的距离,才能更好地垂直居中
|
|
77
|
-
top: addUnit(props.top),
|
|
78
|
-
borderRadius: addUnit(props.round),
|
|
79
|
-
color: props.color
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return style
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
|
|
87
|
-
* */
|
|
88
|
-
const isImg = computed(() => {
|
|
89
|
-
return props.name?.indexOf('/') !== -1
|
|
90
|
-
})
|
|
91
|
-
const imgStyle = computed((): CSSProperties => {
|
|
92
|
-
let style: CSSProperties = {}
|
|
93
|
-
// 如果设置width和height属性,则优先使用,否则使用size属性
|
|
94
|
-
style.width = props.width ? addUnit(props.width) : addUnit(props.size)
|
|
95
|
-
style.height = props.height ? addUnit(props.height) : addUnit(props.size)
|
|
96
|
-
style.borderRadius = addUnit(props.round)
|
|
97
|
-
return style
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* 点击icon图标执行
|
|
102
|
-
* */
|
|
103
|
-
const clickHandler = (e: Event) => {
|
|
104
|
-
emit('click', props.index, e)
|
|
105
|
-
// 是否阻止事件冒泡
|
|
106
|
-
props.stop && e.stopPropagation()
|
|
107
|
-
}
|
|
108
|
-
</script>
|
|
109
|
-
|
|
110
|
-
<style lang="scss" scoped>
|
|
111
|
-
@import './index.scss';
|
|
112
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<view :class="`hy-icon ${customClass} hy-icon--${labelPos}`" @tap="clickHandler">
|
|
3
|
+
<image
|
|
4
|
+
class="hy-icon__img"
|
|
5
|
+
v-if="isImg"
|
|
6
|
+
:src="name"
|
|
7
|
+
:mode="imgMode"
|
|
8
|
+
:style="[imgStyle, customStyle]"
|
|
9
|
+
></image>
|
|
10
|
+
<text v-else :class="iconClass" :style="[iconStyle, customStyle]"></text>
|
|
11
|
+
<!-- 这里进行空字符串判断,如果仅仅是v-if="label",可能会出现传递0的时候,结果也无法显示 -->
|
|
12
|
+
<text
|
|
13
|
+
v-if="label"
|
|
14
|
+
class="hy-icon__label"
|
|
15
|
+
:style="{
|
|
16
|
+
color: labelColor,
|
|
17
|
+
fontSize: addUnit(labelSize),
|
|
18
|
+
marginLeft: labelPos == 'right' ? addUnit(space) : 0,
|
|
19
|
+
marginTop: labelPos == 'bottom' ? addUnit(space) : 0
|
|
20
|
+
}"
|
|
21
|
+
>
|
|
22
|
+
{{ label }}
|
|
23
|
+
</text>
|
|
24
|
+
</view>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script lang="ts">
|
|
28
|
+
export default {
|
|
29
|
+
name: 'hy-icon',
|
|
30
|
+
options: {
|
|
31
|
+
addGlobalClass: true,
|
|
32
|
+
virtualHost: true,
|
|
33
|
+
styleIsolation: 'shared'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import { computed } from 'vue'
|
|
40
|
+
import type { CSSProperties } from 'vue'
|
|
41
|
+
import { addUnit } from '../../libs'
|
|
42
|
+
import type { IIconEmits } from './typing'
|
|
43
|
+
import iconProps from './props'
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 基于字体的图标集,包含了大多数常见场景的图标,使用简单,开箱即用,无需自己再写每个图标的样式,直接简单配置即可。支持自定义图标。
|
|
47
|
+
* @displayName hy-icon
|
|
48
|
+
*/
|
|
49
|
+
defineOptions({})
|
|
50
|
+
|
|
51
|
+
const props = defineProps(iconProps)
|
|
52
|
+
const emit = defineEmits<IIconEmits>()
|
|
53
|
+
|
|
54
|
+
const iconClass = computed(() => {
|
|
55
|
+
let classes: string | string[] = [
|
|
56
|
+
'hy-icon__icon',
|
|
57
|
+
props.customPrefix,
|
|
58
|
+
`${props.customPrefix}-${props.name}`
|
|
59
|
+
]
|
|
60
|
+
if (props.isRotate) classes.push('hy-rotate')
|
|
61
|
+
if (props.color)
|
|
62
|
+
// 主题色,通过类配置
|
|
63
|
+
classes.push('hy-icon__icon--' + props.color)
|
|
64
|
+
// 阿里,头条,百度小程序通过数组绑定类名时,无法直接使用[a, b, c]的形式,否则无法识别
|
|
65
|
+
// 故需将其拆成一个字符串的形式,通过空格隔开各个类名
|
|
66
|
+
//#ifdef MP-ALIPAY || MP-TOUTIAO || MP-BAIDU
|
|
67
|
+
classes = classes.join(' ')
|
|
68
|
+
//#endif
|
|
69
|
+
return classes
|
|
70
|
+
})
|
|
71
|
+
const iconStyle = computed<CSSProperties>(() => {
|
|
72
|
+
const style: CSSProperties = {
|
|
73
|
+
fontSize: addUnit(props.size),
|
|
74
|
+
lineHeight: addUnit(props.size),
|
|
75
|
+
fontWeight: props.bold ? 'bold' : 'normal',
|
|
76
|
+
// 某些特殊情况需要设置一个到顶部的距离,才能更好地垂直居中
|
|
77
|
+
top: addUnit(props.top),
|
|
78
|
+
borderRadius: addUnit(props.round),
|
|
79
|
+
color: props.color
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return style
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
|
|
87
|
+
* */
|
|
88
|
+
const isImg = computed(() => {
|
|
89
|
+
return props.name?.indexOf('/') !== -1
|
|
90
|
+
})
|
|
91
|
+
const imgStyle = computed((): CSSProperties => {
|
|
92
|
+
let style: CSSProperties = {}
|
|
93
|
+
// 如果设置width和height属性,则优先使用,否则使用size属性
|
|
94
|
+
style.width = props.width ? addUnit(props.width) : addUnit(props.size)
|
|
95
|
+
style.height = props.height ? addUnit(props.height) : addUnit(props.size)
|
|
96
|
+
style.borderRadius = addUnit(props.round)
|
|
97
|
+
return style
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 点击icon图标执行
|
|
102
|
+
* */
|
|
103
|
+
const clickHandler = (e: Event) => {
|
|
104
|
+
emit('click', props.index, e)
|
|
105
|
+
// 是否阻止事件冒泡
|
|
106
|
+
props.stop && e.stopPropagation()
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style lang="scss" scoped>
|
|
111
|
+
@import './index.scss';
|
|
112
|
+
</style>
|