前提:其实这个很久之前就想写了,但是因为最近一直在搞vue3,就拖到现在了。Taro UI对表单的验证不是很友好,所以写了个表单组件,涵盖验证功能
一、安装async-validator(其实是用了async-validator这个依赖)
npm install async-validator -s
二、引入async-validator,封个校验方法(validate.ts)
import Schema from 'async-validator'
export const onValidateField = (value, rules) => {
const validator = new Schema(rules);
return new Promise<any>((resolve) => {
// @ts-ignore
validator.validate(value, (errors, fields) => {
if (errors) {
resolve(errors);
} else {
const errMsg = Object.keys(rules).map((key) => {
return {
field: key,
message: undefined
};
});
resolve(errMsg);
}
});
});
};
三、封装表单组件(cForm.tsx)
import { AtForm, AtInput, AtList, AtListItem, AtButton } from 'taro-ui'
import { View } from '@tarojs/components'
import { Picker } from '@tarojs/components'
import { useState } from 'react'
import { onValidateField } from '../../utils/validate'
import { dateFormatString } from '../../utils/date'
import './cform.scss'
export default function cForm(props) {
const { info, formTitle, setForm, rules, submitForm } = props
const [formInfo, setFormInfo] = useState(info)
const handleChange = (item) => { // 事件
return async evt => {
let value
switch (item.type) {
case 'picker': case 'date':
value = evt.detail.value
break
default:
value = evt
break
}
setFormInfo({ ...formInfo, [item.id]: value })
setForm({ ...formInfo, [item.id]: value })
cehckOneRules(item.id, value)
}
}
const cehckOneRules = async (label, value) => { // 表单验证
if (rules[label]) {
const errors = await onValidateField(
{ [label]: value },
{ [label]: rules[label] },
)
setErrorMsgList(errors, value)
}
}
const [msgList, setMsgList] = useState({}) // 错误提示
const setErrorMsgList = (errors, value) => {
const msglist = {}
errors?.map((error: { field: React.ReactText; message: any }) => {
if (error.field === 'sfzh' && !error.message && value) {
const csrq = value.length == 18 ? dateFormatString(value.substring(6, 14)) : dateFormatString(value.substring(6, 12))
const num = value.length == 18 ? parseInt(value.substring(16, 17)) % 2 : parseInt(value.substring(14, 15)) % 2
const xb = num ? 0 : 1
setFormInfo({ ...formInfo, sfzh: value, csrq, xb })
setForm({ ...formInfo, sfzh: value, csrq, xb })
}
msglist[error.field] = error?.message || ""
})
setMsgList({ ...msgList, ...msglist })
}
const handleClick = async () => { // 提交
const errors = await onValidateField(formInfo, rules)
setErrorMsgList(errors, '')
let valid = !errors || errors.filter((error) => error.message).length <= 0
if (!valid) {
return
}
submitForm()
}
const contentElemnt = formTitle.map(item => { // 表单组件
switch (item.type) {
case 'text':
return <View key={item.id}>
<AtInput
name={item.id}
title={item.name}
type='text'
value={formInfo[item.id]}
onErrorClick={item.onErrorClick}
onChange={handleChange(item)}
disabled={item.disabled}
/>
{msgList[item.id] ? (<View className='font-red'>{msgList[item.id]}</View>) : ''}
</View>
case 'picker':
return <View key={item.id}>
<Picker mode='selector' value={formInfo[item.id]} range={item.range} rangeKey={item.rangeKey} disabled={item.disabled} onChange={handleChange(item)}>
<AtList>
<AtListItem
title={item.name}
extraText={item.range.find(range => { return formInfo[item.id] == range.id }) && item.range.find(range => { return formInfo[item.id] == range.id }).name}
/>
</AtList>
</Picker>
{msgList[item.id] ? (<View className='font-red'>{msgList[item.id]}</View>) : ''}
</View>
case 'date':
return <View key={item.id}>
<Picker key={item.id} mode='date' value={formInfo[item.id]} disabled={item.disabled} onChange={handleChange(item)}>
<AtList>
<AtListItem title={item.name} extraText={formInfo[item.id]} />
</AtList>
</Picker>
{msgList[item.id] ? (<View className='font-red'>{msgList[item.id]}</View>) : ''}
</View>
}
})
return (
<View>
<AtForm className='cform'>
{contentElemnt}
</AtForm>
<AtButton type='primary' className='mt50 ml30 mr30 center' onClick={handleClick}>下一步</AtButton>
</View>
)
}
四、界面引入
import { Component, useRef } from 'react'
import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'
import CForm from '../../components/cform/cform'
import { collectStuInfo } from '../../api/collect-info'
import { getLocalStorage } from '../../utils/auth'
import './collect-info.scss'
interface IForm {
id: string;
name: string;
type: string;
range?: Array<{ id: string, name: string }>;
rangeKey?: string;
placeholder?: string;
disabled?: boolean;
}
interface IStudent {
ksxm: string;
xb: number | string;
zjlxm: string;
yxjssj: string;
}
interface IState {
student: IStudent;
formTitle: Array<IForm>;
rules: object;
msgList: object;
}
export default class Index extends Component {
state: IState = {
student: {
ksxm: '',
xb: '',
zjlxm: '1',
yxjssj: ''
},
formTitle: [
{ id: 'ksxm', name: '姓名', type: 'text', placeholder: '请输入' },
{
id: 'zjlxm',
name: '证件类型',
type: 'picker',
range: [
{ id: '1', name: '居民身份证' },
{ id: '2', name: '军官证' }
],
rangeKey: 'name',
placeholder: '请选择'
},
{ id: 'xb', name: '性别', type: 'picker', range: [{ id: '0', name: '男' }, { id: '1', name: '女' }], rangeKey: 'name', placeholder: '根据身份证获取', disabled: true },
{ id: 'yxjssj', name: '有效结束期限', type: 'date', placeholder: '请选择' }
],
rules: {
ksxm: [
{ required: true, message: "必填" },
{ max: 20, message: "长度在1-20", }
],
yxjssj: [
{ required: true, message: "必选" },
{
validator: (rule, value) => {
return new Date(value) >= new Date(this.state.student.yxqssj);
},
message: "截止日期必须小于起始日期"
}
]
},
msgList: {}
}
componentDidMount() {
const user = getLocalStorage('user')
this.setState({ student: user })
}
async submitForm() {
const { student } = this.state
const user = getLocalStorage('user')
const params = {
...student,
}
const res: any = await collectStuInfo(params).catch(err => {
console.log(err)
})
if (res) {
Taro.redirectTo({
url: `/pages/photo-specification/photo-specification`
})
}
}
setForm(student) {
this.setState({ student })
}
render() {
const { student, formTitle, msgList, rules } = this.state
return (
<View className='collect-info fixed'>
<View className='content'>
<CForm info={student} formTitle={formTitle} msgList={msgList} rules={rules} setForm={this.setForm.bind(this)} submitForm={this.submitForm.bind(this)}></CForm>
</View>
</View>
)
}
}
结束语:上面就是一个完整的表单验证啦