uview-pro 0.2.4 → 0.3.1

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 (249) hide show
  1. package/changelog.md +584 -545
  2. package/components/common/props.ts +22 -22
  3. package/components/u-action-sheet/types.ts +37 -35
  4. package/components/u-action-sheet/u-action-sheet.vue +178 -167
  5. package/components/u-alert-tips/types.ts +41 -39
  6. package/components/u-alert-tips/u-alert-tips.vue +238 -223
  7. package/components/u-avatar/types.ts +36 -34
  8. package/components/u-avatar/u-avatar.vue +217 -207
  9. package/components/u-avatar-cropper/types.ts +23 -23
  10. package/components/u-avatar-cropper/u-avatar-cropper.vue +297 -286
  11. package/components/u-avatar-cropper/weCropper.d.ts +62 -62
  12. package/components/u-avatar-cropper/weCropper.js +1281 -1281
  13. package/components/u-avatar-cropper/weCropper.ts +1276 -1276
  14. package/components/u-back-top/types.ts +36 -39
  15. package/components/u-back-top/u-back-top.vue +140 -128
  16. package/components/u-badge/types.ts +38 -36
  17. package/components/u-badge/u-badge.vue +183 -165
  18. package/components/u-button/types.ts +66 -66
  19. package/components/u-button/u-button.vue +579 -566
  20. package/components/u-calendar/types.ts +75 -75
  21. package/components/u-calendar/u-calendar.vue +793 -793
  22. package/components/u-car-keyboard/types.ts +14 -12
  23. package/components/u-car-keyboard/u-car-keyboard.vue +262 -253
  24. package/components/u-card/types.ts +61 -59
  25. package/components/u-card/u-card.vue +209 -194
  26. package/components/u-cell-group/types.ts +19 -17
  27. package/components/u-cell-group/u-cell-group.vue +60 -50
  28. package/components/u-cell-item/types.ts +56 -54
  29. package/components/u-cell-item/u-cell-item.vue +226 -213
  30. package/components/u-checkbox/types.ts +33 -31
  31. package/components/u-checkbox/u-checkbox.vue +282 -283
  32. package/components/u-checkbox-group/types.ts +34 -32
  33. package/components/u-checkbox-group/u-checkbox-group.vue +130 -130
  34. package/components/u-circle-progress/types.ts +54 -52
  35. package/components/u-circle-progress/u-circle-progress.vue +206 -191
  36. package/components/u-city-select/types.ts +22 -20
  37. package/components/u-city-select/u-city-select.vue +276 -264
  38. package/components/u-col/types.ts +32 -30
  39. package/components/u-col/u-col.vue +142 -123
  40. package/components/u-collapse/types.ts +33 -33
  41. package/components/u-collapse/u-collapse.vue +190 -186
  42. package/components/u-collapse-item/types.ts +27 -27
  43. package/components/u-collapse-item/u-collapse-item.vue +290 -285
  44. package/components/u-column-notice/types.ts +50 -48
  45. package/components/u-column-notice/u-column-notice.vue +222 -210
  46. package/components/u-count-down/types.ts +44 -42
  47. package/components/u-count-down/u-count-down.vue +286 -274
  48. package/components/u-count-to/types.ts +34 -32
  49. package/components/u-count-to/u-count-to.vue +266 -248
  50. package/components/u-divider/types.ts +33 -31
  51. package/components/u-divider/u-divider.vue +145 -129
  52. package/components/u-dropdown/types.ts +34 -32
  53. package/components/u-dropdown/u-dropdown.vue +330 -302
  54. package/components/u-dropdown-item/types.ts +29 -27
  55. package/components/u-dropdown-item/u-dropdown-item.vue +120 -128
  56. package/components/u-empty/types.ts +38 -36
  57. package/components/u-empty/u-empty.vue +103 -88
  58. package/components/u-field/types.ts +71 -69
  59. package/components/u-field/u-field.vue +388 -373
  60. package/components/u-form/types.ts +29 -27
  61. package/components/u-form/u-form.vue +130 -136
  62. package/components/u-form-item/types.ts +72 -70
  63. package/components/u-form-item/u-form-item.vue +447 -447
  64. package/components/u-full-screen/types.ts +16 -14
  65. package/components/u-full-screen/u-full-screen.vue +103 -89
  66. package/components/u-gap/types.ts +20 -18
  67. package/components/u-gap/u-gap.vue +50 -40
  68. package/components/u-grid/types.ts +21 -19
  69. package/components/u-grid/u-grid.vue +91 -93
  70. package/components/u-grid-item/types.ts +16 -16
  71. package/components/u-grid-item/u-grid-item.vue +130 -134
  72. package/components/u-icon/types.ts +61 -62
  73. package/components/u-icon/u-icon.vue +296 -294
  74. package/components/u-image/types.ts +51 -51
  75. package/components/u-image/u-image.vue +239 -230
  76. package/components/u-index-anchor/types.ts +16 -16
  77. package/components/u-index-anchor/u-index-anchor.vue +94 -86
  78. package/components/u-index-list/types.ts +43 -43
  79. package/components/u-index-list/u-index-list.vue +352 -355
  80. package/components/u-input/types.ts +137 -140
  81. package/components/u-input/u-input.vue +288 -279
  82. package/components/u-keyboard/types.ts +40 -40
  83. package/components/u-keyboard/u-keyboard.vue +178 -169
  84. package/components/u-lazy-load/types.ts +37 -37
  85. package/components/u-lazy-load/u-lazy-load.vue +246 -235
  86. package/components/u-line/types.ts +44 -44
  87. package/components/u-line/u-line.vue +68 -59
  88. package/components/u-line-progress/types.ts +58 -58
  89. package/components/u-line-progress/u-line-progress.vue +126 -117
  90. package/components/u-link/types.ts +43 -43
  91. package/components/u-link/u-link.vue +84 -75
  92. package/components/u-loading/types.ts +35 -35
  93. package/components/u-loading/u-loading.vue +105 -96
  94. package/components/u-loading-popup/types.ts +26 -26
  95. package/components/u-loading-popup/u-loading-popup.vue +253 -239
  96. package/components/u-loadmore/types.ts +79 -79
  97. package/components/u-loadmore/u-loadmore.vue +156 -145
  98. package/components/u-mask/types.ts +40 -43
  99. package/components/u-mask/u-mask.vue +113 -106
  100. package/components/u-message-input/types.ts +74 -74
  101. package/components/u-message-input/u-message-input.vue +281 -270
  102. package/components/u-modal/types.ts +118 -118
  103. package/components/u-modal/u-modal.vue +220 -211
  104. package/components/u-navbar/types.ts +103 -103
  105. package/components/u-navbar/u-navbar.vue +251 -240
  106. package/components/u-no-network/image.ts +2 -2
  107. package/components/u-no-network/types.ts +28 -28
  108. package/components/u-no-network/u-no-network.vue +303 -292
  109. package/components/u-notice-bar/types.ts +111 -111
  110. package/components/u-notice-bar/u-notice-bar.vue +189 -179
  111. package/components/u-number-box/types.ts +42 -42
  112. package/components/u-number-box/u-number-box.vue +321 -312
  113. package/components/u-number-keyboard/types.ts +26 -26
  114. package/components/u-number-keyboard/u-number-keyboard.vue +188 -179
  115. package/components/u-picker/types.ts +123 -123
  116. package/components/u-picker/u-picker.vue +685 -676
  117. package/components/u-popup/types.ts +59 -59
  118. package/components/u-popup/u-popup.vue +385 -375
  119. package/components/u-radio/types.ts +27 -25
  120. package/components/u-radio/u-radio.vue +279 -272
  121. package/components/u-radio-group/types.ts +31 -29
  122. package/components/u-radio-group/u-radio-group.vue +96 -108
  123. package/components/u-rate/types.ts +42 -40
  124. package/components/u-rate/u-rate.vue +249 -234
  125. package/components/u-read-more/types.ts +37 -35
  126. package/components/u-read-more/u-read-more.vue +172 -156
  127. package/components/u-root-portal/u-root-portal.vue +56 -54
  128. package/components/u-row/types.ts +22 -20
  129. package/components/u-row/u-row.vue +105 -87
  130. package/components/u-row-notice/types.ts +41 -39
  131. package/components/u-row-notice/u-row-notice.vue +256 -244
  132. package/components/u-safe-bottom/u-safe-bottom.vue +57 -46
  133. package/components/u-search/types.ts +55 -53
  134. package/components/u-search/u-search.vue +279 -268
  135. package/components/u-section/types.ts +34 -32
  136. package/components/u-section/u-section.vue +150 -131
  137. package/components/u-select/types.ts +45 -43
  138. package/components/u-select/u-select.vue +388 -378
  139. package/components/u-skeleton/types.ts +22 -20
  140. package/components/u-skeleton/u-skeleton.vue +231 -220
  141. package/components/u-slider/types.ts +34 -32
  142. package/components/u-slider/u-slider.vue +255 -247
  143. package/components/u-status-bar/u-status-bar.vue +74 -65
  144. package/components/u-steps/types.ts +30 -28
  145. package/components/u-steps/u-steps.vue +181 -169
  146. package/components/u-sticky/types.ts +24 -22
  147. package/components/u-sticky/u-sticky.vue +178 -162
  148. package/components/u-subsection/types.ts +38 -36
  149. package/components/u-subsection/u-subsection.vue +339 -328
  150. package/components/u-swipe-action/types.ts +52 -50
  151. package/components/u-swipe-action/u-swipe-action.vue +276 -260
  152. package/components/u-swiper/types.ts +49 -47
  153. package/components/u-swiper/u-swiper.vue +308 -291
  154. package/components/u-switch/types.ts +30 -28
  155. package/components/u-switch/u-switch.vue +150 -141
  156. package/components/u-tabbar/types.ts +38 -36
  157. package/components/u-tabbar/u-tabbar.vue +315 -298
  158. package/components/u-table/types.ts +27 -25
  159. package/components/u-table/u-table.vue +67 -55
  160. package/components/u-tabs/types.ts +53 -51
  161. package/components/u-tabs/u-tabs.vue +302 -291
  162. package/components/u-tabs-swiper/types.ts +55 -53
  163. package/components/u-tabs-swiper/u-tabs-swiper.vue +409 -397
  164. package/components/u-tag/types.ts +39 -37
  165. package/components/u-tag/u-tag.vue +268 -252
  166. package/components/u-td/types.ts +14 -12
  167. package/components/u-td/u-td.vue +98 -87
  168. package/components/u-text/types.ts +72 -72
  169. package/components/u-text/u-text.vue +343 -341
  170. package/components/u-th/types.ts +14 -12
  171. package/components/u-th/u-th.vue +92 -81
  172. package/components/u-time-line/u-time-line.vue +53 -39
  173. package/components/u-time-line-item/types.ts +16 -14
  174. package/components/u-time-line-item/u-time-line-item.vue +90 -78
  175. package/components/u-toast/types.ts +38 -36
  176. package/components/u-toast/u-toast.vue +240 -233
  177. package/components/u-top-tips/types.ts +16 -14
  178. package/components/u-top-tips/u-top-tips.vue +130 -113
  179. package/components/u-tr/types.ts +11 -8
  180. package/components/u-tr/u-tr.vue +39 -24
  181. package/components/u-upload/types.ts +82 -80
  182. package/components/u-upload/u-upload.vue +568 -559
  183. package/components/u-verification-code/types.ts +24 -22
  184. package/components/u-verification-code/u-verification-code.vue +176 -164
  185. package/components/u-waterfall/types.ts +18 -16
  186. package/components/u-waterfall/u-waterfall.vue +187 -175
  187. package/iconfont.css +913 -913
  188. package/index.scss +25 -25
  189. package/index.ts +38 -38
  190. package/libs/config/config.ts +26 -26
  191. package/libs/config/zIndex.ts +37 -37
  192. package/libs/css/color.scss +155 -155
  193. package/libs/css/common.scss +178 -178
  194. package/libs/css/style.components.scss +16 -16
  195. package/libs/css/style.h5.scss +8 -8
  196. package/libs/css/style.mp.scss +72 -72
  197. package/libs/css/style.nvue.scss +15 -15
  198. package/libs/css/style.vue.scss +188 -188
  199. package/libs/function/$parent.ts +24 -24
  200. package/libs/function/addUnit.ts +13 -13
  201. package/libs/function/color.ts +37 -37
  202. package/libs/function/colorGradient.ts +139 -139
  203. package/libs/function/debounce.ts +28 -28
  204. package/libs/function/deepClone.ts +39 -39
  205. package/libs/function/deepMerge.ts +35 -35
  206. package/libs/function/getParent.ts +63 -63
  207. package/libs/function/getRect.ts +26 -26
  208. package/libs/function/guid.ts +42 -42
  209. package/libs/function/md5.ts +403 -403
  210. package/libs/function/parent.ts +21 -21
  211. package/libs/function/queryParams.ts +64 -64
  212. package/libs/function/random.ts +16 -16
  213. package/libs/function/randomArray.ts +11 -11
  214. package/libs/function/route.ts +118 -118
  215. package/libs/function/styleUtils.ts +83 -83
  216. package/libs/function/sys.ts +15 -15
  217. package/libs/function/test.ts +289 -289
  218. package/libs/function/throttle.ts +31 -31
  219. package/libs/function/timeFormat.ts +55 -55
  220. package/libs/function/timeFrom.ts +48 -48
  221. package/libs/function/toast.ts +14 -14
  222. package/libs/function/trim.ts +21 -21
  223. package/libs/function/type2icon.ts +39 -39
  224. package/libs/hooks/index.ts +4 -3
  225. package/libs/hooks/useCompRelation.ts +364 -0
  226. package/libs/hooks/useComponent.ts +759 -759
  227. package/libs/hooks/useEmitter.ts +79 -79
  228. package/libs/hooks/useParent.ts +33 -33
  229. package/libs/hooks/useRect.ts +33 -33
  230. package/libs/index.ts +337 -320
  231. package/libs/request/auto-http.ts +76 -76
  232. package/libs/request/index.ts +242 -242
  233. package/libs/store/index.ts +88 -88
  234. package/libs/util/async-validator.d.ts +75 -75
  235. package/libs/util/async-validator.js +1 -1
  236. package/libs/util/calendar.d.ts +57 -57
  237. package/libs/util/emitter.ts +112 -112
  238. package/libs/util/eventBus.ts +86 -86
  239. package/libs/util/logger.ts +364 -364
  240. package/libs/util/mitt.ts +118 -118
  241. package/libs/util/parent.ts +20 -20
  242. package/package.json +4 -4
  243. package/readme.md +241 -241
  244. package/theme.scss +38 -38
  245. package/types/components.d.ts +97 -97
  246. package/types/global.d.ts +331 -331
  247. package/types/ignore-errors.d.ts +30 -30
  248. package/types/index.d.ts +19 -19
  249. package/types/uni-app.d.ts +63 -63
