当前位置: 首页 > 工具软件 > Taro UI > 使用案例 >

Taro UI 表单验证

傅阿苏
2023-12-01

前提:其实这个很久之前就想写了,但是因为最近一直在搞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>
    )
  }
}

结束语:上面就是一个完整的表单验证啦

 类似资料: