hy-app 0.3.0 → 0.3.2

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 (108) hide show
  1. package/README.md +6 -3
  2. package/common/shakeService.ts +31 -29
  3. package/components/avatar.zip +0 -0
  4. package/components/hy-action-sheet/hy-action-sheet.vue +71 -46
  5. package/components/hy-address-picker/hy-address-picker.vue +94 -83
  6. package/components/hy-avatar/hy-avatar.vue +84 -85
  7. package/components/hy-back-top/hy-back-top.vue +8 -6
  8. package/components/hy-badge/hy-badge.vue +47 -46
  9. package/components/hy-button/hy-button.vue +117 -93
  10. package/components/hy-calendar/hy-calendar.vue +168 -160
  11. package/components/hy-card/hy-card.vue +50 -43
  12. package/components/hy-card/typing.d.ts +33 -32
  13. package/components/hy-cell/hy-cell.vue +73 -51
  14. package/components/hy-check-button/hy-check-button.vue +54 -47
  15. package/components/hy-checkbox/hy-checkbox.vue +97 -105
  16. package/components/hy-code-input/hy-code-input.vue +80 -89
  17. package/components/hy-config-provider/hy-config-provider.vue +20 -21
  18. package/components/hy-count-down/hy-count-down.vue +66 -67
  19. package/components/hy-count-to/hy-count-to.vue +105 -99
  20. package/components/hy-count-to/typing.d.ts +13 -12
  21. package/components/hy-datetime-picker/hy-datetime-picker.vue +261 -253
  22. package/components/hy-datetime-picker/typing.d.ts +42 -40
  23. package/components/hy-divider/hy-divider.vue +68 -73
  24. package/components/hy-dropdown/hy-dropdown.vue +20 -19
  25. package/components/hy-dropdown-item/hy-dropdown-item.vue +66 -61
  26. package/components/hy-dropdown-item/typing.d.ts +9 -9
  27. package/components/hy-empty/hy-empty.vue +42 -42
  28. package/components/hy-flex/hy-flex.vue +99 -0
  29. package/components/hy-flex/index.scss +8 -0
  30. package/components/hy-flex/typing.d.ts +23 -0
  31. package/components/hy-float-button/hy-float-button.vue +218 -210
  32. package/components/hy-folding-panel/hy-folding-panel.vue +32 -33
  33. package/components/hy-form/hy-form.vue +264 -252
  34. package/components/hy-form/typing.d.ts +4 -0
  35. package/components/hy-form-group/hy-form-group.vue +114 -183
  36. package/components/hy-form-item/hy-form-item.vue +12 -10
  37. package/components/hy-form-item/index.scss +2 -2
  38. package/components/hy-form-item/typing.d.ts +3 -6
  39. package/components/hy-grid/hy-grid.vue +44 -43
  40. package/components/hy-icon/hy-icon.vue +61 -67
  41. package/components/hy-image/hy-image.vue +112 -88
  42. package/components/hy-image/typing.d.ts +27 -23
  43. package/components/hy-input/hy-input.vue +157 -127
  44. package/components/hy-input/typing.d.ts +53 -47
  45. package/components/hy-line/hy-line.vue +26 -26
  46. package/components/hy-line-progress/hy-line-progress.vue +42 -35
  47. package/components/hy-list/hy-list.vue +76 -85
  48. package/components/hy-loading/hy-loading.vue +26 -23
  49. package/components/hy-login/TheUserLogin.vue +1 -1
  50. package/components/hy-menu/hy-menu.vue +48 -43
  51. package/components/hy-menu/typing.d.ts +18 -17
  52. package/components/hy-modal/hy-modal.vue +39 -35
  53. package/components/hy-navbar/hy-navbar.vue +25 -25
  54. package/components/hy-navbar/typing.d.ts +24 -22
  55. package/components/hy-notice-bar/hy-notice-bar.vue +26 -27
  56. package/components/hy-notify/hy-notify.vue +53 -53
  57. package/components/hy-number-step/hy-number-step.vue +134 -146
  58. package/components/hy-number-step/typing.d.ts +35 -35
  59. package/components/hy-overlay/hy-overlay.vue +23 -21
  60. package/components/hy-pagination/hy-pagination.vue +41 -36
  61. package/components/hy-picker/hy-picker.vue +184 -154
  62. package/components/hy-picker/typing.d.ts +39 -39
  63. package/components/hy-popover/hy-popover.vue +97 -77
  64. package/components/hy-popup/hy-popup.vue +107 -98
  65. package/components/hy-price/hy-price.vue +38 -34
  66. package/components/hy-qrcode/hy-qrcode.vue +50 -51
  67. package/components/hy-radio/hy-radio.vue +101 -113
  68. package/components/hy-rate/hy-rate.vue +107 -88
  69. package/components/hy-read-more/hy-read-more.vue +64 -49
  70. package/components/hy-scroll-list/hy-scroll-list.vue +45 -48
  71. package/components/hy-search/hy-search.vue +73 -66
  72. package/components/hy-search/typing.d.ts +36 -35
  73. package/components/hy-signature/hy-signature.vue +282 -240
  74. package/components/hy-slider/hy-slider.vue +195 -153
  75. package/components/hy-slider/typing.d.ts +21 -21
  76. package/components/hy-steps/hy-steps.vue +118 -90
  77. package/components/hy-steps/index.scss +31 -21
  78. package/components/hy-submit-bar/hy-submit-bar.vue +61 -70
  79. package/components/hy-subsection/hy-subsection.vue +99 -102
  80. package/components/hy-subsection/typing.d.ts +19 -19
  81. package/components/hy-swipe-action/hy-swipe-action.vue +131 -118
  82. package/components/hy-swiper/hy-swiper.vue +85 -71
  83. package/components/hy-switch/hy-switch.vue +67 -72
  84. package/components/hy-switch/typing.d.ts +21 -19
  85. package/components/hy-tabs/hy-tabs.vue +168 -113
  86. package/components/hy-tag/hy-tag.vue +90 -86
  87. package/components/hy-tag/typing.d.ts +26 -21
  88. package/components/hy-text/hy-text.vue +119 -111
  89. package/components/hy-textarea/hy-textarea.vue +100 -93
  90. package/components/hy-textarea/typing.d.ts +36 -31
  91. package/components/hy-toast/hy-toast.vue +77 -67
  92. package/components/hy-tooltip/hy-tooltip.vue +109 -91
  93. package/components/hy-transition/hy-transition.vue +62 -66
  94. package/components/hy-upload/hy-upload.vue +294 -152
  95. package/components/hy-upload/typing.d.ts +41 -36
  96. package/components/hy-warn/hy-warn.vue +34 -27
  97. package/components/hy-waterfall/hy-waterfall.vue +83 -74
  98. package/components/hy-watermark/hy-watermark.vue +134 -115
  99. package/components/index.ts +1 -1
  100. package/composables/usePopover.ts +236 -221
  101. package/composables/useQueue.ts +53 -52
  102. package/global.d.ts +1 -0
  103. package/package.json +2 -2
  104. package/store/index.ts +9 -1
  105. package/theme.scss +5 -5
  106. package/typing/index.ts +0 -1
  107. package/typing/modules/common.d.ts +0 -2
  108. package/web-types.json +1 -1
