| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 | 
							- <template>
 
- 	<view class="uni-forms-item"
 
- 		:class="['is-direction-' + localLabelPos ,border?'uni-forms-item--border':'' ,border && isFirstBorder?'is-first-border':'']">
 
- 		<slot name="label">
 
- 			<view class="uni-forms-item__label" :class="{'no-label':!label && !isRequired}"
 
- 				:style="{width:localLabelWidth,justifyContent: localLabelAlign}">
 
- 				<text v-if="isRequired" class="is-required">*</text>
 
- 				<text>{{label}}</text>
 
- 			</view>
 
- 		</slot>
 
- 		<!-- #ifndef APP-NVUE -->
 
- 		<view class="uni-forms-item__content">
 
- 			<slot></slot>
 
- 			<view class="uni-forms-item__error" :class="{'msg--active':msg}">
 
- 				<text>{{msg}}</text>
 
- 			</view>
 
- 		</view>
 
- 		<!-- #endif -->
 
- 		<!-- #ifdef APP-NVUE -->
 
- 		<view class="uni-forms-item__nuve-content">
 
- 			<view class="uni-forms-item__content">
 
- 				<slot></slot>
 
- 			</view>
 
- 			<view class="uni-forms-item__error" :class="{'msg--active':msg}">
 
- 				<text class="error-text">{{msg}}</text>
 
- 			</view>
 
- 		</view>
 
- 		<!-- #endif -->
 
- 	</view>
 
- </template>
 
- <script>
 
- 	/**
 
- 	 * uni-fomrs-item 表单子组件
 
- 	 * @description uni-fomrs-item 表单子组件,提供了基础布局已经校验能力
 
- 	 * @tutorial https://ext.dcloud.net.cn/plugin?id=2773
 
- 	 * @property {Boolean} required 是否必填,左边显示红色"*"号
 
- 	 * @property {String } 	label 				输入框左边的文字提示
 
- 	 * @property {Number } 	labelWidth 			label的宽度,单位px(默认65)
 
- 	 * @property {String } 	labelAlign = [left|center|right] label的文字对齐方式(默认left)
 
- 	 * 	@value left		label 左侧显示
 
- 	 * 	@value center	label 居中
 
- 	 * 	@value right	label 右侧对齐
 
- 	 * @property {String } 	errorMessage 		显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
 
- 	 * @property {String } 	name 				表单域的属性名,在使用校验规则时必填
 
- 	 * @property {String } 	leftIcon 			【1.4.0废弃】label左边的图标,限 uni-ui 的图标名称
 
- 	 * @property {String } 	iconColor 		【1.4.0废弃】左边通过icon配置的图标的颜色(默认#606266)
 
- 	 * @property {String} validateTrigger = [bind|submit|blur]	【1.4.0废弃】校验触发器方式 默认 submit
 
- 	 * 	@value bind 	发生变化时触发
 
- 	 * 	@value submit 提交时触发
 
- 	 * 	@value blur 	失去焦点触发
 
- 	 * @property {String } 	labelPosition = [top|left] 【1.4.0废弃】label的文字的位置(默认left)
 
- 	 * 	@value top	顶部显示 label
 
- 	 * 	@value left	左侧显示 label
 
- 	 */
 
