uview-pro 0.6.18 → 0.6.19

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 (278) hide show
  1. package/changelog.md +1778 -1768
  2. package/components/u-action-sheet/types.ts +52 -52
  3. package/components/u-action-sheet/u-action-sheet.vue +196 -196
  4. package/components/u-action-sheet-item/types.ts +35 -35
  5. package/components/u-action-sheet-item/u-action-sheet-item.vue +85 -85
  6. package/components/u-alert-tips/types.ts +49 -49
  7. package/components/u-alert-tips/u-alert-tips.vue +239 -239
  8. package/components/u-avatar/types.ts +44 -44
  9. package/components/u-avatar/u-avatar.vue +218 -218
  10. package/components/u-avatar-cropper/types.ts +23 -23
  11. package/components/u-avatar-cropper/u-avatar-cropper.vue +299 -299
  12. package/components/u-avatar-cropper/weCropper.d.ts +62 -62
  13. package/components/u-avatar-cropper/weCropper.js +1281 -1281
  14. package/components/u-avatar-cropper/weCropper.ts +1276 -1276
  15. package/components/u-back-top/types.ts +44 -44
  16. package/components/u-back-top/u-back-top.vue +141 -141
  17. package/components/u-badge/types.ts +46 -46
  18. package/components/u-badge/u-badge.vue +153 -153
  19. package/components/u-button/types.ts +76 -76
  20. package/components/u-button/u-button.vue +608 -608
  21. package/components/u-calendar/types.ts +126 -126
  22. package/components/u-calendar/u-calendar.vue +1243 -1243
  23. package/components/u-car-keyboard/types.ts +22 -22
  24. package/components/u-car-keyboard/u-car-keyboard.vue +263 -263
  25. package/components/u-card/types.ts +69 -69
  26. package/components/u-card/u-card.vue +210 -210
  27. package/components/u-cell-group/types.ts +27 -27
  28. package/components/u-cell-group/u-cell-group.vue +60 -60
  29. package/components/u-cell-item/types.ts +64 -64
  30. package/components/u-cell-item/u-cell-item.vue +221 -221
  31. package/components/u-checkbox/types.ts +45 -45
  32. package/components/u-checkbox/u-checkbox.vue +363 -363
  33. package/components/u-checkbox-group/types.ts +45 -45
  34. package/components/u-checkbox-group/u-checkbox-group.vue +172 -172
  35. package/components/u-circle-progress/types.ts +63 -63
  36. package/components/u-circle-progress/u-circle-progress.vue +299 -299
  37. package/components/u-city-select/types.ts +30 -30
  38. package/components/u-city-select/u-city-select.vue +281 -281
  39. package/components/u-col/types.ts +40 -40
  40. package/components/u-col/u-col.vue +142 -142
  41. package/components/u-collapse/types.ts +41 -41
  42. package/components/u-collapse/u-collapse.vue +190 -190
  43. package/components/u-collapse-item/types.ts +35 -35
  44. package/components/u-collapse-item/u-collapse-item.vue +291 -291
  45. package/components/u-column-notice/types.ts +58 -58
  46. package/components/u-column-notice/u-column-notice.vue +223 -223
  47. package/components/u-config-provider/types.ts +53 -53
  48. package/components/u-config-provider/u-config-provider.vue +185 -185
  49. package/components/u-count-down/types.ts +52 -52
  50. package/components/u-count-down/u-count-down.vue +288 -288
  51. package/components/u-count-to/types.ts +42 -42
  52. package/components/u-count-to/u-count-to.vue +266 -266
  53. package/components/u-divider/types.ts +41 -41
  54. package/components/u-divider/u-divider.vue +146 -146
  55. package/components/u-dropdown/types.ts +67 -67
  56. package/components/u-dropdown/u-dropdown.vue +436 -436
  57. package/components/u-dropdown-item/types.ts +40 -40
  58. package/components/u-dropdown-item/u-dropdown-item.vue +124 -124
  59. package/components/u-empty/types.ts +46 -46
  60. package/components/u-empty/u-empty.vue +109 -109
  61. package/components/u-fab/types.ts +83 -83
  62. package/components/u-fab/u-fab.vue +330 -330
  63. package/components/u-field/types.ts +81 -81
  64. package/components/u-field/u-field.vue +398 -398
  65. package/components/u-form/types.ts +39 -39
  66. package/components/u-form/u-form.vue +175 -135
  67. package/components/u-form-item/types.ts +95 -95
  68. package/components/u-form-item/u-form-item.vue +563 -563
  69. package/components/u-full-screen/types.ts +26 -26
  70. package/components/u-full-screen/u-full-screen.vue +100 -100
  71. package/components/u-gap/types.ts +28 -28
  72. package/components/u-gap/u-gap.vue +50 -50
  73. package/components/u-grid/types.ts +29 -29
  74. package/components/u-grid/u-grid.vue +91 -91
  75. package/components/u-grid-item/types.ts +24 -24
  76. package/components/u-grid-item/u-grid-item.vue +130 -130
  77. package/components/u-icon/types.ts +69 -69
  78. package/components/u-icon/u-icon.vue +301 -301
  79. package/components/u-image/types.ts +61 -61
  80. package/components/u-image/u-image.vue +245 -245
  81. package/components/u-index-anchor/types.ts +24 -24
  82. package/components/u-index-anchor/u-index-anchor.vue +120 -120
  83. package/components/u-index-list/types.ts +44 -44
  84. package/components/u-index-list/u-index-list.vue +346 -346
  85. package/components/u-input/types.ts +165 -165
  86. package/components/u-input/u-input.vue +453 -453
  87. package/components/u-keyboard/types.ts +40 -40
  88. package/components/u-keyboard/u-keyboard.vue +201 -201
  89. package/components/u-lazy-load/types.ts +37 -37
  90. package/components/u-lazy-load/u-lazy-load.vue +246 -246
  91. package/components/u-line/types.ts +44 -44
  92. package/components/u-line/u-line.vue +68 -68
  93. package/components/u-line-progress/types.ts +58 -58
  94. package/components/u-line-progress/u-line-progress.vue +131 -131
  95. package/components/u-link/types.ts +54 -54
  96. package/components/u-link/u-link.vue +100 -100
  97. package/components/u-loading/types.ts +45 -45
  98. package/components/u-loading/u-loading.vue +106 -106
  99. package/components/u-loading-popup/types.ts +26 -26
  100. package/components/u-loading-popup/u-loading-popup.vue +254 -254
  101. package/components/u-loadmore/types.ts +88 -88
  102. package/components/u-loadmore/u-loadmore.vue +170 -170
  103. package/components/u-mask/types.ts +48 -48
  104. package/components/u-mask/u-mask.vue +113 -113
  105. package/components/u-message-input/types.ts +90 -90
  106. package/components/u-message-input/u-message-input.vue +283 -283
  107. package/components/u-modal/service.ts +48 -48
  108. package/components/u-modal/types.ts +139 -139
  109. package/components/u-modal/u-modal.vue +465 -465
  110. package/components/u-navbar/types.ts +104 -104
  111. package/components/u-navbar/u-navbar.vue +268 -268
  112. package/components/u-no-network/image.ts +2 -2
  113. package/components/u-no-network/types.ts +29 -29
  114. package/components/u-no-network/u-no-network.vue +313 -313
  115. package/components/u-notice-bar/types.ts +111 -111
  116. package/components/u-notice-bar/u-notice-bar.vue +191 -191
  117. package/components/u-number-box/types.ts +42 -42
  118. package/components/u-number-box/u-number-box.vue +322 -322
  119. package/components/u-number-keyboard/types.ts +26 -26
  120. package/components/u-number-keyboard/u-number-keyboard.vue +189 -189
  121. package/components/u-pagination/types.ts +38 -38
  122. package/components/u-pagination/u-pagination.vue +102 -102
  123. package/components/u-picker/types.ts +140 -140
  124. package/components/u-picker/u-picker.vue +854 -854
  125. package/components/u-popup/types.ts +64 -64
  126. package/components/u-popup/u-popup.vue +427 -427
  127. package/components/u-radio/types.ts +39 -39
  128. package/components/u-radio/u-radio.vue +349 -349
  129. package/components/u-radio-group/types.ts +40 -40
  130. package/components/u-radio-group/u-radio-group.vue +96 -96
  131. package/components/u-rate/types.ts +50 -50
  132. package/components/u-rate/u-rate.vue +250 -250
  133. package/components/u-read-more/types.ts +46 -46
  134. package/components/u-read-more/u-read-more.vue +182 -182
  135. package/components/u-root-portal/u-root-portal.vue +55 -55
  136. package/components/u-row/types.ts +30 -30
  137. package/components/u-row/u-row.vue +105 -105
  138. package/components/u-row-notice/types.ts +49 -49
  139. package/components/u-row-notice/u-row-notice.vue +257 -257
  140. package/components/u-safe-bottom/types.ts +19 -19
  141. package/components/u-safe-bottom/u-safe-bottom.vue +55 -55
  142. package/components/u-search/u-search.vue +297 -297
  143. package/components/u-section/types.ts +42 -42
  144. package/components/u-section/u-section.vue +156 -156
  145. package/components/u-select/types.ts +56 -56
  146. package/components/u-select/u-select.vue +447 -447
  147. package/components/u-skeleton/types.ts +89 -89
  148. package/components/u-skeleton/u-skeleton.vue +253 -253
  149. package/components/u-slider/types.ts +55 -55
  150. package/components/u-slider/u-slider.vue +422 -422
  151. package/components/u-status-bar/types.ts +21 -21
  152. package/components/u-status-bar/u-status-bar.vue +64 -64
  153. package/components/u-step/types.ts +37 -37
  154. package/components/u-step/u-step.vue +292 -292
  155. package/components/u-steps/types.ts +39 -39
  156. package/components/u-steps/u-steps.vue +105 -105
  157. package/components/u-sticky/types.ts +33 -33
  158. package/components/u-sticky/u-sticky.vue +177 -177
  159. package/components/u-subsection/types.ts +46 -46
  160. package/components/u-subsection/u-subsection.vue +358 -358
  161. package/components/u-swipe-action/types.ts +60 -60
  162. package/components/u-swipe-action/u-swipe-action.vue +276 -276
  163. package/components/u-swiper/types.ts +57 -57
  164. package/components/u-swiper/u-swiper.vue +308 -308
  165. package/components/u-switch/types.ts +39 -39
  166. package/components/u-switch/u-switch.vue +212 -212
  167. package/components/u-tabbar/types.ts +57 -57
  168. package/components/u-tabbar/u-tabbar.vue +480 -480
  169. package/components/u-table/types.ts +35 -35
  170. package/components/u-table/u-table.vue +67 -67
  171. package/components/u-tabs/types.ts +64 -64
  172. package/components/u-tabs/u-tabs.vue +328 -328
  173. package/components/u-tabs-swiper/types.ts +64 -64
  174. package/components/u-tabs-swiper/u-tabs-swiper.vue +407 -407
  175. package/components/u-tag/types.ts +47 -47
  176. package/components/u-tag/u-tag.vue +276 -276
  177. package/components/u-td/types.ts +22 -22
  178. package/components/u-td/u-td.vue +65 -65
  179. package/components/u-text/types.ts +80 -80
  180. package/components/u-text/u-text.vue +355 -355
  181. package/components/u-textarea/types.ts +105 -105
  182. package/components/u-textarea/u-textarea.vue +450 -450
  183. package/components/u-th/types.ts +22 -22
  184. package/components/u-th/u-th.vue +63 -63
  185. package/components/u-time-line/u-time-line.vue +64 -64
  186. package/components/u-time-line-item/types.ts +24 -24
  187. package/components/u-time-line-item/u-time-line-item.vue +89 -89
  188. package/components/u-toast/service.ts +40 -40
  189. package/components/u-toast/types.ts +54 -54
  190. package/components/u-toast/u-toast.vue +385 -385
  191. package/components/u-top-tips/types.ts +25 -25
  192. package/components/u-top-tips/u-top-tips.vue +130 -130
  193. package/components/u-tr/types.ts +19 -19
  194. package/components/u-tr/u-tr.vue +39 -39
  195. package/components/u-transition/types.ts +43 -43
  196. package/components/u-transition/u-transition.vue +437 -437
  197. package/components/u-upload/types.ts +171 -171
  198. package/components/u-upload/u-upload.vue +1605 -1605
  199. package/components/u-verification-code/types.ts +32 -32
  200. package/components/u-verification-code/u-verification-code.vue +183 -183
  201. package/components/u-waterfall/types.ts +26 -26
  202. package/components/u-waterfall/u-waterfall.vue +188 -188
  203. package/iconfont.css +913 -913
  204. package/index.scss +26 -26
  205. package/libs/config/color.ts +9 -9
  206. package/libs/config/config.ts +59 -59
  207. package/libs/config/theme-tokens.ts +102 -102
  208. package/libs/config/zIndex.ts +32 -32
  209. package/libs/css/color.scss +155 -155
  210. package/libs/css/common.scss +183 -183
  211. package/libs/css/style.components.scss +16 -16
  212. package/libs/css/style.h5.scss +8 -8
  213. package/libs/css/style.mp.scss +68 -68
  214. package/libs/css/style.nvue.scss +15 -15
  215. package/libs/css/style.theme.scss +56 -56
  216. package/libs/css/style.vue.scss +188 -188
  217. package/libs/function/$parent.ts +24 -24
  218. package/libs/function/addUnit.ts +50 -50
  219. package/libs/function/clipboard.ts +73 -73
  220. package/libs/function/color.ts +28 -28
  221. package/libs/function/colorGradient.ts +139 -139
  222. package/libs/function/debounce.ts +28 -28
  223. package/libs/function/deepClone.ts +39 -39
  224. package/libs/function/deepMerge.ts +35 -35
  225. package/libs/function/getParent.ts +63 -63
  226. package/libs/function/getRect.ts +26 -26
  227. package/libs/function/guid.ts +42 -42
  228. package/libs/function/md5.ts +403 -403
  229. package/libs/function/parent.ts +21 -21
  230. package/libs/function/queryParams.ts +64 -64
  231. package/libs/function/random.ts +16 -16
  232. package/libs/function/randomArray.ts +11 -11
  233. package/libs/function/route.ts +118 -118
  234. package/libs/function/styleUtils.ts +83 -83
  235. package/libs/function/sys.ts +15 -15
  236. package/libs/function/test.ts +289 -289
  237. package/libs/function/throttle.ts +31 -31
  238. package/libs/function/timeFormat.ts +55 -55
  239. package/libs/function/timeFrom.ts +48 -48
  240. package/libs/function/toast.ts +14 -14
  241. package/libs/function/trim.ts +21 -21
  242. package/libs/function/type2icon.ts +38 -38
  243. package/libs/hooks/index.ts +10 -10
  244. package/libs/hooks/useColor.ts +61 -61
  245. package/libs/hooks/useCompRelation.ts +424 -424
  246. package/libs/hooks/useDebounce.ts +15 -15
  247. package/libs/hooks/useEmitter.ts +79 -79
  248. package/libs/hooks/useLocale.ts +34 -34
  249. package/libs/hooks/useModal.ts +131 -131
  250. package/libs/hooks/useRect.ts +40 -40
  251. package/libs/hooks/useRouter.ts +173 -173
  252. package/libs/hooks/useTheme.ts +173 -173
  253. package/libs/hooks/useThrottle.ts +16 -16
  254. package/libs/hooks/useToast.ts +115 -115
  255. package/libs/index.ts +349 -349
  256. package/libs/request/auto-http.ts +76 -76
  257. package/libs/request/index.ts +301 -301
  258. package/libs/store/index.ts +88 -88
  259. package/libs/util/async-validator.d.ts +75 -75
  260. package/libs/util/async-validator.js +1 -1
  261. package/libs/util/calendar.d.ts +57 -57
  262. package/libs/util/canvas-2d.ts +182 -182
  263. package/libs/util/config-provider.ts +804 -804
  264. package/libs/util/emitter.ts +112 -112
  265. package/libs/util/mitt.ts +118 -118
  266. package/libs/util/system-theme.ts +25 -25
  267. package/locale/index.ts +2 -2
  268. package/locale/lang/en-US.ts +136 -136
  269. package/locale/lang/zh-CN.ts +136 -136
  270. package/package.json +1 -1
  271. package/plugins/root/page.ts +77 -77
  272. package/plugins/root/root.ts +53 -53
  273. package/theme.scss +149 -149
  274. package/types/components.d.ts +105 -105
  275. package/types/global.d.ts +470 -470
  276. package/types/ignore-errors.d.ts +30 -30
  277. package/types/index.d.ts +15 -15
  278. package/types/uni-app.d.ts +63 -63
