/**
|
* CFormItem - 表单套装组件,单项包囊器
|
* @author Tevin
|
* @tutorial rules see https://github.com/yiminghe/async-validator#type
|
*/
|
|
<template>
|
<view
|
class="c-form-item"
|
ref="formItem"
|
>
|
<slot :itemRes="itemRes" />
|
</view>
|
</template>
|
|
<script>
|
import { $ } from '@tarojs/extend';
|
import Schema from 'async-validator';
|
import { validateMsgs } from './validateMsgs.js';
|
|
export default {
|
name: 'CFormItem',
|
props: {
|
// 表单数据资源(表单组件内部机制专用)
|
formRes: Object,
|
// 表单项字段键名
|
name: String,
|
// 表单项中文名
|
label: String,
|
// 表单项验证规则之是否必填
|
required: Boolean,
|
// 表单项验证规则之复合规则
|
// 例如:[{type:'string', min: 2, max: 100}]
|
// 常用项:
|
// type string 类型,常见有 string、number、boolean、array、object、url、email
|
// len number string 类型时为字符串长度;number 类型时为确定数字; array 类型时为数组长度
|
// max number 必须设置 type:string 类型为字符串最大长度;number 类型时为最大值;array 类型时为数组最大长度
|
// min number 必须设置 type:string 类型为字符串最小长度;number 类型时为最小值;array 类型时为数组最小长度
|
// pattern RegExp 正则表达式匹配
|
// required boolean 是否为必选字段
|
// transform (value) => any 将字段值转换成目标值后进行校验
|
// message string 错误信息,不设置时会通过模板自动生成
|
rules: Array,
|
// 表单是否禁用
|
disabled: Boolean,
|
},
|
data() {
|
return {
|
error: false,
|
errTimer: -1,
|
};
|
},
|
computed: {
|
itemRes() {
|
return {
|
formData: this.formRes.formData,
|
name: this.name,
|
label: this.label,
|
required: this.isRequired,
|
disabled: this.disabled,
|
error: this.error,
|
onChange: evt => this.onChange(evt),
|
};
|
},
|
isRequired() {
|
if (this.required) {
|
return true;
|
} else {
|
if (!this.rules || this.rules.length === 0) {
|
return false;
|
} else if (this.rules.length > 0) {
|
for (let rule of this.rules) {
|
if (rule.required) {
|
return true;
|
}
|
}
|
return false;
|
}
|
}
|
},
|
},
|
methods: {
|
onChange(evt) {
|
// 当类型为 object 时,必须为简单 object
|
if (Object.prototype.toString.call(evt) === '[object Object]') {
|
const hasOwn = Object.prototype.hasOwnProperty;
|
if (
|
evt.constructor &&
|
!hasOwn.call(evt, 'constructor') &&
|
!hasOwn.call(evt.constructor.prototype, 'isPrototypeOf')
|
) {
|
throw new Error(
|
'错误的表单项 onChange 参数类型!(At: ' + this.name + ')'
|
);
|
}
|
}
|
// 未改变值不触发
|
if (this.formRes.formData[this.name] === evt) {
|
return;
|
}
|
this.formRes.$handleChange({
|
[this.name]: evt,
|
});
|
},
|
$setError() {
|
this.error = true;
|
clearTimeout(this.errTimer);
|
this.errTimer = setTimeout(() => {
|
this.error = false;
|
}, 5000);
|
},
|
},
|
mounted() {
|
this.$nextTick(() => {
|
// 注册验证
|
this.formRes.$regItemValidator(this.name, (validateType, errMsg) => {
|
// 直接设定错误
|
if (validateType === 'setError') {
|
this.$setError();
|
return new Promise((resolve, reject) => {
|
$(this.$refs.formItem)
|
.offset()
|
.then(offset => {
|
resolve({
|
name: this.name,
|
passed: false,
|
msg: errMsg,
|
offset,
|
});
|
});
|
});
|
}
|
// 未设置验证
|
else if (!this.required && !this.rules) {
|
this.error = false;
|
return Promise.resolve({
|
name: this.name,
|
passed: true,
|
});
|
}
|
// 正常验证
|
else {
|
// 验证规则
|
const descriptor = this.rules || [];
|
if (this.required) {
|
descriptor.unshift({
|
required: true,
|
});
|
}
|
const validator = new Schema({
|
[this.name]: descriptor,
|
});
|
// 汉化通用验证消息
|
validator.messages(validateMsgs);
|
return new Promise((resolve, reject) => {
|
validator
|
.validate({
|
[this.name]: this.formRes.formData[this.name],
|
})
|
.then(
|
res => {
|
this.error = false;
|
resolve({
|
name: this.name,
|
passed: true,
|
});
|
},
|
({ errors, fields }) => {
|
if (validateType !== 'msgOnly') {
|
this.$setError();
|
}
|
$(this.$refs.formItem)
|
.offset()
|
.then(offset => {
|
resolve({
|
name: this.name,
|
passed: false,
|
msg: errors[0].message.replace(
|
this.name,
|
this.label || this.name
|
),
|
offset,
|
});
|
});
|
}
|
);
|
});
|
}
|
});
|
});
|
},
|
beforeDestroy() {
|
this.formRes.$regItemValidator(this.name, null);
|
},
|
};
|
</script>
|