- 	export default {
 
- 		name: 'uniFormsItem',
 
- 		options: {
 
- 			virtualHost: true
 
- 		},
 
- 		provide() {
 
- 			return {
 
- 				uniFormItem: this
 
- 			}
 
- 		},
 
- 		inject: {
 
- 			form: {
 
- 				from: 'uniForm',
 
- 				default: null
 
- 			},
 
- 		},
 
- 		props: {
 
- 			// 表单校验规则
 
- 			rules: {
 
- 				type: Array,
 
- 				default () {
 
- 					return null;
 
- 				}
 
- 			},
 
- 			// 表单域的属性名,在使用校验规则时必填
 
- 			name: {
 
- 				type: [String, Array],
 
- 				default: ''
 
- 			},
 
- 			required: {
 
- 				type: Boolean,
 
- 				default: false
 
- 			},
 
- 			label: {
 
- 				type: String,
 
- 				default: ''
 
- 			},
 
- 			// label的宽度 ,默认 80
 
- 			labelWidth: {
 
- 				type: [String, Number],
 
- 				default: ''
 
- 			},
 
- 			// label 居中方式,默认 left 取值 left/center/right
 
- 			labelAlign: {
 
- 				type: String,
 
- 				default: ''
 
- 			},
 
- 			// 强制显示错误信息
 
- 			errorMessage: {
 
- 				type: [String, Boolean],
 
- 				default: ''
 
- 			},
 
- 			// 1.4.0 弃用,统一使用 form 的校验时机
 
- 			// validateTrigger: {
 
- 			// 	type: String,
 
- 			// 	default: ''
 
- 			// },
 
- 			// 1.4.0 弃用,统一使用 form 的label 位置
 
- 			// labelPosition: {
 
- 			// 	type: String,
 
- 			// 	default: ''
 
- 			// },
 
- 			// 1.4.0 以下属性已经废弃,请使用  #label 插槽代替
 
- 			leftIcon: String,
 
- 			iconColor: {
 
- 				type: String,
 
- 				default: '#606266'
 
- 			},
 
- 		},
 
- 		data() {
 
- 			return {
 
- 				errMsg: '',
 
- 				isRequired: false,
 
- 				userRules: null,
 
- 				localLabelAlign: 'left',
 
- 				localLabelWidth: '65px',
 
- 				localLabelPos: 'left',
 
- 				border: false,
 
- 				isFirstBorder: false,
 
- 			};
 
- 		},
 
- 		computed: {
 
- 			// 处理错误信息
 
- 			msg() {
 
- 				return this.errorMessage || this.errMsg;
 
- 			}
 
- 		},
 
- 		watch: {
 
- 			// 规则发生变化通知子组件更新
 
- 			'form.formRules'(val) {
 
- 				// TODO 处理头条vue3 watch不生效的问题
 
- 				// #ifndef MP-TOUTIAO
 
- 				this.init()
 
- 				// #endif
 
- 			},
 
- 			'form.labelWidth'(val) {
 
- 				// 宽度
 
- 				this.localLabelWidth = this._labelWidthUnit(val)
 
- 			},
 
- 			'form.labelPosition'(val) {
 
- 				// 标签位置
 
- 				this.localLabelPos = this._labelPosition()
 
- 			},
 
- 			'form.labelAlign'(val) {
 
- 			}
 
- 		},
 
- 		created() {
 
- 			this.init(true)
 
- 			if (this.name && this.form) {
 
- 				// TODO 处理头条vue3 watch不生效的问题
 
- 				// #ifdef MP-TOUTIAO
 
- 				this.$watch('form.formRules', () => {
 
- 					this.init()
 
- 				})
 
- 				// #endif
 
- 				// 监听变化
 
- 				this.$watch(
 
- 					() => {
 
- 						const val = this.form._getDataValue(this.name, this.form.localData)
 
- 						return val
 
- 					},
 
- 					(value, oldVal) => {
 
- 						const isEqual = this.form._isEqual(value, oldVal)
 
- 						// 简单判断前后值的变化,只有发生变化才会发生校验
 
- 						// TODO  如果 oldVal = undefined ,那么大概率是源数据里没有值导致 ,这个情况不哦校验 ,可能不严谨 ,需要在做观察
 
- 						// fix by mehaotian 暂时取消 && oldVal !== undefined ,如果formData 中不存在,可能会不校验
 
- 						if (!isEqual) {
 
- 							const val = this.itemSetValue(value)
 
- 							this.onFieldChange(val, false)
 
- 						}
 
- 					}, {
 
- 						immediate: false
 
- 					}
 
- 				);
 
- 			}
 
- 		},
 
- 		// #ifndef VUE3
 
- 		destroyed() {
 
- 			if (this.__isUnmounted) return
 
- 			this.unInit()
 
- 		},
 
- 		// #endif
 
- 		// #ifdef VUE3
 
- 		unmounted() {
 
- 			this.__isUnmounted = true
 
- 			this.unInit()
 
- 		},
 
- 		// #endif
 
- 		methods: {
 
- 			/**
 
- 			 * 外部调用方法
 
- 			 * 设置规则 ,主要用于小程序自定义检验规则
 
- 			 * @param {Array} rules 规则源数据
 
- 			 */
 
- 			setRules(rules = null) {
 
- 				this.userRules = rules
 
- 				this.init(false)
 
- 			},
 
- 			// 兼容老版本表单组件
 
- 			setValue() {
 
- 				// console.log('setValue 方法已经弃用,请使用最新版本的 uni-forms 表单组件以及其他关联组件。');
 
- 			},
 
- 			/**
 
- 			 * 外部调用方法
 
- 			 * 校验数据
 
- 			 * @param {any} value 需要校验的数据
 
- 			 * @param {boolean} 是否立即校验
 
- 			 * @return {Array|null} 校验内容
 
- 			 */
 
- 			async onFieldChange(value, formtrigger = true) {
 
- 				const {
 
- 					formData,
 
- 					localData,
 
- 					errShowType,
 
- 					validateCheck,
 
- 					validateTrigger,
 
- 					_isRequiredField,
 
- 					_realName
 
- 				} = this.form
 
- 				const name = _realName(this.name)
 
- 				if (!value) {
 
- 					value = this.form.formData[name]
 
- 				}
 
- 				// fixd by mehaotian 不在校验前清空信息,解决闪屏的问题
 
- 				// this.errMsg = '';
 
- 				// fix by mehaotian 解决没有检验规则的情况下,抛出错误的问题
 
- 				const ruleLen = this.itemRules.rules && this.itemRules.rules.length
 
- 				if (!this.validator || !ruleLen || ruleLen === 0) return;
 
- 				// 检验时机
 
- 				// let trigger = this.isTrigger(this.itemRules.validateTrigger, this.validateTrigger, validateTrigger);
 
- 				const isRequiredField = _isRequiredField(this.itemRules.rules || []);
 
- 				let result = null;
 
- 				// 只有等于 bind 时 ,才能开启时实校验
 
- 				if (validateTrigger === 'bind' || formtrigger) {
 
- 					// 校验当前表单项
 
- 					result = await this.validator.validateUpdate({
 
- 							[name]: value
 
- 						},
 
- 						formData
 
- 					);
 
- 					// 判断是否必填,非必填,不填不校验,填写才校验 ,暂时只处理 undefined  和空的情况
 
- 					if (!isRequiredField && (value === undefined || value === '')) {
 
- 						result = null;
 
- 					}
 
- 					// 判断错误信息显示类型
 
- 					if (result && result.errorMessage) {
 
- 						if (errShowType === 'undertext') {
 
- 							// 获取错误信息
 
- 							this.errMsg = !result ? '' : result.errorMessage;
 
- 						}
 
- 						if (errShowType === 'toast') {
 
- 							uni.showToast({
 
- 								title: result.errorMessage || '校验错误',
 
- 								icon: 'none'
 
- 							});
 
- 						}
 
- 						if (errShowType === 'modal') {
 
- 							uni.showModal({
 
- 								title: '提示',
 
- 								content: result.errorMessage || '校验错误'
 
- 							});
 
- 						}
 
- 					} else {
 
- 						this.errMsg = ''
 
- 					}
 
- 					// 通知 form 组件更新事件
 
- 					validateCheck(result ? result : null)
 
- 				} else {
 
- 					this.errMsg = ''
 
- 				}
 
- 				return result ? result : null;
 
- 			},
 
- 			/**
 
- 			 * 初始组件数据
 
- 			 */
 
- 			init(type = false) {
 
- 				const {
 
- 					validator,
 
- 					formRules,
 
- 					childrens,
 
- 					formData,
 
- 					localData,
 
- 					_realName,
 
- 					labelWidth,
 
- 					_getDataValue,
 
- 					_setDataValue
 
- 				} = this.form || {}
 
- 				// 对齐方式
 
- 				this.localLabelAlign = this._justifyContent()
 
- 				// 宽度
 
- 				this.localLabelWidth = this._labelWidthUnit(labelWidth)
 
- 				// 标签位置
 
- 				this.localLabelPos = this._labelPosition()
 
- 				this.isRequired = this.required
 
- 				// 将需要校验的子组件加入form 队列
 
- 				this.form && type && childrens.push(this)
 
- 				if (!validator || !formRules) return
 
- 				// 判断第一个 item
 
- 				if (!this.form.isFirstBorder) {
 
- 					this.form.isFirstBorder = true;
 
- 					this.isFirstBorder = true;
 
- 				}
 
- 				// 判断 group 里的第一个 item
 
- 				if (this.group) {
 
- 					if (!this.group.isFirstBorder) {
 
- 						this.group.isFirstBorder = true;
 
- 						this.isFirstBorder = true;
 
- 					}
 
- 				}
 
- 				this.border = this.form.border;
 
- 				// 获取子域的真实名称
 
- 				const name = _realName(this.name)
 
- 				const itemRule = this.userRules || this.rules
 
- 				if (typeof formRules === 'object' && itemRule) {
 
- 					// 子规则替换父规则
 
- 					formRules[name] = {
 
- 						rules: itemRule
 
- 					}
 
- 					validator.updateSchema(formRules);
 
- 				}
 
- 				// 注册校验规则
 
- 				const itemRules = formRules[name] || {}
 
- 				this.itemRules = itemRules
 
- 				// 注册校验函数
 
- 				this.validator = validator
 
- 				// 默认值赋予
 
- 				this.itemSetValue(_getDataValue(this.name, localData))
 
- 				this.isRequired = this._isRequired()
 
- 			},
 
- 			unInit() {
 
- 				if (this.form) {
 
- 					const {
 
- 						childrens,
 
- 						formData,
 
- 						_realName
 
- 					} = this.form
 
- 					childrens.forEach((item, index) => {
 
- 						if (item === this) {
 
- 							this.form.childrens.splice(index, 1)
 
- 							delete formData[_realName(item.name)]
 
- 						}
 
- 					})
 
- 				}
 
- 			},
 
- 			// 设置item 的值
 
- 			itemSetValue(value) {
 
- 				const name = this.form._realName(this.name)
 
- 				const rules = this.itemRules.rules || []
 
- 				const val = this.form._getValue(name, value, rules)
 
- 				this.form._setDataValue(name, this.form.formData, val)
 
- 				return val
 
- 			},
 
- 			/**
 
- 			 * 移除该表单项的校验结果
 
- 			 */
 
- 			clearValidate() {
 
- 				this.errMsg = '';
 
- 			},
 
- 			// 是否显示星号
 
- 			_isRequired() {
 
- 				// TODO 不根据规则显示 星号,考虑后续兼容
 
- 				// if (this.form) {
 
- 				// 	if (this.form._isRequiredField(this.itemRules.rules || []) && this.required) {
 
- 				// 		return true
 
- 				// 	}
 
- 				// 	return false
 
- 				// }
 
- 				return this.required
 
- 			},
 
- 			// 处理对齐方式
 
- 			_justifyContent() {
 
- 				if (this.form) {
 
- 					const {
 
- 						labelAlign
 
- 					} = this.form
 
- 					let labelAli = this.labelAlign ? this.labelAlign : labelAlign;
 
- 					if (labelAli === 'left') return 'flex-start';
 
- 					if (labelAli === 'center') return 'center';
 
- 					if (labelAli === 'right') return 'flex-end';
 
- 				}
 
- 				return 'flex-start';
 
- 			},
 
- 			// 处理 label宽度单位 ,继承父元素的值
 
- 			_labelWidthUnit(labelWidth) {
 
- 				// if (this.form) {
 
- 				// 	const {
 
- 				// 		labelWidth
 
- 				// 	} = this.form
 
- 				return this.num2px(this.labelWidth ? this.labelWidth : (labelWidth || (this.label ? 65 : 'auto')))
 
- 				// }
 
- 				// return '65px'
 
- 			},
 
- 			// 处理 label 位置
 
- 			_labelPosition() {
 
- 				if (this.form) return this.form.labelPosition || 'left'
 
- 				return 'left'
 
- 			},
 
- 			/**
 
- 			 * 触发时机
 
- 			 * @param {Object} rule 当前规则内时机
 
- 			 * @param {Object} itemRlue 当前组件时机
 
- 			 * @param {Object} parentRule 父组件时机
 
- 			 */
 
- 			isTrigger(rule, itemRlue, parentRule) {
 
- 				//  bind  submit
 
- 				if (rule === 'submit' || !rule) {
 
- 					if (rule === undefined) {
 
- 						if (itemRlue !== 'bind') {
 
- 							if (!itemRlue) {
 
- 								return parentRule === '' ? 'bind' : 'submit';
 
- 							}
 
- 							return 'submit';
 
- 						}
 
- 						return 'bind';
 
- 					}
 
- 					return 'submit';
 
- 				}
 
- 				return 'bind';
 
- 			},
 
- 			num2px(num) {
 
- 				if (typeof num === 'number') {
 
- 					return `${num}px`
 
- 				}
 
- 				return num
 
- 			}
 
- 		}
 
- 	};
 