@@ -40,30 +40,30 @@
40
40
 
41
41
  <script lang="ts">
42
42
  export default {
43
- name: 'hy-folding-panel',
43
+ name: "hy-folding-panel",
44
44
  options: {
45
45
  addGlobalClass: true,
46
46
  virtualHost: true,
47
- styleIsolation: 'shared',
47
+ styleIsolation: "shared",
48
48
  },
49
- }
49
+ };
50
50
  </script>
51
51
 
52
52
  <script setup lang="ts">
53
- import { toRefs, ref, watch } from 'vue'
54
- import type { CSSProperties, PropType } from 'vue'
55
- import type { IFoldingPanel, PanelVo } from './typing'
56
- import { addUnit } from '../../utils'
57
- import { ColorConfig } from '../../config'
58
-
59
- import HyCell from '../hy-cell/hy-cell.vue'
60
- import HyLine from '../hy-line/hy-line.vue'
53
+ import { toRefs, ref, watch } from "vue";
54
+ import type { CSSProperties, PropType } from "vue";
55
+ import type { IFoldingPanel, PanelVo } from "./typing";
56
+ import { addUnit } from "../../utils";
57
+ import { ColorConfig } from "../../config";
58
+ // 组件
59
+ import HyCell from "../hy-cell/hy-cell.vue";
60
+ import HyLine from "../hy-line/hy-line.vue";
61
61
 
62
62
  /**
63
63
  * 通过折叠面板收纳内容区域。
64
64
  * @displayName hy-folding-panel
65
65
  */
