<script setup>
|
import { ref, provide } from 'vue'
|
import { validationRules, combineRule } from '../../lib/js/rule.js'
|
const props = defineProps({
|
model: '',
|
rules: {
|
type: [Object],
|
},
|
checkAll: { // 是否检查所有,关闭:按顺序检查
|
type: [Boolean, Number, String],
|
default: true
|
},
|
showError: { // 是否显示错误信息
|
type: Boolean,
|
default: true
|
},
|
toastError: { // toast提示错误信息
|
type: Boolean,
|
default: false
|
},
|
errorStyle: {
|
type: Object,
|
default () {
|
return {
|
fontSize: '24rpx',
|
color: 'RGB(234, 88, 11)',
|
margin: '10rpx 0'
|
}
|
}
|
},
|
classes: '',
|
itemStyle: { // formItem样式
|
type: Object
|
},
|
itemContentStyle: { // formItem Content样式
|
type: Object
|
},
|
boldLable: { // 加粗label
|
type: Boolean,
|
default: false
|
},
|
border: { // 是否有下边框
|
type: Boolean,
|
default: true
|
},
|
})
|
|
const emit = defineEmits(['reset'])
|
|
const _rawModel = props.model ? { ...props.model } : {}
|
|
provide('$parent', { addChild, getProps, getModel })
|
|
// 子组件
|
const childs = ref([])
|
|
function addChild(child) {
|
childs.value.push(child)
|
}
|
|
function getProps(name) {
|
return name ? props[name] : props
|
}
|
|
function getModel(prop) {
|
if (!props.model) return
|
return props.model[prop]
|
}
|
|
function checkField(name, model) {
|
return name in model
|
}
|
|
function checkRules(rules, value) {
|
return rules.map(rule => {
|
const { type, message } = rule
|
return { ...rule, valid: validationRules[type](rule.value, value, props.model) }
|
})
|
}
|
|
// 获取props.rules中的错误
|
function getFormRulesErrors(rules) {
|
let errors = []
|
for (let field in rules) {
|
const hasField = checkField(field, props.model)
|
const _rules = combineRule(rules[field])
|
const res = hasField ? checkRules(_rules, props.model[field]) : []
|
res.forEach(r => {
|
if (!r.valid) {
|
errors.push({ ...r, prop: field })
|
}
|
})
|
}
|
return errors
|
}
|
|
function validateChilds(childs, checkAll) {
|
if (props.checkAll) { // 检查所有
|
childs.forEach(c => c.check())
|
return callback(childs.every(c => c.getValid()))
|
}
|
return callback(childs.every(c => c.check()))
|
}
|
|
// 获取child中的规则
|
function getChildRules() {
|
let rules = {}
|
childs.value.forEach(c => {
|
const name = c.props.prop,
|
childRules = c.props.rules
|
if (name && childRules.length) {
|
rules[name] = childRules
|
}
|
})
|
return rules
|
}
|
|
// 合并 props.rules和child中的props.rules
|
function mergeRules(rules, childRules) {
|
for (let field in rules) {
|
const value = rules[field]
|
if (field in childRules) {
|
rules[field] = [...value, ...childRules[field]].reduce((acc, cur) => { // 去重
|
const index = acc.findIndex(item => JSON.stringify(item) === JSON.stringify(cur))
|
return index === -1 ? [...acc, cur] : acc
|
}, [])
|
}
|
}
|
return rules
|
}
|
|
function getChildErrors() {
|
const errors = childs.value.filter(c => !c.getValid())
|
return errors.map(c => {
|
const { valid, error, dirty, rule } = c.getState()
|
const { rule: { type, value } } = rule
|
return { message: error, valid, type, value }
|
})
|
}
|
|
function validate() {
|
let errors = []
|
validateChilds(childs.value, props.checkAll)
|
if (props.rules) {
|
const childRules = getChildRules()
|
const rules = { ...childRules, ...mergeRules(props.rules, childRules) }
|
errors = getFormRulesErrors(rules)
|
} else {
|
errors = getChildErrors()
|
}
|
return callback(errors)
|
}
|
|
function callback(errors) {
|
return new Promise((resolve, reject) => {
|
if (errors.length) {
|
if (props.toastError) {
|
showToastError([...errors])
|
}
|
return reject(errors)
|
}
|
return resolve()
|
})
|
}
|
|
function showToastError(errors) {
|
const { message } = errors.shift()
|
if (!message) return
|
uni.showToast({
|
icon: 'none',
|
title: message
|
})
|
}
|
|
function reset() {
|
childs.value.forEach(c => c.reset())
|
emit('reset', _rawModel)
|
}
|
|
defineExpose({ validate, reset })
|
</script>
|
|
<template>
|
<view :class="classes">
|
<slot></slot>
|
</view>
|
</template>
|