- </script>
 
- <style lang="scss">
 
- 	.uni-forms-item {
 
- 		position: relative;
 
- 		display: flex;
 
- 		/* #ifdef APP-NVUE */
 
- 		// 在 nvue 中,使用 margin-bottom error 信息会被隐藏
 
- 		padding-bottom: 22px;
 
- 		/* #endif */
 
- 		/* #ifndef APP-NVUE */
 
- 		margin-bottom: 22px;
 
- 		/* #endif */
 
- 		flex-direction: row;
 
- 		&__label {
 
- 			display: flex;
 
- 			flex-direction: row;
 
- 			align-items: center;
 
- 			text-align: left;
 
- 			font-size: 14px;
 
- 			color: #606266;
 
- 			height: 36px;
 
- 			padding: 0 12px 0 0;
 
- 			/* #ifndef APP-NVUE */
 
- 			vertical-align: middle;
 
- 			flex-shrink: 0;
 
- 			/* #endif */
 
- 			/* #ifndef APP-NVUE */
 
- 			box-sizing: border-box;
 
- 			/* #endif */
 
- 			&.no-label {
 
- 				padding: 0;
 
- 			}
 
- 		}
 
- 		&__content {
 
- 			/* #ifndef MP-TOUTIAO */
 
- 			// display: flex;
 
- 			// align-items: center;
 
- 			/* #endif */
 
- 			position: relative;
 
- 			font-size: 14px;
 
- 			flex: 1;
 
- 			/* #ifndef APP-NVUE */
 
- 			box-sizing: border-box;
 
- 			/* #endif */
 
- 			flex-direction: row;
 
- 			/* #ifndef APP || H5 || MP-WEIXIN || APP-NVUE */
 
- 			// TODO 因为小程序平台会多一层标签节点 ,所以需要在多余节点继承当前样式
 
- 			&>uni-easyinput,
 
- 			&>uni-data-picker {
 
- 				width: 100%;
 
- 			}
 
- 			/* #endif */
 
- 		}
 
- 		& .uni-forms-item__nuve-content {
 
- 			display: flex;
 
- 			flex-direction: column;
 
- 			flex: 1;
 
- 		}
 
- 		&__error {
 
- 			color: #f56c6c;
 
- 			font-size: 12px;
 
- 			line-height: 1;
 
- 			padding-top: 4px;
 
- 			position: absolute;
 
- 			/* #ifndef APP-NVUE */
 
- 			top: 100%;
 
- 			left: 0;
 
- 			transition: transform 0.3s;
 
- 			transform: translateY(-100%);
 
- 			/* #endif */
 
- 			/* #ifdef APP-NVUE */
 
- 			bottom: 5px;
 
- 			/* #endif */
 
- 			opacity: 0;
 
- 			.error-text {
 
- 				// 只有 nvue 下这个样式才生效
 
- 				color: #f56c6c;
 
- 				font-size: 12px;
 
- 			}
 
- 			&.msg--active {
 
- 				opacity: 1;
 
- 				transform: translateY(0%);
 
- 			}
 
- 		}
 
- 		// 位置修饰样式
 
- 		&.is-direction-left {
 
- 			flex-direction: row;
 
- 		}
 
- 		&.is-direction-top {
 
- 			flex-direction: column;
 
- 			.uni-forms-item__label {
 
- 				padding: 0 0 8px;
 
- 				line-height: 1.5715;
 
- 				text-align: left;
 
- 				/* #ifndef APP-NVUE */
 
- 				white-space: initial;
 
- 				/* #endif */
 
- 			}
 
- 		}
 
- 		.is-required {
 
- 			// color: $uni-color-error;
 
- 			color: #dd524d;
 
- 			font-weight: bold;
 
- 		}
 
- 	}
 
