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