hy-app 0.2.16 → 0.3.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 (100) hide show
  1. package/components/hy-action-sheet/hy-action-sheet.vue +5 -5
  2. package/components/hy-action-sheet/index.scss +6 -1
  3. package/components/hy-address-picker/hy-address-picker.vue +5 -5
  4. package/components/hy-avatar/hy-avatar.vue +5 -5
  5. package/components/hy-back-top/hy-back-top.vue +5 -5
  6. package/components/hy-badge/hy-badge.vue +5 -5
  7. package/components/hy-button/hy-button.vue +5 -5
  8. package/components/hy-calendar/hy-calendar.vue +22 -18
  9. package/components/hy-calendar/typing.d.ts +1 -1
  10. package/components/hy-card/hy-card.vue +5 -5
  11. package/components/hy-cell/hy-cell.vue +10 -11
  12. package/components/hy-check-button/hy-check-button.vue +7 -6
  13. package/components/hy-checkbox/hy-checkbox.vue +14 -14
  14. package/components/hy-code-input/hy-code-input.vue +8 -15
  15. package/components/hy-code-input/index.scss +1 -1
  16. package/components/hy-config-provider/hy-config-provider.vue +8 -6
  17. package/components/hy-count-down/hy-count-down.vue +7 -6
  18. package/components/hy-count-to/hy-count-to.vue +6 -5
  19. package/components/hy-datetime-picker/hy-datetime-picker.vue +7 -7
  20. package/components/hy-divider/hy-divider.vue +10 -7
  21. package/components/hy-dropdown/hy-dropdown.vue +6 -5
  22. package/components/hy-dropdown-item/hy-dropdown-item.vue +7 -5
  23. package/components/hy-empty/hy-empty.vue +6 -5
  24. package/components/hy-float-button/hy-float-button.vue +6 -6
  25. package/components/hy-folding-panel/hy-folding-panel.vue +6 -5
  26. package/components/hy-form/README.md +301 -0
  27. package/components/hy-form/hy-form.vue +252 -533
  28. package/components/hy-form/index.ts +5 -0
  29. package/components/hy-form/typing.d.ts +57 -77
  30. package/components/hy-form-group/hy-form-group.vue +533 -0
  31. package/components/hy-form-group/typing.d.ts +77 -0
  32. package/components/hy-form-item/hy-form-item.vue +199 -0
  33. package/components/hy-form-item/index.scss +41 -0
  34. package/components/hy-form-item/typing.d.ts +43 -0
  35. package/components/hy-grid/hy-grid.vue +11 -13
  36. package/components/hy-grid/typing.d.ts +1 -1
  37. package/components/hy-icon/hy-icon.vue +15 -15
  38. package/components/hy-image/hy-image.vue +15 -9
  39. package/components/hy-input/hy-input.vue +17 -11
  40. package/components/hy-line/hy-line.vue +8 -6
  41. package/components/hy-line-progress/hy-line-progress.vue +9 -16
  42. package/components/hy-list/hy-list.vue +6 -5
  43. package/components/hy-loading/hy-loading.vue +9 -10
  44. package/components/hy-login/TheUserLogin.vue +82 -90
  45. package/components/hy-menu/hy-menu.vue +11 -8
  46. package/components/hy-modal/hy-modal.vue +6 -6
  47. package/components/hy-modal/index.scss +1 -1
  48. package/components/hy-navbar/hy-navbar.vue +6 -6
  49. package/components/hy-notice-bar/hy-notice-bar.vue +14 -9
  50. package/components/hy-notify/hy-notify.vue +6 -6
  51. package/components/hy-number-step/hy-number-step.vue +8 -6
  52. package/components/hy-overlay/hy-overlay.vue +8 -9
  53. package/components/hy-pagination/hy-pagination.vue +11 -8
  54. package/components/hy-picker/hy-picker.vue +9 -9
  55. package/components/hy-popover/hy-popover.vue +11 -7
  56. package/components/hy-popup/hy-popup.vue +9 -7
  57. package/components/hy-price/hy-price.vue +6 -5
  58. package/components/hy-qrcode/hy-qrcode.vue +6 -8
  59. package/components/hy-radio/hy-radio.vue +10 -12
  60. package/components/hy-rate/hy-rate.vue +7 -7
  61. package/components/hy-read-more/hy-read-more.vue +6 -5
  62. package/components/hy-scroll-list/hy-scroll-list.vue +6 -5
  63. package/components/hy-search/hy-search.vue +8 -6
  64. package/components/hy-signature/hy-signature.vue +6 -5
  65. package/components/hy-slider/hy-slider.vue +8 -7
  66. package/components/hy-steps/hy-steps.vue +10 -17
  67. package/components/hy-submit-bar/hy-submit-bar.vue +6 -6
  68. package/components/hy-subsection/hy-subsection.vue +16 -19
  69. package/components/hy-swipe-action/hy-swipe-action.vue +31 -25
  70. package/components/hy-swipe-action/typing.d.ts +1 -1
  71. package/components/hy-swiper/hy-swiper.vue +15 -15
  72. package/components/hy-swiper/typing.d.ts +13 -1
  73. package/components/hy-switch/hy-switch.vue +17 -10
  74. package/components/hy-tabBar/hy-tabBar.vue +35 -26
  75. package/components/hy-tabs/hy-tabs.vue +28 -21
  76. package/components/hy-tag/hy-tag.vue +13 -17
  77. package/components/hy-tag/typing.d.ts +1 -1
  78. package/components/hy-text/hy-text.vue +12 -14
  79. package/components/hy-textarea/hy-textarea.vue +16 -13
  80. package/components/hy-textarea/index.scss +7 -2
  81. package/components/hy-textarea/typing.d.ts +1 -1
  82. package/components/hy-toast/hy-toast.vue +72 -75
  83. package/components/hy-tooltip/hy-tooltip.vue +8 -14
  84. package/components/hy-transition/hy-transition.vue +8 -6
  85. package/components/hy-upload/hy-upload.vue +6 -5
  86. package/components/hy-warn/hy-warn.vue +6 -6
  87. package/components/hy-waterfall/hy-waterfall.vue +8 -7
  88. package/components/hy-watermark/hy-watermark.vue +20 -10
  89. package/components/index.ts +7 -1
  90. package/global.d.ts +3 -1
  91. package/libs/css/common.scss +5 -0
  92. package/libs/css/mixin.scss +2 -1
  93. package/libs/css/vars.css +1 -1
  94. package/package.json +2 -2
  95. package/theme.scss +1 -1
  96. package/utils/inspect.ts +34 -34
  97. package/utils/utils.ts +1 -19
  98. package/web-types.json +1 -1
  99. /package/components/{hy-form → hy-form-group}/index.scss +0 -0
  100. /package/components/{hy-form → hy-form-group}/props.ts +0 -0