- 	.uni-forms-item--border {
 
- 		margin-bottom: 0;
 
- 		padding: 10px 0;
 
- 		// padding-bottom: 0;
 
- 		border-top: 1px #eee solid;
 
- 		/* #ifndef APP-NVUE */
 
- 		.uni-forms-item__content {
 
- 			flex-direction: column;
 
- 			justify-content: flex-start;
 
- 			align-items: flex-start;
 
- 			.uni-forms-item__error {
 
- 				position: relative;
 
- 				top: 5px;
 
- 				left: 0;
 
- 				padding-top: 0;
 
- 			}
 
- 		}
 
- 		/* #endif */
 
- 		/* #ifdef APP-NVUE */
 
- 		display: flex;
 
- 		flex-direction: column;
 
- 		.uni-forms-item__error {
 
- 			position: relative;
 
- 			top: 0px;
 
- 			left: 0;
 
- 			padding-top: 0;
 
- 			margin-top: 5px;
 
- 		}
 
- 		/* #endif */
 
- 	}
 
- 	.is-first-border {
 
- 		/* #ifndef APP-NVUE */
 
- 		border: none;
 
- 		/* #endif */
 
- 		/* #ifdef APP-NVUE */
 
- 		border-width: 0;
 
- 		/* #endif */
 
- 	}
 
- </style>
 
 
  |