66
- defineOptions({})
66
+ defineOptions({});
67
67
 
68
68
  // const props = withDefaults(defineProps<IProps>(), defaultProps);
69
69
  const props = defineProps({
@@ -110,7 +110,7 @@ const props = defineProps({
110
110
  * */
111
111
  size: {
112
112
  type: String,
113
- default: 'medium',
113
+ default: "medium",
114
114
  },
115
115
  /** 内容面板高度 */
116
116
  contentHeight: {
@@ -121,45 +121,44 @@ const props = defineProps({
121
121
  customStyle: {
122
122
  type: Object as PropType<CSSProperties>,
123
123
  },
124
- })
125
- const { list, contentHeight, accordion } = toRefs(props)
126
- const emit = defineEmits<IFoldingPanel>()
124
+ });
125
+ const emit = defineEmits<IFoldingPanel>();
127
126
 
128
- const lists = ref<PanelVo[]>([])
127
+ const lists = ref<PanelVo[]>([]);
129
128
 
130
129
  watch(
131
- () => list.value,
130
+ () => props.list,
132
131
  (newValue: PanelVo[]) => {
133
132
  lists.value = newValue.map((item) => ({
134
133
  ...item,
135
- arrowDirection: 'down',
134
+ arrowDirection: "down",
136
135
  spread: false,
137
- }))
136
+ }));
138
137
  },
139
138
  { immediate: true },
140
- )
139
+ );
141
140
 
142
141
  const clickHandler = (temp: PanelVo, index: number) => {
143
142
  // if (temp?.disabled && temp?.animating) return;
144
- lists.value = list.value.map((item, i) => {
145
- if (accordion.value) {
143
+ lists.value = props.list.map((item, i) => {
144
+ if (props.accordion) {
146
145
  // 判断是否是收起来
147
- item.spread = i === index ? !item.spread : false
146
+ item.spread = i === index ? !item.spread : false;
148
147
  } else {
149
148
  if (i === index) {
150
- item.spread = !item.spread
149
+ item.spread = !item.spread;
151
150
  }
152
151
  }
153
152
 
154
- item.arrowDirection = item.spread ? 'up' : 'down'
155
- return item
156
- })
157
- const event = temp.spread ? 'open' : 'close'
158
- emit('change', temp, index)
159
- emit(event, temp, index)
160
- }
153
+ item.arrowDirection = item.spread ? "up" : "down";
154
+ return item;
155
+ });
156
+ const event: "open" | "close" = temp.spread ? "open" : "close";
157
+ emit("change", temp, index);
158
+ emit(event, temp, index);
159
+ };
161
160
  </script>
162
161
 
163
162
  <style lang="scss" scoped>
164
- @import './index.scss';
163
+ @import "./index.scss";
165
164
  </style>