@@ -1,563 +1,563 @@
1
- <template>
2
- <view
3
- class="u-form-item"
4
- :class="[
5
- {
6
- 'u-border-bottom': elBorderBottom,
7
- 'u-form-item__border-bottom--error': validateState === 'error' && showError('border-bottom'),
8
- 'u-form-item__border--error': validateState === 'error' && showError('border')
9
- },
10
- customClass
11
- ]"
12
- :style="$u.toStyle(customStyle)"
13
- >
14
- <view
15
- class="u-form-item__body"
16
- :style="{
17
- flexDirection: elLabelPosition == 'left' ? 'row' : 'column'
18
- }"
19
- >
20
- <!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" -->
21
- <view
22
- class="u-form-item--left"
23
- :style="{
24
- width: uLabelWidth,
25
- flex: `0 0 ${uLabelWidth}`,
26
- marginBottom: elLabelPosition == 'left' ? 0 : '10rpx'
27
- }"
28
- >
29
- <!-- 为了块对齐 -->
30
- <view
31
- v-if="required || leftIcon || label || $slots.label || $slots.leftIcon"
32
- class="u-form-item--left__content"
33
- >
34
- <!-- nvue不支持伪元素before -->
35
- <text v-if="required" class="u-form-item--left__content--required">*</text>
36
- <view class="u-form-item--left__content__icon" v-if="leftIcon || $slots.leftIcon">
37
- <slot name="leftIcon">
38
- <u-icon
39
- :name="leftIcon"
40
- :size="currentIconSize"
41
- :custom-prefix="leftIconPrefix"
42
- :custom-style="leftIconStyle"
43
- ></u-icon>
44
- </slot>
45
- </view>
46
- <view
47
- class="u-form-item--left__content__label"
48
- :style="[
49
- elLabelStyle,
50
- { 'font-size': actualFontSize },
51
- {
52
- 'justify-content':
53
- elLabelAlign == 'left'
54
- ? 'flex-start'
55
- : elLabelAlign == 'center'
56
- ? 'center'
57
- : 'flex-end'
58
- }
59
- ]"
60
- >
61
- <slot name="label">
62
- {{ label }}
63
- </slot>
64
- </view>
65
- </view>
66
- </view>
67
- <view class="u-form-item--right u-flex">
68
- <view class="u-form-item--right__content">
69
- <view class="u-form-item--right__content__slot">
70
- <slot />
71
- </view>
72
- <view
73
- class="u-form-item--right__content__icon u-flex"
74
- v-if="$slots.right || $slots.rightIcon || rightIcon"
75
- >
76
- <slot name="rightIcon">
77
- <u-icon
78
- v-if="rightIcon"
79
- :name="rightIcon"
80
- :size="currentIconSize"
81
- :custom-prefix="rightIconPrefix"
82
- :custom-style="rightIconStyle"
83
- ></u-icon>
84
- </slot>
85
- <slot name="right" />
86
- </view>
87
- </view>
88
- </view>
89
- </view>
90
- <view
91
- class="u-form-item__message"
92
- v-if="validateState === 'error' && showError('message')"
93
- :style="{
94
- paddingLeft: elLabelPosition == 'left' && label ? $u.addUnit(elLabelWidth) : '0',
95
- fontSize: $u.addUnit(currentSizeConfig.messageSize),
96
- lineHeight: $u.addUnit(currentSizeConfig.messageSize)
97
- }"
98
- >
99
- {{ validateMessage }}
100
- </view>
101
- </view>
102
- </template>
103
-
104
- <script lang="ts">
105
- export default {
106
- name: 'u-form-item',
107
- options: {
108
- addGlobalClass: true,
109
- // #ifndef MP-TOUTIAO
110
- virtualHost: true,
111
- // #endif
112
- styleIsolation: 'shared'
113
- }
114
- };
115
- </script>
116
-
117
- <script setup lang="ts">
118
- import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick, useSlots } from 'vue';
119
- import { $u, useChildren, useParent } from '../..';
120
- // @ts-ignore
121
- import schema from '../../libs/util/async-validator';
122
- import { FormItemProps } from './types';
123
- import type { SizeType } from '../../types/global';
124
- import uIcon from '../u-icon/u-icon.vue';
125
- // 去除警告信息
126
- schema.warning = function () {};
127
-
128
- /**
129
- * form-item 表单item
130
- * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
131
- * @tutorial https://uviewpro.cn/zh/components/form.html
132
- * @property {String} label 左侧提示文字
133
- * @property {Object} prop 表单域model对象的属性名,在使用 validate、resetFields 方法的情况下,该属性是必填的
134
- * @property {Boolean} border-bottom 是否显示表单域的下划线边框
135
- * @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
136
- * @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
137
- * @property {Object} label-style label的样式,对象形式
138
- * @property {String} label-align label的对齐方式
139
- * @property {String} right-icon 右侧自定义字体图标(限uView内置图标)或图片地址
140
- * @property {String} left-icon 左侧自定义字体图标(限uView内置图标)或图片地址
141
- * @property {Object} left-icon-style 左侧图标的样式,对象形式
142
- * @property {Object} right-icon-style 右侧图标的样式,对象形式
143
- * @property {Boolean} required 是否显示左边的"*"号,这里仅起展示作用,如需校验必填,请通过rules配置必填规则(默认false)
144
- * @example <u-form-item label="姓名"><u-input v-model="form.name" /></u-form-item>
145
- */
146
-
147
- const props = defineProps(FormItemProps);
148
-
149
- // 插槽
150
- const $slots = useSlots();
151
-
152
- const { broadcast } = useParent('u-form-item');
153
- const { parentExposed } = useChildren('u-form-item', 'u-form');
154
-
155
- // 组件状态
156
- const initialValue = ref(''); // 存储初始值,用于重置
157
- const validateState = ref(''); // 校验状态 success/error/validating
158
- const validateMessage = ref(''); // 校验失败的提示语
159
- const errorType = ref<string[]>(['message']); // 错误提示方式,默认 message
160
- const fieldValue = ref(''); // 当前表单项的值
161
- const parentData = ref({
162
- borderBottom: true, // 父表单下划线边框
163
- labelWidth: 90, // 父表单 label 宽度
164
- labelPosition: 'left', // 父表单 label 位置
165
- labelStyle: {}, // 父表单 label 样式
166
- labelAlign: 'left' // 父表单 label 对齐
167
- });
168
-
169
- // 显示错误提示
170
- // errorType: ['message', 'toast', 'border-bottom', 'none']
171
- const showError = computed(() => (type: string) => {
172
- // 如果errorType数组中含有none,或者toast提示类型
173
- if (errorType.value.indexOf('none') >= 0) return false;
174
- else if (errorType.value.indexOf(type) >= 0) return true;
175
- else return false;
176
- });
177
-
178
- // 根据 size 定义不同的配置
179
- const sizeConfig = {
180
- small: {
181
- fontSize: 24,
182
- iconSize: 28,
183
- messageSize: 20
184
- },
185
- default: {
186
- fontSize: 28,
187
- iconSize: 32,
188
- messageSize: 24
189
- },
190
- large: {
191
- fontSize: 32,
192
- iconSize: 36,
193
- messageSize: 28
194
- }
195
- };
196
-
197
- // 获取实际使用的 size 值(优先级:u-textarea.size > u-form.size)
198
- const actualSize = computed(() => {
199
- // 优先使用 props 的 size 属性
200
- if (props.size !== '') {
201
- return String(props.size);
202
- }
203
- // 次优先:使用 u-form 的 size 属性(u-form 的 size 只支持预设值)
204
- if (parentExposed.value?.props?.size) {
205
- return String(parentExposed.value.props.size);
206
- }
207
- // 默认值
208
- return 'default';
209
- });
210
-
211
- // 判断实际使用的 size 是否在预设配置中
212
- const isInSizeConfig = computed(() => actualSize.value in sizeConfig);
213
-
214
- // 获取预设 size(用于查找 sizeConfig 配置,如图标大小、高度等)
215
- const presetSize = computed(() => {
216
- return (isInSizeConfig.value ? actualSize.value : 'default') as SizeType;
217
- });
218
-
219
- // 获取当前尺寸配置
220
- const currentSizeConfig = computed(() => sizeConfig[presetSize.value]);
221
-
222
- // 获取实际要使用的 font-size(如果是预设值使用配置值,否则作为自定义值处理)
223
- const actualFontSize = computed(() => {
224
- if (isInSizeConfig.value) {
225
- return $u.addUnit(currentSizeConfig.value.fontSize);
226
- }
227
- // 自定义size值,直接作为fontSize处理
228
- return $u.addUnit(actualSize.value);
229
- });
230
-
231
- // 计算当前图标大小
232
- const currentIconSize = computed(() => currentSizeConfig.value.iconSize);
233
-
234
- // 监听校验状态和父表单 errorType 变化
235
- watch(validateState, () => {
236
- broadcastInputError();
237
- });
238
-
239
- // 监听u-form组件的errorType的变化
240
- watch(
241
- () => parentExposed?.value?.props?.errorType,
242
- val => {
243
- if (val) errorType.value = val;
244
- broadcastInputError();
245
- },
246
- { immediate: true }
247
- );
248
-
249
- // 计算属性
250
- const uLabelWidth = computed(() => {
251
- // 如果用户设置label为空字符串(微信小程序空字符串最终会变成字符串的'true'),意味着要将label的位置宽度设置为auto
252
- return elLabelPosition.value == 'left'
253
- ? (props.label === 'true' || props.label === '') && !$slots.label
254
- ? 'auto'
255
- : $u.addUnit(elLabelWidth.value)
256
- : '100%';
257
- });
258
-
259
- // label的宽度
260
- const elLabelWidth = computed(() => {
261
- // label默认宽度为90,优先使用本组件的值,如果没有(如果设置为0,也算是配置了值,依然起效),则用u-form的值
262
- return props.labelWidth != 0 && props.labelWidth !== ''
263
- ? props.labelWidth
264
- : parentData.value.labelWidth
265
- ? parentData.value.labelWidth
266
- : 90;
267
- });
268
-
269
- // label的样式
270
- const elLabelStyle = computed(() => {
271
- return Object.keys(props.labelStyle).length
272
- ? props.labelStyle
273
- : parentData.value.labelStyle
274
- ? parentData.value.labelStyle
275
- : {};
276
- });
277
-
278
- // label的位置,左侧或者上方
279
- const elLabelPosition = computed(() => {
280
- return props.labelPosition
281
- ? props.labelPosition
282
- : parentData.value.labelPosition
283
- ? parentData.value.labelPosition
284
- : 'left';
285
- });
286
-
287
- // label的对齐方式
288
- const elLabelAlign = computed(() => {
289
- return props.labelAlign ? props.labelAlign : parentData.value.labelAlign ? parentData.value.labelAlign : 'left';
290
- });
291
-
292
- // label的下划线
293
- const elBorderBottom = computed(() => {
294
- // 子组件的borderBottom默认为空字符串,如果不等于空字符串,意味着子组件设置了值,优先使用子组件的值
295
- return props.borderBottom !== ''
296
- ? props.borderBottom
297
- : parentData.value.borderBottom
298
- ? parentData.value.borderBottom
299
- : false;
300
- });
301
-
302
- // 事件派发/广播工具
303
- function broadcastInputError() {
304
- // 子组件发出事件,参数为true或者false,true代表有错误
305
- broadcast('onFormItemError', validateState.value === 'error' && showError.value('border'));
306
- }
307
-
308
- /**
309
- * 根据路径获取对象的值(支持嵌套路径,如 'a.b.c')
310
- * @param obj 对象
311
- * @param path 路径字符串
312
- */
313
- function getPropByPath(obj: any, path: string) {
314
- if (!obj || !path) return { o: obj, k: '', v: undefined };
315
-
316
- const paths = path.split('.');
317
- let current = obj;
318
- let key = '';
319
-
320
- for (let i = 0; i < paths.length; i++) {
321
- key = paths[i];
322
- if (!current) break;
323
-
324
- if (i === paths.length - 1) {
325
- return { o: current, k: key, v: current[key] };
326
- }
327
- current = current[key];
328
- }
329
-
330
- return { o: current, k: key, v: undefined };
331
- }
332
-
333
- /**
334
- * 获取当前u-form-item的校验规则
335
- */
336
- function getRules() {
337
- // 父组件的所有规则
338
- let rules = parentExposed?.value?.rules?.value || {};
339
- rules = rules
340
- ? rules[props.prop] || getPropByPath(rules, props.prop.replace(/(\.|^)(\d+)\./, '.defaultField.fields.')).v
341
- : [];
342
- // 保证返回的是一个数组形式
343
- return [].concat(rules || []);
344
- }
345
-
346
- // blur事件时进行表单校验
347
- function onFieldBlur() {
348
- validation('blur');
349
- }
350
-
351
- // change事件进行表单校验
352
- function onFieldChange() {
353
- validation('change');
354
- }
355
-
356
- function onFormBlur() {
357
- onFieldBlur();
358
- }
359
-
360
- function onFormChange() {
361
- onFieldChange();
362
- }
363
-
364
- /**
365
- * 过滤出符合要求的rule规则
366
- * @param triggerType 触发类型
367
- */
368
- function getFilteredRule(triggerType = '') {
369
- // 获取所有规则
370
- const rules = getRules();
371
- // 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
372
- if (!triggerType) return rules;
373
- // 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
374
- // 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如['blur','change']
375
- // 某些场景可能的判断规则,可能不存在trigger属性,故先判断是否存在此属性
376
- return rules.filter((res: any) => res.trigger && res.trigger.indexOf(triggerType) !== -1);
377
- }
378
-
379
- /**
380
- * 校验数据
381
- * @param trigger 触发类型
382
- * @param callback 校验回调
383
- */
384
- function validation(trigger: string, callback: (msg: string) => void = () => {}) {
385
- // 检验之前,先获取需要校验的值(支持嵌套路径)
386
- const propPath = getPropByPath(parentExposed?.value?.model?.value, props.prop);
387
- fieldValue.value = propPath.v;
388
-
389
- // blur和change是否有当前方式的校验规则
390
- let rules = getFilteredRule(trigger);
391
- // 判断是否有验证规则,如果没有规则,也调用回调方法,否则父组件u-form会因为
392
- // 对count变量的统计错误而无法进入上一层的回调
393
- if (!rules || rules.length === 0) {
394
- callback('');
395
- return;
396
- }
397
- // 设置当前的状态,标识为校验中
398
- validateState.value = 'validating';
399
- // 调用async-validator的方法
400
- let validator = new schema({ [props.prop]: rules });
401
- validator.validate({ [props.prop]: fieldValue.value }, { firstFields: true }, (errors: any) => {
402
- // 记录状态和报错信息
403
- validateState.value = !errors ? 'success' : 'error';
404
- validateMessage.value = errors ? errors[0].message : '';
405
- // 调用回调方法
406
- callback(validateMessage.value);
407
- });
408
- }
409
-
410
- /**
411
- * 清空当前的u-form-item
412
- */
413
- function resetField() {
414
- if (parentExposed?.value?.model?.value && props.prop) {
415
- // 支持嵌套路径的重置
416
- const propPath = getPropByPath(parentExposed.value.model.value, props.prop);
417
- if (propPath.o && propPath.k) {
418
- propPath.o[propPath.k] = initialValue.value;
419
- }
420
- }
421
- // 设置为`success`状态,只是为了清空错误标记
422
- // 延时50毫秒,如果立即清空状态,则无法清空错误标记
423
- setTimeout(() => {
424
- validateState.value = 'success';
425
- }, 50);
426
- }
427
-
428
- // 组件挂载时注册到父表单
429
- onMounted(() => {
430
- nextTick(() => {
431
- if (parentExposed.value) {
432
- // 继承父表单配置
433
- // 历遍parentData中的属性,将parent中的同名属性赋值给parentData
434
- Object.keys(parentData.value).forEach(key => {
435
- (parentData.value as any)[key] = parentExposed?.value?.props[key];
436
- });
437
- // 如果没有传入prop,或者uForm为空(如果u-form-input单独使用,就不会有uForm注入),就不进行校验
438
- if (props.prop) {
439
- // 将本实例添加到父组件中
440
- parentExposed?.value?.addField &&
441
- parentExposed?.value?.addField({
442
- validation,
443
- resetField,
444
- prop: props.prop
445
- });
446
- errorType.value = parentExposed?.value?.errorType || errorType.value;
447
- // 设置初始值(支持嵌套路径)
448
- const propPath = getPropByPath(parentExposed?.value?.model?.value, props.prop);
449
- fieldValue.value = propPath.v;
450
- // 设置初始值
451
- initialValue.value = fieldValue.value;
452
- }
453
- }
454
- });
455
- });
456
- // 组件销毁前,将实例从u-form的缓存中移除
457
- onBeforeUnmount(() => {
458
- // 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
459
- if (parentExposed?.value && props.prop) {
460
- parentExposed?.value?.removeField && parentExposed?.value?.removeField({ prop: props.prop });
461
- }
462
- });
463
-
464
- defineExpose({
465
- validation,
466
- resetField,
467
- onFormBlur,
468
- onFormChange
469
- });
470
- </script>
471
-
472
- <style lang="scss" scoped>
473
- @import '../../libs/css/style.components.scss';
474
-
475
- .u-form-item {
476
- @include vue-flex;
477
- // align-items: flex-start;
478
- padding: 20rpx 0;
479
- font-size: 28rpx;
480
- color: $u-main-color;
481
- box-sizing: border-box;
482
- // line-height: 70rpx;
483
- flex-direction: column;
484
-
485
- &__border-bottom--error:after {
486
- border-color: $u-type-error;
487
- }
488
-
489
- &__border--error {
490
- :deep(.u-input--border) {
491
- border-color: $u-type-error !important;
492
- }
493
- }
494
-
495
- &__body {
496
- @include vue-flex;
497
- }
498
-
499
- &--left {
500
- @include vue-flex;
501
- align-items: center;
502
-
503
- &__content {
504
- position: relative;
505
- @include vue-flex;
506
- align-items: center;
507
- padding-right: 10rpx;
508
- flex: 1;
509
-
510
- &__icon {
511
- margin-right: 8rpx;
512
- @include vue-flex;
513
- align-items: center;
514
- }
515
-
516
- &--required {
517
- position: absolute;
518
- left: -16rpx;
519
- vertical-align: middle;
520
- color: $u-type-error;
521
- padding-top: 6rpx;
522
- }
523
-
524
- &__label {
525
- @include vue-flex;
526
- align-items: center;
527
- flex: 1;
528
- }
529
- }
530
- }
531
-
532
- &--right {
533
- flex: 1;
534
-
535
- &__content {
536
- @include vue-flex;
537
- align-items: center;
538
- flex: 1;
539
-
540
- &__slot {
541
- flex: 1;
542
- @include vue-flex;
543
- align-items: center;
544
- }
545
-
546
- &__icon {
547
- margin-left: 10rpx;
548
- color: $u-light-color;
549
- font-size: 30rpx;
550
- @include vue-flex;
551
- align-items: center;
552
- }
553
- }
554
- }
555
-
556
- &__message {
557
- font-size: 24rpx;
558
- line-height: 24rpx;
559
- color: $u-type-error;
560
- margin-top: 12rpx;
561
- }
562
- }
563
- </style>
1
+ <template>
2
+ <view
3
+ class="u-form-item"
4
+ :class="[
5
+ {
6
+ 'u-border-bottom': elBorderBottom,
7
+ 'u-form-item__border-bottom--error': validateState === 'error' && showError('border-bottom'),
8
+ 'u-form-item__border--error': validateState === 'error' && showError('border')
9
+ },
10
+ customClass
11
+ ]"
12
+ :style="$u.toStyle(customStyle)"
13
+ >
14
+ <view
15
+ class="u-form-item__body"
16
+ :style="{
17
+ flexDirection: elLabelPosition == 'left' ? 'row' : 'column'
18
+ }"
19
+ >
20
+ <!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" -->
21
+ <view
22
+ class="u-form-item--left"
23
+ :style="{
24
+ width: uLabelWidth,
25
+ flex: `0 0 ${uLabelWidth}`,
26
+ marginBottom: elLabelPosition == 'left' ? 0 : '10rpx'
27
+ }"
28
+ >
29
+ <!-- 为了块对齐 -->
30
+ <view
31
+ v-if="required || leftIcon || label || $slots.label || $slots.leftIcon"
32
+ class="u-form-item--left__content"
33
+ >
34
+ <!-- nvue不支持伪元素before -->
35
+ <text v-if="required" class="u-form-item--left__content--required">*</text>
36
+ <view class="u-form-item--left__content__icon" v-if="leftIcon || $slots.leftIcon">
37
+ <slot name="leftIcon">
38
+ <u-icon
39
+ :name="leftIcon"
40
+ :size="currentIconSize"
41
+ :custom-prefix="leftIconPrefix"
42
+ :custom-style="leftIconStyle"
43
+ ></u-icon>
44
+ </slot>
45
+ </view>
46
+ <view
47
+ class="u-form-item--left__content__label"
48
+ :style="[
49
+ elLabelStyle,
50
+ { 'font-size': actualFontSize },
51
+ {
52
+ 'justify-content':
53
+ elLabelAlign == 'left'
54
+ ? 'flex-start'
55
+ : elLabelAlign == 'center'
56
+ ? 'center'
57
+ : 'flex-end'
58
+ }
59
+ ]"
60
+ >
61
+ <slot name="label">
62
+ {{ label }}
63
+ </slot>
64
+ </view>
65
+ </view>
66
+ </view>
67
+ <view class="u-form-item--right u-flex">
68
+ <view class="u-form-item--right__content">
69
+ <view class="u-form-item--right__content__slot">
70
+ <slot />
71
+ </view>
72
+ <view
73
+ class="u-form-item--right__content__icon u-flex"
74
+ v-if="$slots.right || $slots.rightIcon || rightIcon"
75
+ >
76
+ <slot name="rightIcon">
77
+ <u-icon
78
+ v-if="rightIcon"
79
+ :name="rightIcon"
80
+ :size="currentIconSize"
81
+ :custom-prefix="rightIconPrefix"
82
+ :custom-style="rightIconStyle"
83
+ ></u-icon>
84
+ </slot>
85
+ <slot name="right" />
86
+ </view>
87
+ </view>
88
+ </view>
89
+ </view>
90
+ <view
91
+ class="u-form-item__message"
92
+ v-if="validateState === 'error' && showError('message')"
93
+ :style="{
94
+ paddingLeft: elLabelPosition == 'left' && label ? $u.addUnit(elLabelWidth) : '0',
95
+ fontSize: $u.addUnit(currentSizeConfig.messageSize),
96
+ lineHeight: $u.addUnit(currentSizeConfig.messageSize)
97
+ }"
98
+ >
99
+ {{ validateMessage }}
100
+ </view>
101
+ </view>
102
+ </template>
103
+
104
+ <script lang="ts">
105
+ export default {
106
+ name: 'u-form-item',
107
+ options: {
108
+ addGlobalClass: true,
109
+ // #ifndef MP-TOUTIAO
110
+ virtualHost: true,
111
+ // #endif
112
+ styleIsolation: 'shared'
113
+ }
114
+ };
115
+ </script>
116
+
117
+ <script setup lang="ts">
118
+ import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick, useSlots } from 'vue';
119
+ import { $u, useChildren, useParent } from '../..';
120
+ // @ts-ignore
121
+ import schema from '../../libs/util/async-validator';
122
+ import { FormItemProps } from './types';
123
+ import type { SizeType } from '../../types/global';
124
+ import uIcon from '../u-icon/u-icon.vue';
125
+ // 去除警告信息
126
+ schema.warning = function () {};
127
+
128
+ /**
129
+ * form-item 表单item
130
+ * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
131
+ * @tutorial https://uviewpro.cn/zh/components/form.html
132
+ * @property {String} label 左侧提示文字
133
+ * @property {Object} prop 表单域model对象的属性名,在使用 validate、resetFields 方法的情况下,该属性是必填的
134
+ * @property {Boolean} border-bottom 是否显示表单域的下划线边框
135
+ * @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
136
+ * @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
137
+ * @property {Object} label-style label的样式,对象形式
138
+ * @property {String} label-align label的对齐方式
139
+ * @property {String} right-icon 右侧自定义字体图标(限uView内置图标)或图片地址
140
+ * @property {String} left-icon 左侧自定义字体图标(限uView内置图标)或图片地址
141
+ * @property {Object} left-icon-style 左侧图标的样式,对象形式
142
+ * @property {Object} right-icon-style 右侧图标的样式,对象形式
143
+ * @property {Boolean} required 是否显示左边的"*"号,这里仅起展示作用,如需校验必填,请通过rules配置必填规则(默认false)
144
+ * @example <u-form-item label="姓名"><u-input v-model="form.name" /></u-form-item>
145
+ */
146
+
147
+ const props = defineProps(FormItemProps);
148
+
149
+ // 插槽
150
+ const $slots = useSlots();
151
+
152
+ const { broadcast } = useParent('u-form-item');
153
+ const { parentExposed } = useChildren('u-form-item', 'u-form');
154
+
155
+ // 组件状态
156
+ const initialValue = ref(''); // 存储初始值,用于重置
157
+ const validateState = ref(''); // 校验状态 success/error/validating
158
+ const validateMessage = ref(''); // 校验失败的提示语
159
+ const errorType = ref<string[]>(['message']); // 错误提示方式,默认 message
160
+ const fieldValue = ref(''); // 当前表单项的值
161
+ const parentData = ref({
162
+ borderBottom: true, // 父表单下划线边框
163
+ labelWidth: 90, // 父表单 label 宽度
164
+ labelPosition: 'left', // 父表单 label 位置
165
+ labelStyle: {}, // 父表单 label 样式
166
+ labelAlign: 'left' // 父表单 label 对齐
167
+ });
168
+
169
+ // 显示错误提示
170
+ // errorType: ['message', 'toast', 'border-bottom', 'none']
171
+ const showError = computed(() => (type: string) => {
172
+ // 如果errorType数组中含有none,或者toast提示类型
173
+ if (errorType.value.indexOf('none') >= 0) return false;
174
+ else if (errorType.value.indexOf(type) >= 0) return true;
175
+ else return false;
176
+ });
177
+
178
+ // 根据 size 定义不同的配置
179
+ const sizeConfig = {
180
+ small: {
181
+ fontSize: 24,
182
+ iconSize: 28,
183
+ messageSize: 20
184
+ },
185
+ default: {
186
+ fontSize: 28,
187
+ iconSize: 32,
188
+ messageSize: 24
189
+ },
190
+ large: {
191
+ fontSize: 32,
192
+ iconSize: 36,
193
+ messageSize: 28
194
+ }
195
+ };
196
+
197
+ // 获取实际使用的 size 值(优先级:u-textarea.size > u-form.size)
198
+ const actualSize = computed(() => {
199
+ // 优先使用 props 的 size 属性
200
+ if (props.size !== '') {
201
+ return String(props.size);
202
+ }
203
+ // 次优先:使用 u-form 的 size 属性(u-form 的 size 只支持预设值)
204
+ if (parentExposed.value?.props?.size) {
205
+ return String(parentExposed.value.props.size);
206
+ }
207
+ // 默认值
208
+ return 'default';
209
+ });
210
+
211
+ // 判断实际使用的 size 是否在预设配置中
212
+ const isInSizeConfig = computed(() => actualSize.value in sizeConfig);
213
+
214
+ // 获取预设 size(用于查找 sizeConfig 配置,如图标大小、高度等)
215
+ const presetSize = computed(() => {
216
+ return (isInSizeConfig.value ? actualSize.value : 'default') as SizeType;
217
+ });
218
+
219
+ // 获取当前尺寸配置
220
+ const currentSizeConfig = computed(() => sizeConfig[presetSize.value]);
221
+
222
+ // 获取实际要使用的 font-size(如果是预设值使用配置值,否则作为自定义值处理)
223
+ const actualFontSize = computed(() => {
224
+ if (isInSizeConfig.value) {
225
+ return $u.addUnit(currentSizeConfig.value.fontSize);
226
+ }
227
+ // 自定义size值,直接作为fontSize处理
228
+ return $u.addUnit(actualSize.value);
229
+ });
230
+
231
+ // 计算当前图标大小
232
+ const currentIconSize = computed(() => currentSizeConfig.value.iconSize);
233
+
234
+ // 监听校验状态和父表单 errorType 变化
235
+ watch(validateState, () => {
236
+ broadcastInputError();
237
+ });
238
+
239
+ // 监听u-form组件的errorType的变化
240
+ watch(
241
+ () => parentExposed?.value?.props?.errorType,
242
+ val => {
243
+ if (val) errorType.value = val;
244
+ broadcastInputError();
245
+ },
246
+ { immediate: true }
247
+ );
248
+
249
+ // 计算属性
250
+ const uLabelWidth = computed(() => {
251
+ // 如果用户设置label为空字符串(微信小程序空字符串最终会变成字符串的'true'),意味着要将label的位置宽度设置为auto
252
+ return elLabelPosition.value == 'left'
253
+ ? (props.label === 'true' || props.label === '') && !$slots.label
254
+ ? 'auto'
255
+ : $u.addUnit(elLabelWidth.value)
256
+ : '100%';
257
+ });
258
+
259
+ // label的宽度
260
+ const elLabelWidth = computed(() => {
261
+ // label默认宽度为90,优先使用本组件的值,如果没有(如果设置为0,也算是配置了值,依然起效),则用u-form的值
262
+ return props.labelWidth != 0 && props.labelWidth !== ''
263
+ ? props.labelWidth
264
+ : parentData.value.labelWidth
265
+ ? parentData.value.labelWidth
266
+ : 90;
267
+ });
268
+
269
+ // label的样式
270
+ const elLabelStyle = computed(() => {
271
+ return Object.keys(props.labelStyle).length
272
+ ? props.labelStyle
273
+ : parentData.value.labelStyle
274
+ ? parentData.value.labelStyle
275
+ : {};
276
+ });
277
+
278
+ // label的位置,左侧或者上方
279
+ const elLabelPosition = computed(() => {
280
+ return props.labelPosition
281
+ ? props.labelPosition
282
+ : parentData.value.labelPosition
283
+ ? parentData.value.labelPosition
284
+ : 'left';
285
+ });
286
+
287
+ // label的对齐方式
288
+ const elLabelAlign = computed(() => {
289
+ return props.labelAlign ? props.labelAlign : parentData.value.labelAlign ? parentData.value.labelAlign : 'left';
290
+ });
291
+
292
+ // label的下划线
293
+ const elBorderBottom = computed(() => {
294
+ // 子组件的borderBottom默认为空字符串,如果不等于空字符串,意味着子组件设置了值,优先使用子组件的值
295
+ return props.borderBottom !== ''
296
+ ? props.borderBottom
297
+ : parentData.value.borderBottom
298
+ ? parentData.value.borderBottom
299
+ : false;
300
+ });
301
+
302
+ // 事件派发/广播工具
303
+ function broadcastInputError() {
304
+ // 子组件发出事件,参数为true或者false,true代表有错误
305
+ broadcast('onFormItemError', validateState.value === 'error' && showError.value('border'));
306
+ }
307
+
308
+ /**
309
+ * 根据路径获取对象的值(支持嵌套路径,如 'a.b.c')
310
+ * @param obj 对象
311
+ * @param path 路径字符串
312
+ */
313
+ function getPropByPath(obj: any, path: string) {
314
+ if (!obj || !path) return { o: obj, k: '', v: undefined };
315
+
316
+ const paths = path.split('.');
317
+ let current = obj;
318
+ let key = '';
319
+
320
+ for (let i = 0; i < paths.length; i++) {
321
+ key = paths[i];
322
+ if (!current) break;
323
+
324
+ if (i === paths.length - 1) {
325
+ return { o: current, k: key, v: current[key] };
326
+ }
327
+ current = current[key];
328
+ }
329
+
330
+ return { o: current, k: key, v: undefined };
331
+ }
332
+
333
+ /**
334
+ * 获取当前u-form-item的校验规则
335
+ */
336
+ function getRules() {
337
+ // 父组件的所有规则
338
+ let rules = parentExposed?.value?.rules?.value || {};
339
+ rules = rules
340
+ ? rules[props.prop] || getPropByPath(rules, props.prop.replace(/(\.|^)(\d+)\./, '.defaultField.fields.')).v
341
+ : [];
342
+ // 保证返回的是一个数组形式
343
+ return [].concat(rules || []);
344
+ }
345
+
346
+ // blur事件时进行表单校验
347
+ function onFieldBlur() {
348
+ validation('blur');
349
+ }
350
+
351
+ // change事件进行表单校验
352
+ function onFieldChange() {
353
+ validation('change');
354
+ }
355
+
356
+ function onFormBlur() {
357
+ onFieldBlur();
358
+ }
359
+
360
+ function onFormChange() {
361
+ onFieldChange();
362
+ }
363
+
364
+ /**
365
+ * 过滤出符合要求的rule规则
366
+ * @param triggerType 触发类型
367
+ */
368
+ function getFilteredRule(triggerType = '') {
369
+ // 获取所有规则
370
+ const rules = getRules();
371
+ // 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
372
+ if (!triggerType) return rules;
373
+ // 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
374
+ // 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如['blur','change']
375
+ // 某些场景可能的判断规则,可能不存在trigger属性,故先判断是否存在此属性
376
+ return rules.filter((res: any) => res.trigger && res.trigger.indexOf(triggerType) !== -1);
377
+ }
378
+
379
+ /**
380
+ * 校验数据
381
+ * @param trigger 触发类型
382
+ * @param callback 校验回调
383
+ */
384
+ function validation(trigger: string, callback: (msg: string) => void = () => {}) {
385
+ // 检验之前,先获取需要校验的值(支持嵌套路径)
386
+ const propPath = getPropByPath(parentExposed?.value?.model?.value, props.prop);
387
+ fieldValue.value = propPath.v;
388
+
389
+ // blur和change是否有当前方式的校验规则
390
+ let rules = getFilteredRule(trigger);
391
+ // 判断是否有验证规则,如果没有规则,也调用回调方法,否则父组件u-form会因为
392
+ // 对count变量的统计错误而无法进入上一层的回调
393
+ if (!rules || rules.length === 0) {
394
+ callback('');
395
+ return;
396
+ }
397
+ // 设置当前的状态,标识为校验中
398
+ validateState.value = 'validating';
399
+ // 调用async-validator的方法
400
+ let validator = new schema({ [props.prop]: rules });
401
+ validator.validate({ [props.prop]: fieldValue.value }, { firstFields: true }, (errors: any) => {
402
+ // 记录状态和报错信息
403
+ validateState.value = !errors ? 'success' : 'error';
404
+ validateMessage.value = errors ? errors[0].message : '';
405
+ // 调用回调方法
406
+ callback(validateMessage.value);
407
+ });
408
+ }
409
+
410
+ /**
411
+ * 清空当前的u-form-item
412
+ */
413
+ function resetField() {
414
+ if (parentExposed?.value?.model?.value && props.prop) {
415
+ // 支持嵌套路径的重置
416
+ const propPath = getPropByPath(parentExposed.value.model.value, props.prop);
417
+ if (propPath.o && propPath.k) {
418
+ propPath.o[propPath.k] = initialValue.value;
419
+ }
420
+ }
421
+ // 设置为`success`状态,只是为了清空错误标记
422
+ // 延时50毫秒,如果立即清空状态,则无法清空错误标记
423
+ setTimeout(() => {
424
+ validateState.value = 'success';
425
+ }, 50);
426
+ }
427
+
428
+ // 组件挂载时注册到父表单
429
+ onMounted(() => {
430
+ nextTick(() => {
431
+ if (parentExposed.value) {
432
+ // 继承父表单配置
433
+ // 历遍parentData中的属性,将parent中的同名属性赋值给parentData
434
+ Object.keys(parentData.value).forEach(key => {
435
+ (parentData.value as any)[key] = parentExposed?.value?.props[key];
436
+ });
437
+ // 如果没有传入prop,或者uForm为空(如果u-form-input单独使用,就不会有uForm注入),就不进行校验
438
+ if (props.prop) {
439
+ // 将本实例添加到父组件中
440
+ parentExposed?.value?.addField &&
441
+ parentExposed?.value?.addField({
442
+ validation,
443
+ resetField,
444
+ prop: props.prop
445
+ });
446
+ errorType.value = parentExposed?.value?.errorType || errorType.value;
447
+ // 设置初始值(支持嵌套路径)
448
+ const propPath = getPropByPath(parentExposed?.value?.model?.value, props.prop);
449
+ fieldValue.value = propPath.v;
450
+ // 设置初始值
451
+ initialValue.value = fieldValue.value;
452
+ }
453
+ }
454
+ });
455
+ });
456
+ // 组件销毁前,将实例从u-form的缓存中移除
457
+ onBeforeUnmount(() => {
458
+ // 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
459
+ if (parentExposed?.value && props.prop) {
460
+ parentExposed?.value?.removeField && parentExposed?.value?.removeField({ prop: props.prop });
461
+ }
462
+ });
463
+
464
+ defineExpose({
465
+ validation,
466
+ resetField,
467
+ onFormBlur,
468
+ onFormChange
469
+ });
470
+ </script>
471
+
472
+ <style lang="scss" scoped>
473
+ @import '../../libs/css/style.components.scss';
474
+
475
+ .u-form-item {
476
+ @include vue-flex;
477
+ // align-items: flex-start;
478
+ padding: 20rpx 0;
479
+ font-size: 28rpx;
480
+ color: $u-main-color;
481
+ box-sizing: border-box;
482
+ // line-height: 70rpx;
483
+ flex-direction: column;
484
+
485
+ &__border-bottom--error:after {
486
+ border-color: $u-type-error;
487
+ }
488
+
489
+ &__border--error {
490
+ :deep(.u-input--border) {
491
+ border-color: $u-type-error !important;
492
+ }
493
+ }
494
+
495
+ &__body {
496
+ @include vue-flex;
497
+ }
498
+
499
+ &--left {
500
+ @include vue-flex;
501
+ align-items: center;
502
+
503
+ &__content {
504
+ position: relative;
505
+ @include vue-flex;
506
+ align-items: center;
507
+ padding-right: 10rpx;
508
+ flex: 1;
509
+
510
+ &__icon {
511
+ margin-right: 8rpx;
512
+ @include vue-flex;
513
+ align-items: center;
514
+ }
515
+
516
+ &--required {
517
+ position: absolute;
518
+ left: -16rpx;
519
+ vertical-align: middle;
520
+ color: $u-type-error;
521
+ padding-top: 6rpx;
522
+ }
523
+
524
+ &__label {
525
+ @include vue-flex;
526
+ align-items: center;
527
+ flex: 1;
528
+ }
529
+ }
530
+ }
531
+
532
+ &--right {
533
+ flex: 1;
534
+
535
+ &__content {
536
+ @include vue-flex;
537
+ align-items: center;
538
+ flex: 1;
539
+
540
+ &__slot {
541
+ flex: 1;
542
+ @include vue-flex;
543
+ align-items: center;
544
+ }
545
+
546
+ &__icon {
547
+ margin-left: 10rpx;
548
+ color: $u-light-color;
549
+ font-size: 30rpx;
550
+ @include vue-flex;
551
+ align-items: center;
552
+ }
553
+ }
554
+ }
555
+
556
+ &__message {
557
+ font-size: 24rpx;
558
+ line-height: 24rpx;
559
+ color: $u-type-error;
560
+ margin-top: 12rpx;
561
+ }
562
+ }
563
+ </style>