@@ -1,447 +1,447 @@
1
- <template>
2
- <view
3
- class="u-form-item"
4
- :class="{
5
- 'u-border-bottom': elBorderBottom,
6
- 'u-form-item__border-bottom--error': validateState === 'error' && showError('border-bottom')
7
- }"
8
- >
9
- <view
10
- class="u-form-item__body"
11
- :style="{
12
- flexDirection: elLabelPosition == 'left' ? 'row' : 'column'
13
- }"
14
- >
15
- <!-- 微信小程序中,将一个参数设置空字符串,结果会变成字符串"true" -->
16
- <view
17
- class="u-form-item--left"
18
- :style="{
19
- width: uLabelWidth,
20
- flex: `0 0 ${uLabelWidth}`,
21
- marginBottom: elLabelPosition == 'left' ? 0 : '10rpx'
22
- }"
23
- >
24
- <!-- 为了块对齐 -->
25
- <view class="u-form-item--left__content" v-if="required || leftIcon || label">
26
- <!-- nvue不支持伪元素before -->
27
- <text v-if="required" class="u-form-item--left__content--required">*</text>
28
- <view class="u-form-item--left__content__icon" v-if="leftIcon">
29
- <u-icon :name="leftIcon" :custom-style="leftIconStyle"></u-icon>
30
- </view>
31
- <view
32
- class="u-form-item--left__content__label"
33
- :style="[
34
- elLabelStyle,
35
- {
36
- 'justify-content':
37
- elLabelAlign == 'left'
38
- ? 'flex-start'
39
- : elLabelAlign == 'center'
40
- ? 'center'
41
- : 'flex-end'
42
- }
43
- ]"
44
- >
45
- {{ label }}
46
- </view>
47
- </view>
48
- </view>
49
- <view class="u-form-item--right u-flex">
50
- <view class="u-form-item--right__content">
51
- <view class="u-form-item--right__content__slot">
52
- <slot />
53
- </view>
54
- <view class="u-form-item--right__content__icon u-flex" v-if="$slots.right || rightIcon">
55
- <u-icon :custom-style="rightIconStyle" v-if="rightIcon" :name="rightIcon"></u-icon>
56
- <slot name="right" />
57
- </view>
58
- </view>
59
- </view>
60
- </view>
61
- <view
62
- class="u-form-item__message"
63
- v-if="validateState === 'error' && showError('message')"
64
- :style="{
65
- paddingLeft: elLabelPosition == 'left' ? $u.addUnit(elLabelWidth) : '0'
66
- }"
67
- >{{ validateMessage }}</view
68
- >
69
- </view>
70
- </template>
71
-
72
- <script setup lang="ts">
73
- import { ref, computed, inject, onMounted, onBeforeUnmount, watch, getCurrentInstance, nextTick } from 'vue';
74
- import { $u } from '../..';
75
- import { broadcast } from '../../libs/util/emitter';
76
- // @ts-ignore
77
- import schema from '../../libs/util/async-validator';
78
- import { FormItemProps } from './types';
79
- // 去除警告信息
80
- schema.warning = function () {};
81
-
82
- defineOptions({
83
- name: 'u-form-item'
84
- });
85
-
86
- /**
87
- * form-item 表单item
88
- * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
89
- * @tutorial https://uviewpro.cn/zh/components/form.html
90
- * @property {String} label 左侧提示文字
91
- * @property {Object} prop 表单域model对象的属性名,在使用 validate、resetFields 方法的情况下,该属性是必填的
92
- * @property {Boolean} border-bottom 是否显示表单域的下划线边框
93
- * @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
94
- * @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
95
- * @property {Object} label-style label的样式,对象形式
96
- * @property {String} label-align label的对齐方式
97
- * @property {String} right-icon 右侧自定义字体图标(限uView内置图标)或图片地址
98
- * @property {String} left-icon 左侧自定义字体图标(限uView内置图标)或图片地址
99
- * @property {Object} left-icon-style 左侧图标的样式,对象形式
100
- * @property {Object} right-icon-style 右侧图标的样式,对象形式
101
- * @property {Boolean} required 是否显示左边的"*"号,这里仅起展示作用,如需校验必填,请通过rules配置必填规则(默认false)
102
- * @example <u-form-item label="姓名"><u-input v-model="form.name" /></u-form-item>
103
- */
104
-
105
- const props = defineProps(FormItemProps);
106
-
107
- // inject 父表单实例
108
- let parent = inject<any>('u-form', null);
109
- const instance = getCurrentInstance();
110
-
111
- // 组件状态
112
- const initialValue = ref(''); // 存储初始值,用于重置
113
- const validateState = ref(''); // 校验状态 success/error/validating
114
- const validateMessage = ref(''); // 校验失败的提示语
115
- const errorType = ref<string[]>(['message']); // 错误提示方式,默认 message
116
- const fieldValue = ref(''); // 当前表单项的值
117
- const parentData = ref({
118
- borderBottom: true, // 父表单下划线边框
119
- labelWidth: 90, // 父表单 label 宽度
120
- labelPosition: 'left', // 父表单 label 位置
121
- labelStyle: {}, // 父表单 label 样式
122
- labelAlign: 'left' // 父表单 label 对齐
123
- });
124
-
125
- // 监听校验状态和父表单 errorType 变化
126
- watch(validateState, () => {
127
- broadcastInputError();
128
- });
129
-
130
- // 监听u-form组件的errorType的变化
131
- watch(
132
- () => parent?.errorType,
133
- val => {
134
- if (val) errorType.value = val;
135
- broadcastInputError();
136
- },
137
- { immediate: true }
138
- );
139
-
140
- // 计算属性
141
- const uLabelWidth = computed(() => {
142
- // 如果用户设置label为空字符串(微信小程序空字符串最终会变成字符串的'true'),意味着要将label的位置宽度设置为auto
143
- return elLabelPosition.value == 'left'
144
- ? props.label === 'true' || props.label === ''
145
- ? 'auto'
146
- : $u.addUnit(elLabelWidth.value)
147
- : '100%';
148
- });
149
-
150
- // 显示错误提示
151
- // errorType: ['message', 'toast', 'border-bottom', 'none']
152
- const showError = computed(() => (type: string) => {
153
- // 如果errorType数组中含有none,或者toast提示类型
154
- if (errorType.value.indexOf('none') >= 0) return false;
155
- else if (errorType.value.indexOf(type) >= 0) return true;
156
- else return false;
157
- });
158
-
159
- // label的宽度
160
- const elLabelWidth = computed(() => {
161
- // label默认宽度为90,优先使用本组件的值,如果没有(如果设置为0,也算是配置了值,依然起效),则用u-form的值
162
- return props.labelWidth != 0 && props.labelWidth !== ''
163
- ? props.labelWidth
164
- : parentData.value.labelWidth
165
- ? parentData.value.labelWidth
166
- : 90;
167
- });
168
-
169
- // label的样式
170
- const elLabelStyle = computed(() => {
171
- return Object.keys(props.labelStyle).length
172
- ? props.labelStyle
173
- : parentData.value.labelStyle
174
- ? parentData.value.labelStyle
175
- : {};
176
- });
177
-
178
- // label的位置,左侧或者上方
179
- const elLabelPosition = computed(() => {
180
- return props.labelPosition
181
- ? props.labelPosition
182
- : parentData.value.labelPosition
183
- ? parentData.value.labelPosition
184
- : 'left';
185
- });
186
-
187
- // label的对齐方式
188
- const elLabelAlign = computed(() => {
189
- return props.labelAlign ? props.labelAlign : parentData.value.labelAlign ? parentData.value.labelAlign : 'left';
190
- });
191
-
192
- // label的下划线
193
- const elBorderBottom = computed(() => {
194
- // 子组件的borderBottom默认为空字符串,如果不等于空字符串,意味着子组件设置了值,优先使用子组件的值
195
- return props.borderBottom !== ''
196
- ? props.borderBottom
197
- : parentData.value.borderBottom
198
- ? parentData.value.borderBottom
199
- : true;
200
- });
201
-
202
- // 事件派发/广播工具
203
- function broadcastInputError() {
204
- // 子组件发出事件,第三个参数为true或者false,true代表有错误
205
- if (instance) {
206
- // 这里可用 emitter 工具库的 broadcast 方法
207
- // 子组件发出事件,第三个参数为true或者false,true代表有错误
208
- broadcast(
209
- instance,
210
- 'u-input',
211
- 'on-form-item-error',
212
- validateState.value === 'error' && showError.value('border')
213
- );
214
- }
215
- }
216
-
217
- /**
218
- * 添加表单校验事件监听
219
- */
220
- function setRules() {
221
- // 由于人性化考虑,必填"*"号通过props的required配置,不再通过rules的规则自动生成
222
- // 从父组件u-form拿到当前u-form-item需要验证 的规则
223
- // let rules = this.getRules();
224
- // if (rules.length) {
225
- // this.isRequired = rules.some(rule => {
226
- // // 如果有必填项,就返回,没有的话,就是undefined
227
- // return rule.required;
228
- // });
229
- // }
230
- // // blur事件,失效了
231
- // uni.$on('on-form-blur', onFieldBlur)
232
- // // change事件,失效了
233
- // uni.$on('on-form-change', onFieldChange)
234
- }
235
-
236
- /**
237
- * 获取当前u-form-item的校验规则
238
- */
239
- function getRules() {
240
- // 父组件的所有规则
241
- let rules = parent?.rules?.value || parent?.rules || {};
242
- rules = rules ? rules[props.prop] : [];
243
- // 保证返回的是一个数组形式
244
- return [].concat(rules || []);
245
- }
246
-
247
- // blur事件时进行表单校验
248
- function onFieldBlur() {
249
- validation('blur');
250
- }
251
-
252
- // change事件进行表单校验
253
- function onFieldChange() {
254
- validation('change');
255
- }
256
-
257
- function onFormBlur() {
258
- onFieldBlur();
259
- }
260
-
261
- function onFormChange() {
262
- onFieldChange();
263
- }
264
-
265
- /**
266
- * 过滤出符合要求的rule规则
267
- * @param triggerType 触发类型
268
- */
269
- function getFilteredRule(triggerType = '') {
270
- // 获取所有规则
271
- const rules = getRules();
272
- // 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
273
- if (!triggerType) return rules;
274
- // 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
275
- // 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如['blur','change']
276
- // 某些场景可能的判断规则,可能不存在trigger属性,故先判断是否存在此属性
277
- return rules.filter((res: any) => res.trigger && res.trigger.indexOf(triggerType) !== -1);
278
- }
279
-
280
- /**
281
- * 校验数据
282
- * @param trigger 触发类型
283
- * @param callback 校验回调
284
- */
285
- function validation(trigger: string, callback: (msg: string) => void = () => {}) {
286
- // 检验之前,先获取需要校验的值
287
- fieldValue.value = parent?.model?.[props.prop];
288
- // blur和change是否有当前方式的校验规则
289
- let rules = getFilteredRule(trigger);
290
- // 判断是否有验证规则,如果没有规则,也调用回调方法,否则父组件u-form会因为
291
- // 对count变量的统计错误而无法进入上一层的回调
292
- if (!rules || rules.length === 0) {
293
- callback('');
294
- return;
295
- }
296
- // 设置当前的状态,标识为校验中
297
- validateState.value = 'validating';
298
- // 调用async-validator的方法
299
- let validator = new schema({ [props.prop]: rules });
300
- validator.validate({ [props.prop]: fieldValue.value }, { firstFields: true }, (errors: any, fields: any) => {
301
- // 记录状态和报错信息
302
- validateState.value = !errors ? 'success' : 'error';
303
- validateMessage.value = errors ? errors[0].message : '';
304
- // 调用回调方法
305
- callback(validateMessage.value);
306
- });
307
- }
308
-
309
- /**
310
- * 清空当前的u-form-item
311
- */
312
- function resetField() {
313
- if (parent?.model && props.prop) {
314
- parent.model[props.prop] = initialValue.value;
315
- }
316
- // 设置为`success`状态,只是为了清空错误标记
317
- validateState.value = 'success';
318
- }
319
-
320
- // 组件挂载时注册到父表单
321
- onMounted(() => {
322
- // 支付宝、头条小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用
323
- // 兼容 provide/inject 及 $u.$parent
324
- parent = $u.parentData('u-form', instance);
325
- if (parent) {
326
- // 继承父表单配置
327
- // 历遍parentData中的属性,将parent中的同名属性赋值给parentData
328
- Object.keys(parentData.value).forEach(key => {
329
- parentData.value[key] = parent.props[key];
330
- });
331
- // 如果没有传入prop,或者uForm为空(如果u-form-input单独使用,就不会有uForm注入),就不进行校验
332
- if (props.prop) {
333
- // 将本实例添加到父组件中
334
- parent.addField &&
335
- parent.addField({
336
- validation,
337
- resetField,
338
- prop: props.prop
339
- });
340
- errorType.value = parent.errorType || errorType.value;
341
- // 设置初始值
342
- fieldValue.value = parent.model?.[props.prop];
343
- // 设置初始值
344
- initialValue.value = fieldValue.value;
345
- // 添加表单校验,这里必须要写在$nextTick中,因为u-form的rules是通过ref手动传入的
346
- // 不在$nextTick中的话,可能会造成执行此处代码时,父组件还没通过ref把规则给u-form,导致规则为空
347
- nextTick(() => {
348
- setRules();
349
- });
350
- }
351
- }
352
- });
353
- // 组件销毁前,将实例从u-form的缓存中移除
354
- onBeforeUnmount(() => {
355
- // 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
356
- if (parent && props.prop) {
357
- parent.removeField && parent.removeField({ prop: props.prop });
358
- }
359
- });
360
-
361
- defineExpose({ validation, resetField, onFormBlur, onFormChange });
362
- </script>
363
-
364
- <style lang="scss" scoped>
365
- @import '../../libs/css/style.components.scss';
366
-
367
- .u-form-item {
368
- @include vue-flex;
369
- // align-items: flex-start;
370
- padding: 20rpx 0;
371
- font-size: 28rpx;
372
- color: $u-main-color;
373
- box-sizing: border-box;
374
- line-height: $u-form-item-height;
375
- flex-direction: column;
376
-
377
- &__border-bottom--error:after {
378
- border-color: $u-type-error;
379
- }
380
-
381
- &__body {
382
- @include vue-flex;
383
- }
384
-
385
- &--left {
386
- @include vue-flex;
387
- align-items: center;
388
-
389
- &__content {
390
- position: relative;
391
- @include vue-flex;
392
- align-items: center;
393
- padding-right: 10rpx;
394
- flex: 1;
395
-
396
- &__icon {
397
- margin-right: 8rpx;
398
- }
399
-
400
- &--required {
401
- position: absolute;
402
- left: -16rpx;
403
- vertical-align: middle;
404
- color: $u-type-error;
405
- padding-top: 6rpx;
406
- }
407
-
408
- &__label {
409
- @include vue-flex;
410
- align-items: center;
411
- flex: 1;
412
- }
413
- }
414
- }
415
-
416
- &--right {
417
- flex: 1;
418
-
419
- &__content {
420
- @include vue-flex;
421
- align-items: center;
422
- flex: 1;
423
-
424
- &__slot {
425
- flex: 1;
426
- /* #ifndef MP */
427
- @include vue-flex;
428
- align-items: center;
429
- /* #endif */
430
- }
431
-
432
- &__icon {
433
- margin-left: 10rpx;
434
- color: $u-light-color;
435
- font-size: 30rpx;
436
- }
437
- }
438
- }
439
-
440
- &__message {
441
- font-size: 24rpx;
442
- line-height: 24rpx;
443
- color: $u-type-error;
444
- margin-top: 12rpx;
445
- }
446
- }
447
- </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 class="u-form-item--left__content" v-if="required || leftIcon || label">
31
+ <!-- nvue不支持伪元素before -->
32
+ <text v-if="required" class="u-form-item--left__content--required">*</text>
33
+ <view class="u-form-item--left__content__icon" v-if="leftIcon">
34
+ <u-icon :name="leftIcon" :custom-style="leftIconStyle"></u-icon>
35
+ </view>
36
+ <view
37
+ class="u-form-item--left__content__label"
38
+ :style="[
39
+ elLabelStyle,
40
+ {
41
+ 'justify-content':
42
+ elLabelAlign == 'left'
43
+ ? 'flex-start'
44
+ : elLabelAlign == 'center'
45
+ ? 'center'
46
+ : 'flex-end'
47
+ }
48
+ ]"
49
+ >
50
+ {{ label }}
51
+ </view>
52
+ </view>
53
+ </view>
54
+ <view class="u-form-item--right u-flex">
55
+ <view class="u-form-item--right__content">
56
+ <view class="u-form-item--right__content__slot">
57
+ <slot />
58
+ </view>
59
+ <view class="u-form-item--right__content__icon u-flex" v-if="$slots.right || rightIcon">
60
+ <u-icon :custom-style="rightIconStyle" v-if="rightIcon" :name="rightIcon"></u-icon>
61
+ <slot name="right" />
62
+ </view>
63
+ </view>
64
+ </view>
65
+ </view>
66
+ <view
67
+ class="u-form-item__message"
68
+ v-if="validateState === 'error' && showError('message')"
69
+ :style="{
70
+ paddingLeft: elLabelPosition == 'left' ? $u.addUnit(elLabelWidth) : '0'
71
+ }"
72
+ >{{ validateMessage }}</view
73
+ >
74
+ </view>
75
+ </template>
76
+
77
+ <script lang="ts">
78
+ export default {
79
+ name: 'u-form-item',
80
+ options: {
81
+ addGlobalClass: true,
82
+ // #ifndef MP-TOUTIAO
83
+ virtualHost: true,
84
+ // #endif
85
+ styleIsolation: 'shared'
86
+ }
87
+ };
88
+ </script>
89
+
90
+ <script setup lang="ts">
91
+ import { ref, computed, onMounted, onBeforeUnmount, watch, getCurrentInstance, nextTick } from 'vue';
92
+ import { $u, useChildren } from '../..';
93
+ import { broadcast } from '../../libs/util/emitter';
94
+ // @ts-ignore
95
+ import schema from '../../libs/util/async-validator';
96
+ import { FormItemProps } from './types';
97
+ // 去除警告信息
98
+ schema.warning = function () {};
99
+
100
+ /**
101
+ * form-item 表单item
102
+ * @description 此组件一般用于表单场景,可以配置Input输入框,Select弹出框,进行表单验证等。
103
+ * @tutorial https://uviewpro.cn/zh/components/form.html
104
+ * @property {String} label 左侧提示文字
105
+ * @property {Object} prop 表单域model对象的属性名,在使用 validate、resetFields 方法的情况下,该属性是必填的
106
+ * @property {Boolean} border-bottom 是否显示表单域的下划线边框
107
+ * @property {String} label-position 表单域提示文字的位置,left-左侧,top-上方
108
+ * @property {String Number} label-width 提示文字的宽度,单位rpx(默认90)
109
+ * @property {Object} label-style label的样式,对象形式
110
+ * @property {String} label-align label的对齐方式
111
+ * @property {String} right-icon 右侧自定义字体图标(限uView内置图标)或图片地址
112
+ * @property {String} left-icon 左侧自定义字体图标(限uView内置图标)或图片地址
113
+ * @property {Object} left-icon-style 左侧图标的样式,对象形式
114
+ * @property {Object} right-icon-style 右侧图标的样式,对象形式
115
+ * @property {Boolean} required 是否显示左边的"*"号,这里仅起展示作用,如需校验必填,请通过rules配置必填规则(默认false)
116
+ * @example <u-form-item label="姓名"><u-input v-model="form.name" /></u-form-item>
117
+ */
118
+
119
+ const props = defineProps(FormItemProps);
120
+
121
+ const { parentExposed } = useChildren('u-form-item', 'u-form');
122
+
123
+ const instance = getCurrentInstance();
124
+
125
+ // 组件状态
126
+ const initialValue = ref(''); // 存储初始值,用于重置
127
+ const validateState = ref(''); // 校验状态 success/error/validating
128
+ const validateMessage = ref(''); // 校验失败的提示语
129
+ const errorType = ref<string[]>(['message']); // 错误提示方式,默认 message
130
+ const fieldValue = ref(''); // 当前表单项的值
131
+ const parentData = ref({
132
+ borderBottom: true, // 父表单下划线边框
133
+ labelWidth: 90, // 父表单 label 宽度
134
+ labelPosition: 'left', // 父表单 label 位置
135
+ labelStyle: {}, // 父表单 label 样式
136
+ labelAlign: 'left' // 父表单 label 对齐
137
+ });
138
+
139
+ // 监听校验状态和父表单 errorType 变化
140
+ watch(validateState, () => {
141
+ broadcastInputError();
142
+ });
143
+
144
+ // 监听u-form组件的errorType的变化
145
+ watch(
146
+ () => parentExposed?.value?.props?.errorType,
147
+ val => {
148
+ if (val) errorType.value = val;
149
+ broadcastInputError();
150
+ },
151
+ { immediate: true }
152
+ );
153
+
154
+ // 计算属性
155
+ const uLabelWidth = computed(() => {
156
+ // 如果用户设置label为空字符串(微信小程序空字符串最终会变成字符串的'true'),意味着要将label的位置宽度设置为auto
157
+ return elLabelPosition.value == 'left'
158
+ ? props.label === 'true' || props.label === ''
159
+ ? 'auto'
160
+ : $u.addUnit(elLabelWidth.value)
161
+ : '100%';
162
+ });
163
+
164
+ // 显示错误提示
165
+ // errorType: ['message', 'toast', 'border-bottom', 'none']
166
+ const showError = computed(() => (type: string) => {
167
+ // 如果errorType数组中含有none,或者toast提示类型
168
+ if (errorType.value.indexOf('none') >= 0) return false;
169
+ else if (errorType.value.indexOf(type) >= 0) return true;
170
+ else return false;
171
+ });
172
+
173
+ // label的宽度
174
+ const elLabelWidth = computed(() => {
175
+ // label默认宽度为90,优先使用本组件的值,如果没有(如果设置为0,也算是配置了值,依然起效),则用u-form的值
176
+ return props.labelWidth != 0 && props.labelWidth !== ''
177
+ ? props.labelWidth
178
+ : parentData.value.labelWidth
179
+ ? parentData.value.labelWidth
180
+ : 90;
181
+ });
182
+
183
+ // label的样式
184
+ const elLabelStyle = computed(() => {
185
+ return Object.keys(props.labelStyle).length
186
+ ? props.labelStyle
187
+ : parentData.value.labelStyle
188
+ ? parentData.value.labelStyle
189
+ : {};
190
+ });
191
+
192
+ // label的位置,左侧或者上方
193
+ const elLabelPosition = computed(() => {
194
+ return props.labelPosition
195
+ ? props.labelPosition
196
+ : parentData.value.labelPosition
197
+ ? parentData.value.labelPosition
198
+ : 'left';
199
+ });
200
+
201
+ // label的对齐方式
202
+ const elLabelAlign = computed(() => {
203
+ return props.labelAlign ? props.labelAlign : parentData.value.labelAlign ? parentData.value.labelAlign : 'left';
204
+ });
205
+
206
+ // label的下划线
207
+ const elBorderBottom = computed(() => {
208
+ // 子组件的borderBottom默认为空字符串,如果不等于空字符串,意味着子组件设置了值,优先使用子组件的值
209
+ return props.borderBottom !== ''
210
+ ? props.borderBottom
211
+ : parentData.value.borderBottom
212
+ ? parentData.value.borderBottom
213
+ : true;
214
+ });
215
+
216
+ // 事件派发/广播工具
217
+ function broadcastInputError() {
218
+ // 子组件发出事件,第三个参数为true或者false,true代表有错误
219
+ if (instance) {
220
+ // 这里可用 emitter 工具库的 broadcast 方法
221
+ // 子组件发出事件,第三个参数为true或者false,true代表有错误
222
+ broadcast(
223
+ instance,
224
+ 'u-input',
225
+ 'on-form-item-error',
226
+ validateState.value === 'error' && showError.value('border')
227
+ );
228
+ }
229
+ }
230
+
231
+ /**
232
+ * 获取当前u-form-item的校验规则
233
+ */
234
+ function getRules() {
235
+ // 父组件的所有规则
236
+ let rules = parentExposed?.value?.rules?.value || {};
237
+ rules = rules ? rules[props.prop] : [];
238
+ // 保证返回的是一个数组形式
239
+ return [].concat(rules || []);
240
+ }
241
+
242
+ // blur事件时进行表单校验
243
+ function onFieldBlur() {
244
+ validation('blur');
245
+ }
246
+
247
+ // change事件进行表单校验
248
+ function onFieldChange() {
249
+ validation('change');
250
+ }
251
+
252
+ function onFormBlur() {
253
+ onFieldBlur();
254
+ }
255
+
256
+ function onFormChange() {
257
+ onFieldChange();
258
+ }
259
+
260
+ /**
261
+ * 过滤出符合要求的rule规则
262
+ * @param triggerType 触发类型
263
+ */
264
+ function getFilteredRule(triggerType = '') {
265
+ // 获取所有规则
266
+ const rules = getRules();
267
+ // 整体验证表单时,triggerType为空字符串,此时返回所有规则进行验证
268
+ if (!triggerType) return rules;
269
+ // 历遍判断规则是否有对应的事件,比如blur,change触发等的事件
270
+ // 使用indexOf判断,是因为某些时候设置的验证规则的trigger属性可能为多个,比如['blur','change']
271
+ // 某些场景可能的判断规则,可能不存在trigger属性,故先判断是否存在此属性
272
+ return rules.filter((res: any) => res.trigger && res.trigger.indexOf(triggerType) !== -1);
273
+ }
274
+
275
+ /**
276
+ * 校验数据
277
+ * @param trigger 触发类型
278
+ * @param callback 校验回调
279
+ */
280
+ function validation(trigger: string, callback: (msg: string) => void = () => {}) {
281
+ // 检验之前,先获取需要校验的值
282
+ fieldValue.value = parentExposed?.value?.model?.[props.prop];
283
+ // blur和change是否有当前方式的校验规则
284
+ let rules = getFilteredRule(trigger);
285
+ // 判断是否有验证规则,如果没有规则,也调用回调方法,否则父组件u-form会因为
286
+ // 对count变量的统计错误而无法进入上一层的回调
287
+ if (!rules || rules.length === 0) {
288
+ callback('');
289
+ return;
290
+ }
291
+ // 设置当前的状态,标识为校验中
292
+ validateState.value = 'validating';
293
+ // 调用async-validator的方法
294
+ let validator = new schema({ [props.prop]: rules });
295
+ validator.validate({ [props.prop]: fieldValue.value }, { firstFields: true }, (errors: any, fields: any) => {
296
+ // 记录状态和报错信息
297
+ validateState.value = !errors ? 'success' : 'error';
298
+ validateMessage.value = errors ? errors[0].message : '';
299
+ // 调用回调方法
300
+ callback(validateMessage.value);
301
+ });
302
+ }
303
+
304
+ /**
305
+ * 清空当前的u-form-item
306
+ */
307
+ function resetField() {
308
+ if (parentExposed?.value?.model && props.prop) {
309
+ parentExposed.value.model[props.prop] = initialValue.value;
310
+ }
311
+ // 设置为`success`状态,只是为了清空错误标记
312
+ validateState.value = 'success';
313
+ }
314
+
315
+ // 组件挂载时注册到父表单
316
+ onMounted(() => {
317
+ nextTick(() => {
318
+ if (parentExposed.value) {
319
+ // 继承父表单配置
320
+ // 历遍parentData中的属性,将parent中的同名属性赋值给parentData
321
+ Object.keys(parentData.value).forEach(key => {
322
+ parentData.value[key] = parentExposed?.value?.props[key];
323
+ });
324
+ // 如果没有传入prop,或者uForm为空(如果u-form-input单独使用,就不会有uForm注入),就不进行校验
325
+ if (props.prop) {
326
+ // 将本实例添加到父组件中
327
+ parentExposed?.value?.addField &&
328
+ parentExposed?.value?.addField({
329
+ validation,
330
+ resetField,
331
+ prop: props.prop
332
+ });
333
+ errorType.value = parentExposed?.value?.errorType || errorType.value;
334
+ // 设置初始值
335
+ fieldValue.value = parentExposed?.value?.model?.[props.prop];
336
+ // 设置初始值
337
+ initialValue.value = fieldValue.value;
338
+ }
339
+ }
340
+ });
341
+ });
342
+ // 组件销毁前,将实例从u-form的缓存中移除
343
+ onBeforeUnmount(() => {
344
+ // 如果当前没有prop的话表示当前不要进行删除(因为没有注入)
345
+ if (parentExposed?.value && props.prop) {
346
+ parentExposed?.value?.removeField({ prop: props.prop });
347
+ }
348
+ });
349
+
350
+ defineExpose({
351
+ validation,
352
+ resetField,
353
+ onFormBlur,
354
+ onFormChange
355
+ });
356
+ </script>
357
+
358
+ <style lang="scss" scoped>
359
+ @import '../../libs/css/style.components.scss';
360
+
361
+ .u-form-item {
362
+ @include vue-flex;
363
+ // align-items: flex-start;
364
+ padding: 20rpx 0;
365
+ font-size: 28rpx;
366
+ color: $u-main-color;
367
+ box-sizing: border-box;
368
+ line-height: $u-form-item-height;
369
+ flex-direction: column;
370
+
371
+ &__border-bottom--error:after {
372
+ border-color: $u-type-error;
373
+ }
374
+
375
+ &__border--error {
376
+ :deep(.u-input--border) {
377
+ border-color: $u-type-error !important;
378
+ }
379
+ }
380
+
381
+ &__body {
382
+ @include vue-flex;
383
+ }
384
+
385
+ &--left {
386
+ @include vue-flex;
387
+ align-items: center;
388
+
389
+ &__content {
390
+ position: relative;
391
+ @include vue-flex;
392
+ align-items: center;
393
+ padding-right: 10rpx;
394
+ flex: 1;
395
+
396
+ &__icon {
397
+ margin-right: 8rpx;
398
+ }
399
+
400
+ &--required {
401
+ position: absolute;
402
+ left: -16rpx;
403
+ vertical-align: middle;
404
+ color: $u-type-error;
405
+ padding-top: 6rpx;
406
+ }
407
+
408
+ &__label {
409
+ @include vue-flex;
410
+ align-items: center;
411
+ flex: 1;
412
+ }
413
+ }
414
+ }
415
+
416
+ &--right {
417
+ flex: 1;
418
+
419
+ &__content {
420
+ @include vue-flex;
421
+ align-items: center;
422
+ flex: 1;
423
+
424
+ &__slot {
425
+ flex: 1;
426
+ /* #ifndef MP */
427
+ @include vue-flex;
428
+ align-items: center;
429
+ /* #endif */
430
+ }
431
+
432
+ &__icon {
433
+ margin-left: 10rpx;
434
+ color: $u-light-color;
435
+ font-size: 30rpx;
436
+ }
437
+ }
438
+ }
439
+
440
+ &__message {
441
+ font-size: 24rpx;
442
+ line-height: 24rpx;
443
+ color: $u-type-error;
444
+ margin-top: 12rpx;
445
+ }
446
+ }
447
+ </style>