代码基于(woodenf)作者进行vue3实现
Component
<template>
<div class="SchemaForm">
<el-form
ref="SchemaForm"
v-bind="$attrs"
:model="source"
:class="className"
:rules="rules"
label-width="auto"
>
<el-row>
<el-col
v-for="item in config"
:key="item.key"
:span="item.hidden ? 0 : item.span || 15"
>
<el-form-item
v-if="!item.hidden"
:label="item.label"
:prop="item.key"
:labelWidth="item.labelWidth || 'auto'"
v-bind="item.itemProps"
>
<!-- <slot v-if="item.component === 'slot'" :name="item.slotName"></slot> -->
<div
v-if="item.component === '-'"
:class="item.innerClass"
:style="item.style"
>
{{ item.innerText || source[item.key] }}
</div>
<template v-else>
<div class="nx-flex-aling-center">
<component
:is="item.component"
//这一段报错Unexpected mutation of "source" prop
//楼主是直接关闭了eslint检测的
//也可以通过设置一个变量保存父组件props传过来的值,或者通过计算属性返回
v-model="source[item.key]"
v-bind="item.props"
>
<template v-if="item.component === 'el-select'">
<el-option
v-for="(option, optionIndex) in item.data"
:key="optionIndex"
:label="option[item.option[0]]"
:value="option[item.option[1]]"
></el-option>
</template>
<template v-if="item.component === 'el-radio-group'">
<el-radio
v-for="(option, optionIndex) in item.data"
:key="optionIndex"
:label="optionIndex.toString()"
>{{ option }}</el-radio
>
</template>
</component>
<!-- <div v-if="item.after" class="nx-form_after">
{{ item.after }}
</div> -->
</div>
<!-- <div
v-if="item.tips"
class="nx-form_tips"
v-html="item.tips"
></div> -->
</template>
</el-form-item>
</el-col>
</el-row>
<slot name="submit"></slot>
</el-form>
</div>
</template>
<script>
import { ref, onBeforeUnmount } from 'vue'
import { ElInput, ElSelect, ElRadioGroup } from 'element-plus'
import bus from '@/libs/but'
export default {
props: {
config: {
type: Array,
default: () => []
},
source: {
type: Object,
default: () => ({})
},
className: {
type: String,
default: ''
},
rules: {
type: Object,
default: () => ({})
}
},
components: {
ElInput, ElSelect, ElRadioGroup
},
setup () {
const SchemaForm = ref(null)
function resetFields () {
SchemaForm.value.resetFields();
}
// 清除表单
const clearValidate = () => {
SchemaForm.value.clearValidate();
}
// 启用监听
bus.on('resetFields', resetFields);
// 在组件卸载之前移除监听
onBeforeUnmount(() => {
bus.off('resetFields', resetFields);
})
async function validate () {
const valid = await SchemaForm.value.validate();
return valid;
}
return {
resetFields,
clearValidate,
validate,
SchemaForm
}
}
}
</script>
父组件
这里代码比较多就不直接粘贴上来了
<template>
<schema-form
ref="addForm"
:source="form"
:config="searchConfig"
:className="'addDialog'"
:rules="rules"
>
<template #submit>
<div class="save">
<el-button
type="primary"
@click="submitForm"
:loading="btn_lodding"
>保存</el-button
>
</div>
</template>
</schema-form>
</template>
<script setup>
const searchConfig = computed(() => {
return [
{
key: 'username',
component: 'el-input',
value: form.username,
span: 20,
label: '用户名称:',
props: {
placeholder: '请输入用户名称',
}
},
{
key: 'phone',
component: 'el-input',
span: 20,
label: '手机号码:',
props: {
placeholder: '请输入手机号码',
}
},
{
key: 'password',
component: 'el-input',
span: 20,
label: '输入密码:',
props: {
placeholder: '请输入密码',
'show-password': true
}
},
{
key: 'password_s',
component: 'el-input',
span: 20,
label: '确认密码:',
props: {
placeholder: '请确认密码',
'show-password': true
}
},
{
key: 'roleNames',
component: 'el-select',
span: 20,
label: '所属角色:',
data: form.select.name,
option: ['name', 'name'],
props: {}
},
{
key: 'state',
component: 'el-radio-group',
span: 20,
label: '启用状态:',
data: ['启用', '停用'],
props: {}
},
{
hidden: false,
span: 20,
}
]
})
</script>
新增了一个rulse属性,这个是我自己封装的表单验证,不过个人感觉较为冗余,后续会继续优化
我在main.js直接使用app.config.globalProperties.$rules=rulesFn进行全局复用
let rules = reactive({
username: [
{
required: true,
validator: proxy.$rules.FormValidate.Form(form, '请输入用户名').userVerify,
trigger: 'blur',
},
],
phone: [
{
required: true,
validator: proxy.$rules.FormValidate.Form().phoneVerify,
trigger: 'blur',
},
],
password: [
{
required: true,
validator: proxy.$rules.FormValidate.Form().passwordVerify,
trigger: 'blur',
},
],
password_s: [
{
required: true,
validator: proxy.$rules.FormValidate.Form(form).password_sVerify,
trigger: 'blur',
},
],
roleNames: [
{
required: true,
validator: proxy.$rules.FormValidate.Form(form, '请选择角色').userVerify,
trigger: 'blur',
},
]
})
rulesFn.js
const FormValidate = (function () {
function FormValidate () { }
FormValidate.Form = function (form,text) {
return {
// 表单验证 不能为空
userVerify (rule, value, callback) {
if (!value) {
return callback(new Error(text))
}
return callback()
},
// 表单验证下拉框布尔值
nameVerify(rule,value,callback){
if (value===null) {
return callback(new Error(text))
}
return callback()
},
// 表单验证 电话
phoneVerify (rule, value, callback) {
if (!value) {
return callback(new Error('请输入手机号'))
} else if (!/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(value)) {
return callback(new Error('手机号格式错误'))
}
return callback()
},
// 表单验证 密码
passwordVerify (rule, value, callback) {
if (!value) {
return callback(new Error('请输入密码'))
} else if (!/^\w{6,24}$/.test(value)) {
return callback(new Error('密码是6-24个字符'))
}
return callback()
},
// 确认密码
password_sVerify (rule, value, callback) {
if (!value) {
return callback(new Error('请输入密码'))
} else if (value != form.password) {
return callback(new Error('两次密码不一致'))
}
return callback()
},
// 表单验证
// 地址验证
urlVerify (rule, value, callback){
if (!value) {
return callback(new Error('不能为空'))
} else if (!/^[A-Za-z]+$/.test(value)) {
return callback(new Error('只能输入英文字符'))
}
return callback()
},
// 商品模块验证
// 表单验证 账号
userChineVerify (rule, value, callback) {
if (!value) {
return callback(new Error('不能为空'))
}else if (!/^[\u4e00-\u9fa5]{2,8}$/.test(value)) {
return callback(new Error('请输入2到8位中文字符'))
}
callback()
},
// 表单验证 价格
priceVerify (rule, value, callback) {
if (!value) {
return callback(new Error('请输入价格'))
} else if (!/^[0-9]*$/.test(value)) {
return callback(new Error('请输入数字'))
}
callback()
},
// 表单验证 价格
priceVerifys (rule, value, callback) {
if (!value) {
return callback(new Error('请输入价格'))
} else if (!/^(([1-9]{1}\d*)|(0{1}))(\.\d{1,2})?$/.test(value)) {
return callback(new Error('请输入数字,只能2位小数'))
}
callback()
},
// 表单验证 合同金额
contractPriceVerify (rule, value, callback) {
if (!value) {
return callback(new Error('请输入价格'))
} else if (!/^(([1-9]{1}\d*)|(0{1}))(\.\d{1,6})?$/.test(value)) {
return callback(new Error('请输入数字,合同金额只能保留6位小数'))
}
callback()
},
// 表单验证 价格
amountVerify (rule, value, callback) {
if (!value) {
return callback(new Error('请输入数量'))
} else if (!/^[1-9]{1}\d*$/.test(value)) {
return callback(new Error('请输入整数'))
}
callback()
},
// 主图验证
imgUrlVerify (rule, value, callback) {
if (Boolean(form.value[text])===false) {
return callback(new Error('请选择图片'))
}
callback()
},
// 详情图
imgDetailerify (rule, value, callback) {
if (form.value[text].length===0) {
return callback(new Error('请选择图片'))
}
callback()
},
// 官方的联系电话
phoneOffVerify (rule, value, callback) {
if (form.value.official===false) {
if (!value) {
return callback(new Error('请输入手机号'))
} else if (!/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(value)) {
return callback(new Error('手机号格式错误'))
}
callback()
}
},
// 企业档案图片
detailImgVerify(rule, value, callback) {
if (!form.value[text]) {
return callback(new Error('图片上传不能为空'))
}
callback()
},
// 上传图片
addImgVerify(rule, value, callback) {
if (!form[text]) {
return callback(new Error('图片上传不能为空'))
}
callback()
},
imgVerify (rule, value, callback) {
if (form[text].length===0) {
return callback(new Error('请选择图片'))
}
callback()
},
// 企业电话验证
detailPhoneVerify(rule,value,callback){
if(!/^((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)$/.test(value)){
return callback(new Error('手机号格式错误'))
}
callback()
}
}
}
return FormValidate
}())
exports.FormValidate = FormValidate