@@ -1,252 +1,264 @@
1
- <template>
2
- <view class="hy-form-simple">
3
- <slot></slot>
4
- </view>
5
- </template>
6
-
7
- <script lang="ts">
8
- export default {
9
- name: 'hy-form-simple',
10
- options: {
11
- addGlobalClass: true,
12
- virtualHost: true,
13
- styleIsolation: 'shared',
14
- },
15
- }
16
- </script>
17
-
18
- <script setup lang="ts">
19
- import { PropType, provide, reactive, ref, toRefs } from 'vue'
20
- import type { FormItemRule } from './typing'
21
-
22
- /**
23
- * 表单组件父组件,需要搭配hy-form-item
24
- * @displayName hy-form
25
- */
26
- defineOptions({})
27
-
28
- const props = defineProps({
29
- /** 表单数据对象 */
30
- model: Object as PropType<AnyObject>,
31
- /** 表单校验规则 */
32
- rules: Object as unknown as PropType<FormItemRule>,
33
- /** label标签的宽度,单位rpx */
34
- labelWidth: {
35
- type: String,
36
- default: 'auto',
37
- },
38
- /**
39
- * 标签位置
40
- * @values left,top
41
- * */
42
- labelPosition: {
43
- type: String,
44
- default: 'left',
45
- },
46
- /**
47
- * 标签位置
48
- * @values left,center,right
49
- * */
50
- labelAlign: {
51
- type: String,
52
- default: 'left',
53
- },
54
- })
55
-
56
- const emit = defineEmits<{
57
- submit: [data: Record<string, any>]
58
- validate: [valid: boolean, errors: Record<string, string>]
59
- }>()
60
-
61
- // 表单数据
62
- const formData = reactive(props.model || {})
63
- const formItems = ref<any[]>([])
64
- const errors = reactive<Record<string, string>>({})
65
-
66
- // 表单上下文
67
- const formContext = {
68
- formData,
69
- errors,
70
- rules: toRefs(props).rules,
71
- labelWidth: toRefs(props).labelWidth,
72
- labelPosition: toRefs(props).labelPosition,
73
- labelAlign: toRefs(props).labelAlign,
74
- addFormItem: (item: any) => {
75
- formItems.value.push(item)
76
- },
77
- removeFormItem: (item: any) => {
78
- const index = formItems.value.indexOf(item)
79
- if (index > -1) {
80
- formItems.value.splice(index, 1)
81
- }
82
- },
83
- validateField: (field: string, value: any, trigger?: 'blur' | 'change') => {
84
- const fieldRules = props.rules?.[field]
85
- if (!fieldRules) return true
86
-
87
- const rules = Array.isArray(fieldRules) ? fieldRules : [fieldRules]
88
- let isValid = true
89
- let errorMessage = ''
90
-
91
- for (const rule of rules) {
92
- // 检查触发时机
93
- if (
94
- (trigger && !rule.trigger) ||
95
- (trigger && rule.trigger && !rule.trigger.includes(trigger))
96
- ) {
97
- continue
98
- }
99
-
100
- // 必填校验
101
- if (rule.required && (!value || value === '')) {
102
- errorMessage = rule.message || `${field} 是必填项`
103
- isValid = false
104
- break
105
- }
106
-
107
- // 长度校验
108
- if (rule.min && String(value).length < rule.min) {
109
- errorMessage = rule.message || `${field} 长度不能少于 ${rule.min} 个字符`
110
- isValid = false
111
- break
112
- }
113
-
114
- if (rule.max && String(value).length > rule.max) {
115
- errorMessage = rule.message || `${field} 长度不能超过 ${rule.max} 个字符`
116
- isValid = false
117
- break
118
- }
119
-
120
- // 类型校验
121
- if (rule.type === 'phone') {
122
- const phoneRegex = /^1[3-9]\d{9}$/
123
- if (!phoneRegex.test(String(value))) {
124
- errorMessage = rule.message || '请输入正确的手机号'
125
- isValid = false
126
- break
127
- }
128
- }
129
-
130
- if (rule.type === 'email') {
131
- const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/
132
- if (!emailRegex.test(String(value))) {
133
- errorMessage = rule.message || '请输入正确的邮箱格式'
134
- isValid = false
135
- break
136
- }
137
- }
138
-
139
- if (rule.type === 'password') {
140
- const passwordRegex =
141
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/
142
- if (!passwordRegex.test(String(value))) {
143
- errorMessage = rule.message || '密码至少8个字符,包含大小写字母、数字和特殊符号'
144
- isValid = false
145
- break
146
- }
147
- }
148
-
149
- // 自定义校验
150
- if (rule.validator) {
151
- const result = rule.validator(value)
152
- if (result === false || typeof result === 'string') {
153
- errorMessage = typeof result === 'string' ? result : rule.message || `${field} 校验失败`
154
- isValid = false
155
- break
156
- }
157
- }
158
- }
159
-
160
- if (isValid) {
161
- delete errors[field]
162
- } else {
163
- errors[field] = errorMessage
164
- }
165
-
166
- return isValid
167
- },
168
- setFieldValue: (field: string, value: any) => {
169
- formData[field] = value
170
- },
171
- getFieldValue: (field: string) => {
172
- return formData[field]
173
- },
174
- }
175
-
176
- // 提供表单上下文给子组件
177
- provide('formContext', formContext)
178
-
179
- // 验证所有字段
180
- const validate = () => {
181
- return new Promise((resolve, reject) => {
182
- let isValid = true
183
- const allErrors: Record<string, string> = {}
184
-
185
- formItems.value.forEach((item) => {
186
- const field = item.props?.prop
187
- if (field) {
188
- const value = formData[field]
189
- const fieldValid = formContext.validateField(field, value)
190
- if (!fieldValid) {
191
- isValid = false
192
- allErrors[field] = errors[field]
193
- }
194
- }
195
- })
196
-
197
- emit('validate', isValid, allErrors)
198
- if (isValid) {
199
- resolve(isValid)
200
- } else {
201
- reject(allErrors)
202
- }
203
- })
204
- }
205
-
206
- // 重置表单
207
- const resetFields = () => {
208
- Object.keys(formData).forEach((key) => {
209
- formData[key] = undefined
210
- })
211
- Object.keys(errors).forEach((key) => {
212
- delete errors[key]
213
- })
214
- }
215
-
216
- // 清除验证
217
- const clearValidate = (fields?: string[]) => {
218
- if (fields) {
219
- fields.forEach((field) => {
220
- delete errors[field]
221
- })
222
- } else {
223
- Object.keys(errors).forEach((key) => {
224
- delete errors[key]
225
- })
226
- }
227
- }
228
-
229
- // 提交表单
230
- const submit = async () => {
231
- if (await validate()) {
232
- emit('submit', { ...formData })
233
- return formData
234
- }
235
- return false
236
- }
237
-
238
- defineExpose({
239
- validate,
240
- resetFields,
241
- clearValidate,
242
- submit,
243
- formData,
244
- errors,
245
- })
246
- </script>
247
-
248
- <style lang="scss" scoped>
249
- .hy-form-simple {
250
- width: 100%;
251
- }
252
- </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 } from "vue";
20
+ import type { PropType } from "vue";
21
+ import type { FormItemRule } from "./typing";
22
+ import { clearVal, isArray } from "../../utils";
23
+
24
+ /**
25
+ * 表单组件父组件,需要搭配hy-form-item
26
+ * @displayName hy-form
27
+ */
28
+ defineOptions({});
29
+
30
+ const props = defineProps({
31
+ /** 表单数据对象 */
32
+ model: Object as PropType<AnyObject>,
33
+ /** 表单校验规则 */
34
+ rules: Object as PropType<FormItemRule>,
35
+ /** 表单底部边框 */
36
+ border: {
37
+ type: Boolean,
38
+ default: false,
39
+ },
40
+ /** label标签的宽度,单位rpx */
41
+ labelWidth: {
42
+ type: String,
43
+ default: "auto",
44
+ },
45
+ /**
46
+ * 标签位置
47
+ * @values left,top
48
+ * */
49
+ labelPosition: {
50
+ type: String,
51
+ default: "left",
52
+ },
53
+ /**
54
+ * 标签位置
55
+ * @values left,center,right
56
+ * */
57
+ labelAlign: {
58
+ type: String,
59
+ default: "left",
60
+ },
61
+ });
62
+
63
+ const emit = defineEmits<{
64
+ submit: [data: Record<string, any>];
65
+ validate: [valid: boolean, errors: Record<string, string>];
66
+ }>();
67
+
68
+ // 表单数据
69
+ const formData = reactive(props.model || {});
70
+ const formItems = ref<any[]>([]);
71
+ const errors = reactive<Record<string, string>>({});
72
+
73
+ // 表单上下文
74
+ const formContext = {
75
+ formData,
76
+ errors,
77
+ rules: props.rules,
78
+ border: props.border,
79
+ labelWidth: props.labelWidth,
80
+ labelPosition: props.labelPosition,
81
+ labelAlign: props.labelAlign,
82
+ addFormItem: (item: any) => {
83
+ formItems.value.push(item);
84
+ },
85
+ removeFormItem: (item: any) => {
86
+ const index = formItems.value.indexOf(item);
87
+ if (index > -1) {
88
+ formItems.value.splice(index, 1);
89
+ }
90
+ },
91
+ validateField: (field: string, value: any, trigger?: "blur" | "change") => {
92
+ const fieldRules = props.rules?.[field];
93
+ if (!fieldRules) return true;
94
+
95
+ const rules = isArray(fieldRules) ? fieldRules : [fieldRules];
96
+ let isValid = true;
97
+ let errorMessage = "";
98
+
99
+ for (const rule of rules) {
100
+ // 检查触发时机
101
+ if (
102
+ (trigger && !rule.trigger) ||
103
+ (trigger && rule.trigger && !rule.trigger.includes(trigger))
104
+ ) {
105
+ continue;
106
+ }
107
+
108
+ // 必填校验
109
+ if (rule.required && (!value || value === "")) {
110
+ errorMessage = rule.message || `${field} 是必填项`;
111
+ isValid = false;
112
+ break;
113
+ }
114
+
115
+ // 长度校验
116
+ if (rule.min && String(value).length < rule.min) {
117
+ errorMessage =
118
+ rule.message || `${field} 长度不能少于 ${rule.min} 个字符`;
119
+ isValid = false;
120
+ break;
121
+ }
122
+
123
+ if (rule.max && String(value).length > rule.max) {
124
+ errorMessage =
125
+ rule.message || `${field} 长度不能超过 ${rule.max} 个字符`;
126
+ isValid = false;
127
+ break;
128
+ }
129
+
130
+ // 类型校验
131
+ if (rule.type === "phone") {
132
+ const phoneRegex = /^1[3-9]\d{9}$/;
133
+ if (!phoneRegex.test(String(value))) {
134
+ errorMessage = rule.message || "请输入正确的手机号";
135
+ isValid = false;
136
+ break;
137
+ }
138
+ }
139
+
140
+ if (rule.type === "email") {
141
+ const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
142
+ if (!emailRegex.test(String(value))) {
143
+ errorMessage = rule.message || "请输入正确的邮箱格式";
144
+ isValid = false;
145
+ break;
146
+ }
147
+ }
148
+
149
+ if (rule.type === "password") {
150
+ const passwordRegex =
151
+ /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{8,}$/;
152
+ if (!passwordRegex.test(String(value))) {
153
+ errorMessage =
154
+ rule.message || "密码至少8个字符,包含大小写字母、数字和特殊符号";
155
+ isValid = false;
156
+ break;
157
+ }
158
+ }
159
+
160
+ // 自定义校验
161
+ if (rule.validator) {
162
+ const result = rule.validator(value);
163
+ if (result === false || typeof result === "string") {
164
+ errorMessage =
165
+ typeof result === "string"
166
+ ? result
167
+ : rule.message || `${field} 校验失败`;
168
+ isValid = false;
169
+ break;
170
+ }
171
+ }
172
+ }
173
+
174
+ if (isValid) {
175
+ delete errors[field];
176
+ } else {
177
+ errors[field] = errorMessage;
178
+ }
179
+
180
+ return isValid;
181
+ },
182
+ setFieldValue: (field: string, value: any) => {
183
+ formData[field] = value;
184
+ },
185
+ getFieldValue: (field: string) => {
186
+ return formData[field];
187
+ },
188
+ };
189
+
190
+ // 提供表单上下文给子组件
191
+ provide("formContext", formContext);
192
+
193
+ // 验证所有字段
194
+ const validate = () => {
195
+ return new Promise((resolve, reject) => {
196
+ let isValid = true;
197
+ const allErrors: Record<string, string> = {};
198
+
199
+ formItems.value.forEach((item) => {
200
+ const field = item.props?.prop;
201
+ if (field) {
202
+ const value = formData[field];
203
+ const fieldValid = formContext.validateField(field, value);
204
+ if (!fieldValid) {
205
+ isValid = false;
206
+ allErrors[field] = errors[field];
207
+ }
208
+ }
209
+ });
210
+
211
+ emit("validate", isValid, allErrors);
212
+ if (isValid) {
213
+ resolve(isValid);
214
+ } else {
215
+ reject(allErrors);
216
+ }
217
+ });
218
+ };
219
+
220
+ // 重置表单
221
+ const resetFields = () => {
222
+ clearVal(formData);
223
+ Object.keys(errors).forEach((key) => {
224
+ delete errors[key];
225
+ });
226
+ };
227
+
228
+ // 清除验证
229
+ const clearValidate = (fields?: string[]) => {
230
+ if (fields) {
231
+ fields.forEach((field) => {
232
+ delete errors[field];
233
+ });
234
+ } else {
235
+ Object.keys(errors).forEach((key) => {
236
+ delete errors[key];
237
+ });
238
+ }
239
+ };
240
+
241
+ // 提交表单
242
+ const submit = async () => {
243
+ if (await validate()) {
244
+ emit("submit", { ...formData });
245
+ return formData;
246
+ }
247
+ return false;
248
+ };
249
+
250
+ defineExpose({
251
+ validate,
252
+ resetFields,
253
+ clearValidate,
254
+ submit,
255
+ formData,
256
+ errors,
257
+ });
258
+ </script>
259
+
260
+ <style lang="scss" scoped>
261
+ .hy-form {
262
+ width: 100%;
263
+ }
264
+ </style>
@@ -54,4 +54,8 @@ export default interface HyFormSimpleProps {
54
54
  * 标签对齐方式
55
55
  */
56
56
  labelAlign?: 'left' | 'center' | 'right'
57
+ /**
58
+ * 表单列底部边框
59
+ * */
60
+ border: boolean
57
61
  }