@@ -0,0 +1,5 @@
1
+ import HyFormSimple from './hy-form-simple.vue'
2
+ import HyFormItemSimple from './hy-form-item-simple.vue'
3
+
4
+ export { HyFormSimple, HyFormItemSimple }
5
+ export default HyFormSimple
@@ -1,77 +1,57 @@
1
- import type { FormColumnsType } from "../../typing";
2
- import type HyInputProps from "../hy-input/typing";
3
- import type HyTextareaProps from "../hy-textarea/typing";
4
- import type HySwitchProps from "../hy-switch/typing";
5
- import type HyPickerProps from "../hy-picker/typing";
6
- import type HyRadioProps from "../hy-radio/typing";
7
- import type HyCheckButtonProps from "../hy-check-button/typing";
8
-
9
- export default interface HyFormProps {
10
- /**
11
- * @description 表单域提示文字的位置
12
- * left - 左侧
13
- * top - 上方
14
- * */
15
- labelPosition?: "left" | "top";
16
- /**
17
- * @description label宽度
18
- * 数字 - 固定值
19
- * auto - 自适应
20
- * */
21
- labelWidth?: string | number;
22
- /**
23
- * @description 是否右对齐
24
- * */
25
- right?: boolean;
26
- /**
27
- * @description label字体的对齐方式
28
- * left - 左对齐
29
- * center - 中间对齐
30
- * right - 右对齐
31
- * */
32
- labelAlign?: HyApp.RowCenterType;
33
- /**
34
- * @description 显示冒号符号
35
- * */
36
- symbol?: boolean;
37
- /**
38
- * @description 显示底部下划线
39
- * */
40
- borderBottom?: boolean;
41
- /**
42
- * @description 当行内容高度
43
- * */
44
- itemHeight?: number | string;
45
- /**
46
- * @description 输入框属性api集合
47
- * */
48
- input?: Partial<HyInputProps>;
49
- /**
50
- * @description 文本域属性api集合
51
- * */
52
- textarea?: Partial<HyTextareaProps>;
53
- /**
54
- * @description 选择器属性api集合
55
- * */
56
- picker?: Partial<HyPickerProps>;
57
- /**
58
- * @description 开关属性api集合
59
- * */
60
- switchItem?: Partial<HySwitchProps>;
61
- /**
62
- * @description 单选属性api集合
63
- * */
64
- radio?: Partial<HyRadioProps>;
65
- /**
66
- * @description 选择按钮属性api集合
67
- * */
68
- checkButton?: Partial<HyCheckButtonProps>;
69
- /**
70
- * @description 表单配置
71
- * */
72
- columns: FormColumnsType[];
73
- /**
74
- * @description 表单值
75
- * */
76
- formData: Record<string, any>;
77
- }
1
+ export interface FormRule {
2
+ /**
3
+ * 是否必填
4
+ */
5
+ required?: boolean
6
+ /**
7
+ * 验证失败时的提示信息
8
+ */
9
+ message?: string
10
+ /**
11
+ * 自定义验证函数
12
+ */
13
+ validator?: (value: any) => boolean | string
14
+ /**
15
+ * 触发验证的时机
16
+ */
17
+ trigger?: ('blur' | 'change')[]
18
+ /**
19
+ * 最小长度
20
+ */
21
+ min?: number
22
+ /**
23
+ * 最大长度
24
+ */
25
+ max?: number
26
+ /**
27
+ * 验证类型
28
+ */
29
+ type?: 'phone' | 'email' | 'password'
30
+ }
31
+
32
+ export interface FormItemRule {
33
+ [key: string]: FormRule | FormRule[]
34
+ }
35
+
36
+ export default interface HyFormSimpleProps {
37
+ /**
38
+ * 表单数据对象
39
+ */
40
+ model?: Record<string, any>
41
+ /**
42
+ * 验证规则
43
+ */
44
+ rules?: FormItemRule
45
+ /**
46
+ * 标签宽度
47
+ */
48
+ labelWidth?: string | number
49
+ /**
50
+ * 标签位置
51
+ */
52
+ labelPosition?: 'left' | 'top'
53
+ /**
54
+ * 标签对齐方式
55
+ */
56
+ labelAlign?: 'left' | 'center' | 'right'
57
+ }
@@ -0,0 +1,533 @@
1
+ <template>
2
+ <view class="hy-form">
3
+ <view
4
+ :class="['hy-form--item', borderBottom && 'hy-border__bottom']"
5
+ v-for="item in columns"
6
+ :key="item.field"
7
+ >
8
+ <view v-if="item.label" class="hy-form--item__label" :style="labelStyle">
9
+ <text
10
+ v-if="isRequired(item.rules)"
11
+ style="color: red; font-size: 20px; line-height: 10px"
12
+ >*</text
13
+ >{{ item.label }} <text v-if="symbol">:</text>
14
+ </view>
15
+ <view class="hy-form--item__container">
16
+ <view class="hy-form--item__container-content">
17
+ <!-- 输入框 -->
18
+ <view class="flex" v-if="isInput(item.type)">
19
+ <HyInput
20
+ v-model="formData[item.field]"
21
+ :type="item.type"
22
+ :disabled="item?.input?.disabled || input?.disabled"
23
+ :disabledColor="
24
+ item?.input?.disabledColor || input?.disabledColor
25
+ "
26
+ :maxlength="item?.input?.maxlength || input?.maxlength"
27
+ :password="item?.input?.password"
28
+ :clearable="item?.input?.clearable || input?.clearable"
29
+ :readonly="item?.input?.readonly || input?.readonly"
30
+ :placeholder="item?.input?.placeholder"
31
+ :placeholderClass="
32
+ item?.input?.placeholderClass || input?.placeholderClass
33
+ "
34
+ :placeholderStyle="
35
+ item?.input?.placeholderStyle || input?.placeholderStyle
36
+ "
37
+ :showWordLimit="
38
+ item?.input?.showWordLimit || input?.showWordLimit
39
+ "
40
+ :confirmType="item?.input?.confirmType || input?.confirmType"
41
+ :confirmHold="item?.input?.confirmHold || input?.confirmHold"
42
+ :holdKeyboard="item?.input?.holdKeyboard || input?.holdKeyboard"
43
+ :focus="item?.input?.focus || input?.focus"
44
+ :autoBlur="item?.input?.autoBlur || input?.autoBlur"
45
+ :selectionStart="
46
+ item?.input?.selectionStart || input?.selectionStart
47
+ "
48
+ :selectionEnd="item?.input?.selectionEnd || input?.selectionEnd"
49
+ :adjustPosition="
50
+ item?.input?.adjustPosition || input?.adjustPosition
51
+ "
52
+ :inputAlign="item?.input?.inputAlign || input?.inputAlign"
53
+ :fontSize="item?.input?.fontSize || input?.fontSize"
54
+ :color="item?.input?.color || input?.color"
55
+ :prefixIcon="item?.input?.prefixIcon || input?.prefixIcon"
56
+ :suffixIcon="item?.input?.suffixIcon || input?.suffixIcon"
57
+ :formatter="item?.input?.formatter || input?.formatter"
58
+ :shape="item?.input?.shape || input?.shape"
59
+ :border="item?.input?.border || input?.border"
60
+ :customStyle="errorStyle(!!errors[item.field])"
61
+ @change="handleChange($event, item)"
62
+ @blur="handleBlur($event, item)"
63
+ @onPrefix="item?.input?.onPrefix"
64
+ @onSuffix="item?.input?.onSuffix"
65
+ ></HyInput>
66
+ </view>
67
+ <!-- 输入框 -->
68
+
69
+ <!-- 上传 -->
70
+ <template v-if="item.type === FormTypeEnum.UPLOAD">
71
+ <HyUpload
72
+ :fileList="formData[item.field]"
73
+ :maxCount="item.maxCount"
74
+ :disabled="item.disabled"
75
+ />
76
+ </template>
77
+ <!-- 上传 -->
78
+
79
+ <!-- 详情 -->
80
+ <template v-if="item.type === FormTypeEnum.DETAIL">
81
+ <view class="detail">
82
+ {{ formData[item.field] }}
83
+ </view>
84
+ </template>
85
+ <!-- 详情 -->
86
+
87
+ <!-- 文本域 -->
88
+ <view class="flex" v-if="item.type === FormTypeEnum.TEXTAREA">
89
+ <HyTextarea
90
+ v-model="formData[item.field]"
91
+ :disabled="item?.textarea?.disabled || textarea?.disabled"
92
+ :maxlength="item?.textarea?.maxlength || textarea?.maxlength"
93
+ :placeholder="item?.textarea?.placeholder"
94
+ :placeholderClass="
95
+ item?.textarea?.placeholderClass || textarea?.placeholderClass
96
+ "
97
+ :placeholderStyle="
98
+ item?.textarea?.placeholderStyle || textarea?.placeholderStyle
99
+ "
100
+ :holdKeyboard="
101
+ item?.textarea?.holdKeyboard || textarea?.holdKeyboard
102
+ "
103
+ :focus="item?.textarea?.focus || textarea?.focus"
104
+ :selectionStart="
105
+ item?.textarea?.selectionStart || textarea?.selectionStart
106
+ "
107
+ :selectionEnd="
108
+ item?.textarea?.selectionEnd || textarea?.selectionEnd
109
+ "
110
+ :adjustPosition="
111
+ item?.textarea?.adjustPosition || textarea?.adjustPosition
112
+ "
113
+ :formatter="item?.textarea?.formatter || textarea?.formatter"
114
+ :border="item?.textarea?.border || textarea?.border"
115
+ :customStyle="errorStyle(!!errors[item.field])"
116
+ :height="textarea?.height || item?.textarea?.height"
117
+ @change="handleChange($event, item)"
118
+ @blur="handleBlur($event, item)"
119
+ ></HyTextarea>
120
+ </view>
121
+ <!-- 文本域 -->
122
+
123
+ <!-- 复选框/单选框 -->
124
+ <template v-if="item.type === FormTypeEnum.CHECK_BUTTON">
125
+ <hy-check-button
126
+ v-model="formData[item.field]"
127
+ :columns="item.actions"
128
+ :type="item?.checkButton?.type || checkButton?.type"
129
+ :shape="item?.checkButton?.shape || checkButton?.shape"
130
+ :size="item?.checkButton?.size || checkButton?.size"
131
+ :col="item?.checkButton?.col || checkButton?.col"
132
+ :gap="item?.checkButton?.gap || checkButton?.gap"
133
+ :fieldNames="
134
+ item?.checkButton?.fieldNames || checkButton?.fieldNames
135
+ "
136
+ :disabled="item?.checkButton?.size || checkButton?.disabled"
137
+ :selectType="
138
+ item?.checkButton?.selectType || checkButton?.selectType
139
+ "
140
+ ></hy-check-button>
141
+ </template>
142
+ <!-- 复选框/单选框 -->
143
+
144
+ <!-- 单选框 -->
145
+ <template v-if="item.type === FormTypeEnum.RADIO">
146
+ <HyRadio
147
+ v-model="formData[item.field]"
148
+ :columns="item.actions"
149
+ :fieldNames="item?.radio?.fieldNames || radio?.fieldNames"
150
+ :shape="item?.radio?.shape || radio?.shape"
151
+ :disabled="item?.radio?.disabled || radio?.disabled"
152
+ :size="item?.radio?.size || radio?.size"
153
+ :activeColor="item?.radio?.activeColor || radio?.activeColor"
154
+ :inactiveColor="
155
+ item?.radio?.inactiveColor || radio?.inactiveColor
156
+ "
157
+ :iconSize="item?.radio?.iconSize || radio?.iconSize"
158
+ :iconColor="item?.radio?.iconColor || radio?.iconColor"
159
+ :labelDisabled="item?.radio?.disabled || radio?.labelDisabled"
160
+ :placement="item?.radio?.placement || radio?.placement"
161
+ ></HyRadio>
162
+ </template>
163
+ <!-- 单选框 -->
164
+
165
+ <!-- 开关 -->
166
+ <template v-if="item.type === FormTypeEnum.SWITCH">
167
+ <HySwitch
168
+ v-model="formData[item.field]"
169
+ :loading="item?.switchItem?.loading || switchItem?.loading"
170
+ :disabled="item?.switchItem?.disabled || switchItem?.disabled"
171
+ :size="item?.switchItem?.size || switchItem?.size"
172
+ :activeColor="
173
+ item?.switchItem?.activeColor || switchItem?.activeColor
174
+ "
175
+ :inactiveColor="
176
+ item?.switchItem?.inactiveColor || switchItem?.inactiveColor
177
+ "
178
+ :activeValue="
179
+ item?.switchItem?.activeValue || switchItem?.activeValue
180
+ "
181
+ :inactiveValue="
182
+ item?.switchItem?.inactiveValue || switchItem?.inactiveValue
183
+ "
184
+ :activeIcon="
185
+ item?.switchItem?.activeIcon || switchItem?.activeIcon
186
+ "
187
+ :inactiveIcon="
188
+ item?.switchItem?.inactiveIcon || switchItem?.inactiveIcon
189
+ "
190
+ :space="item?.switchItem?.space || switchItem?.space"
191
+ ></HySwitch>
192
+ </template>
193
+ <!-- 开关 -->
194
+
195
+ <!-- 自定义选择器 -->
196
+ <template v-if="item.type === FormTypeEnum.SELECT">
197
+ <HyPicker
198
+ v-model="formData[item.field]"
199
+ :columns="item.select"
200
+ has-input
201
+ :separator="item?.picker?.separator || picker?.separator"
202
+ :itemHeight="item?.picker?.itemHeight || picker?.itemHeight"
203
+ :cancelText="item?.picker?.cancelText || picker?.cancelText"
204
+ :confirmText="item?.picker?.confirmText || picker?.confirmText"
205
+ :cancelColor="item?.picker?.cancelColor || picker?.cancelColor"
206
+ :confirmColor="item?.picker?.confirmColor || picker?.confirmColor"
207
+ :visibleItemCount="
208
+ item?.picker?.visibleItemCount || picker?.visibleItemCount
209
+ "
210
+ :closeOnClickOverlay="
211
+ item?.picker?.closeOnClickOverlay || picker?.closeOnClickOverlay
212
+ "
213
+ :title="item?.picker?.title || picker?.title"
214
+ :showToolbar="item?.picker?.showToolbar || picker?.showToolbar"
215
+ :customStyle="errorStyle(!!errors[item.field])"
216
+ :input="{
217
+ disabled:
218
+ item?.picker?.input?.disabled || picker?.input?.disabled,
219
+ placeholder: item.picker?.input?.placeholder,
220
+ shape: item?.picker?.input?.shape || picker?.input?.shape,
221
+ border: item?.picker?.input?.border || picker?.input?.border,
222
+ customStyle: errorStyle(!!errors[item.field]),
223
+ }"
224
+ ></HyPicker>
225
+ </template>
226
+ <!-- 自定义选择器 -->
227
+
228
+ <!-- 时间选择器 -->
229
+ <template v-if="item.type === FormTypeEnum.DATE">
230
+ <HyDatetimePicker
231
+ v-model="formData[item.field]"
232
+ has-input
233
+ :mode="item.mode"
234
+ :separator="item?.picker?.separator || picker?.separator"
235
+ :itemHeight="item?.picker?.itemHeight || picker?.itemHeight"
236
+ :cancelText="item?.picker?.cancelText || picker?.cancelText"
237
+ :confirmText="item?.picker?.confirmText || picker?.confirmText"
238
+ :cancelColor="item?.picker?.cancelColor || picker?.cancelColor"
239
+ :confirmColor="item?.picker?.confirmColor || picker?.confirmColor"
240
+ :visibleItemCount="
241
+ item?.picker?.visibleItemCount || picker?.visibleItemCount
242
+ "
243
+ :closeOnClickOverlay="
244
+ item?.picker?.closeOnClickOverlay || picker?.closeOnClickOverlay
245
+ "
246
+ :title="item?.picker?.title || picker?.title"
247
+ :showToolbar="item?.picker?.showToolbar || picker?.showToolbar"
248
+ :customStyle="errorStyle(!!errors[item.field])"
249
+ :input="{
250
+ disabled:
251
+ item?.picker?.input?.disabled || picker?.input?.disabled,
252
+ placeholder: item.picker?.input?.placeholder,
253
+ shape: item?.picker?.input?.shape || picker?.input?.shape,
254
+ border: item?.picker?.input?.border || picker?.input?.border,
255
+ customStyle: errorStyle(!!errors[item.field]),
256
+ }"
257
+ ></HyDatetimePicker>
258
+ </template>
259
+ <!-- 时间选择器 -->
260
+
261
+ <!-- 地址选择器 -->
262
+ <template v-if="item.type === FormTypeEnum.ADDRESS">
263
+ <HyAddressPicker
264
+ v-model="formData[item.field]"
265
+ has-input
266
+ :separator="item?.picker?.separator || picker?.separator"
267
+ :itemHeight="item?.picker?.itemHeight || picker?.itemHeight"
268
+ :cancelText="item?.picker?.cancelText || picker?.cancelText"
269
+ :confirmText="item?.picker?.confirmText || picker?.confirmText"
270
+ :cancelColor="item?.picker?.cancelColor || picker?.cancelColor"
271
+ :confirmColor="item?.picker?.confirmColor || picker?.confirmColor"
272
+ :visibleItemCount="
273
+ item?.picker?.visibleItemCount || picker?.visibleItemCount
274
+ "
275
+ :closeOnClickOverlay="
276
+ item?.picker?.closeOnClickOverlay || picker?.closeOnClickOverlay
277
+ "
278
+ :title="item?.picker?.title || picker?.title"
279
+ :showToolbar="item?.picker?.showToolbar || picker?.showToolbar"
280
+ :customStyle="errorStyle(!!errors[item.field])"
281
+ :input="{
282
+ disabled:
283
+ item?.picker?.input?.disabled || picker?.input?.disabled,
284
+ placeholder: item.picker?.input?.placeholder,
285
+ shape: item?.picker?.input?.shape || picker?.input?.shape,
286
+ border: item?.picker?.input?.border || picker?.input?.border,
287
+ customStyle: errorStyle(!!errors[item.field]),
288
+ }"
289
+ ></HyAddressPicker>
290
+ </template>
291
+ <!-- 地址选择器 -->
292
+
293
+ <!-- 自定义插槽 -->
294
+ <view class="flex" v-if="item.type === FormTypeEnum.CUSTOM">
295
+ <slot
296
+ :name="item.field"
297
+ :record="item"
298
+ :errorStyle="errorStyle(!!errors[item.field])"
299
+ ></slot>
300
+ </view>
301
+ <!-- 自定义插槽 -->
302
+ </view>
303
+
304
+ <!-- 提示信息 -->
305
+ <HyTransition :show="!!errors[item.field]" mode="slide-left">
306
+ <view class="hy-form--item__container-warning">{{
307
+ errors[item.field]
308
+ }}</view>
309
+ </HyTransition>
310
+ <!-- 提示信息 -->
311
+ </view>
312
+ </view>
313
+ </view>
314
+ </template>
315
+
316
+ <script lang="ts">
317
+ export default {
318
+ name: 'hy-form',
319
+ options: {
320
+ addGlobalClass: true,
321
+ virtualHost: true,
322
+ styleIsolation: 'shared'
323
+ }
324
+ }
325
+ </script>
326
+
327
+ <script setup lang="ts">
328
+ import { computed, type CSSProperties, reactive, toRefs } from "vue";
329
+ import type IProps from "./typing";
330
+ import defaultProps from "./props";
331
+ import { addUnit, error } from "../../utils";
332
+ import type { FormColumnsType, RulesVo } from "../../typing";
333
+ import { FormTypeEnum } from "../../typing";
334
+
335
+ // 组件
336
+ import HyInput from "../hy-input/hy-input.vue";
337
+ import HyPicker from "../hy-picker/hy-picker.vue";
338
+ import HyUpload from "../hy-upload/hy-upload.vue";
339
+ import HyTextarea from "../hy-textarea/hy-textarea.vue";
340
+ import HySwitch from "../hy-switch/hy-switch.vue";
341
+ import HyRadio from "../hy-radio/hy-radio.vue";
342
+ import HyDatetimePicker from "../hy-datetime-picker/hy-datetime-picker.vue";
343
+ import HyAddressPicker from "../hy-address-picker/hy-address-picker.vue";
344
+ import HyTransition from "../hy-transition/hy-transition.vue";
345
+ import HyCheckButton from "../hy-check-button/hy-check-button.vue";
346
+
347
+ const props = withDefaults(defineProps<IProps>(), defaultProps);
348
+ const {
349
+ formData,
350
+ columns,
351
+ labelWidth,
352
+ labelAlign,
353
+ labelPosition,
354
+ borderBottom,
355
+ } = toRefs(props);
356
+ const emit = defineEmits(["click"]);
357
+
358
+ const labelPos = labelPosition.value === "top" ? "column" : "row";
359
+ const isInput = (type: FormTypeEnum) =>
360
+ type === FormTypeEnum.TEXT ||
361
+ type === FormTypeEnum.NUMBER ||
362
+ type === FormTypeEnum.PASSWORD;
363
+
364
+ /**
365
+ * @description 错误输入框样式
366
+ * */
367
+ const errorStyle = computed(() => {
368
+ return (err: boolean) => {
369
+ const style: CSSProperties = {};
370
+ if (err) {
371
+ style.background = "#dd6161";
372
+ }
373
+
374
+ return style;
375
+ };
376
+ });
377
+
378
+ const isRequired = computed(() => {
379
+ return (temp: any) => {
380
+ if (Array.isArray(temp)) {
381
+ return temp.some((item) => item?.required);
382
+ } else {
383
+ return temp?.required;
384
+ }
385
+ };
386
+ });
387
+
388
+ const errors: AnyObject = reactive({});
389
+
390
+ /**
391
+ * @description 标题行内样式
392
+ * */
393
+ const labelStyle = computed(() => {
394
+ return {
395
+ textAlign: labelAlign.value,
396
+ width: addUnit(labelWidth.value),
397
+ };
398
+ });
399
+
400
+ /**
401
+ * @description 错误信息校验
402
+ * */
403
+ const errorMsg = (rule: RulesVo, value: string): string => {
404
+ // 手机正则
405
+ const phoneExpression = /^1[3-9]\d{9}$/;
406
+ // 邮箱正则
407
+ const emitExpression = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
408
+ // 复杂密码正则
409
+ const passwordExpression =
410
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/;
411
+ if (rule.required && !value) {
412
+ return rule.message!;
413
+ } else if (
414
+ (rule?.min || rule?.max) &&
415
+ (value.length < rule?.min! || value.length > rule?.max!)
416
+ ) {
417
+ return rule.message!;
418
+ } else if (rule.type === "phone" && !phoneExpression.test(String(value))) {
419
+ return rule.message || "请输入正确的手机号";
420
+ } else if (rule.type === "email" && !emitExpression.test(String(value))) {
421
+ return rule.message || "请输入正确的邮箱格式";
422
+ } else if (
423
+ rule.type === "password" &&
424
+ !passwordExpression.test(String(value))
425
+ ) {
426
+ return (
427
+ rule.message || "至少8个字符串,并且包含大、小写字母、数字和特殊符号"
428
+ );
429
+ } else if (rule?.validator && !rule.validator(rule, value)) {
430
+ return rule.message || "";
431
+ } else {
432
+ return "";
433
+ }
434
+ };
435
+
436
+ /**
437
+ * @description 校验字段
438
+ * */
439
+ const validateField = (
440
+ rules: RulesVo | RulesVo[] | undefined,
441
+ value: string,
442
+ field: string,
443
+ event?: "blur" | "change",
444
+ ) => {
445
+ if (!rules) return;
446
+
447
+ let errorMessage = "";
448
+
449
+ if (Array.isArray(rules)) {
450
+ // 遍历所有规则
451
+ for (const index in rules) {
452
+ // 判断是否有change事件或者blur事件
453
+ if (event && !rules[index]?.trigger?.includes(event)) continue;
454
+ errorMessage = errorMsg(rules[index], value);
455
+ if (errorMessage) {
456
+ errors[field] = errorMessage;
457
+ return;
458
+ }
459
+ // 可以继续添加更多规则
460
+ }
461
+ } else {
462
+ // 必填校验
463
+ if (event && !rules?.trigger?.includes(event)) return;
464
+ errorMessage = errorMsg(rules, value);
465
+ }
466
+
467
+ // 自定义校验规则
468
+ // if (rules.custom) {
469
+ // const customError = rules.custom(value);
470
+ // if (customError) {
471
+ // errorMessage = customError;
472
+ // }
473
+ // }
474
+
475
+ errors[field] = errorMessage;
476
+ };
477
+
478
+ /**
479
+ * @description 提交表单
480
+ * */
481
+ const handleSubmit = () => {
482
+ return new Promise((resolve, reject) => {
483
+ let isValid = true;
484
+
485
+ // 校验所有字段
486
+ props.columns.forEach((col) => {
487
+ validateField(col.rules, formData.value[col.field], col.field);
488
+ if (errors[col.field]) {
489
+ isValid = false;
490
+ }
491
+ });
492
+
493
+ if (isValid) {
494
+ // alert("表单提交成功!");
495
+ resolve(formData.value);
496
+ } else {
497
+ reject();
498
+ error("表单校验失败,请检查输入!");
499
+ }
500
+ });
501
+ };
502
+
503
+ /**
504
+ * @description 输入值触发
505
+ * */
506
+ const handleChange = (event: string, temp: FormColumnsType) => {
507
+ if (isInput(temp.type) && temp?.input?.onChange) {
508
+ temp.input.onChange(event, temp);
509
+ }
510
+ validateField(temp.rules, event, temp.field, "change");
511
+ };
512
+ /**
513
+ * @description 输入值触发
514
+ * */
515
+ const handleBlur = (event: string, temp: FormColumnsType) => {
516
+ if (isInput(temp.type) && temp?.input?.onBlur) {
517
+ temp.input.onBlur(event, temp);
518
+ }
519
+ validateField(temp.rules, event, temp.field, "blur");
520
+ };
521
+
522
+ defineExpose({
523
+ validateField,
524
+ handleSubmit,
525
+ });
526
+ </script>
527
+
528
+ <style lang="scss" scoped>
529
+ @import "./index.scss";
530
+ .hy-form--item {
531
+ flex-direction: v-bind(labelPos);
532
+ }
533
+ </style>