1. 定义Form表单组件:
<template>
<div class="v-form-conatiner">
<van-cell-group>
<!-- 自定义cell-group表头 -->
<template v-if="title" #title>
<slot name="group-title">
{{ title }}
</slot>
</template>
<ValidationObserver slim ref="validationObserver">
<template v-for="item in formModel">
<!-- 输入框、选择框、日期选择器、文件上传、单选框、复选框等可输入组件渲染 -->
<ValidationProvider
v-if="!formModelOptions[item.key + 'Hidden']"
:key="item.key"
:name="item.options.label"
:rules="item.options.rules ? item.options.rules : ''"
v-slot="{ errors }"
slim
>
<van-cell
v-if="item.componentType === 'van-cell'"
:title="item.options.label"
:value="formData[item.key]"
/>
<component
v-else
:is="item.componentType"
:ref="`${item.key}`"
:data="item.options"
:keyName="item.key"
v-model="formData[item.key]"
@changed="changed(item)"
:error-message="errors[0]"
/>
</ValidationProvider>
</template>
</ValidationObserver>
</van-cell-group>
</div>
</template>
<script>
import { ValidationProvider } from "vee-validate";
import formItemBase from "./mixins/FormBase";
import VantField from "@/components/common/form/VantField.vue";
import VantPersonal from "@/components/common/form/VantPersonal.vue";
import VantAgent from "@/components/common/form/VantAgent.vue";
import VantRadio from "@/components/common/form/VantRadio.vue";
import VantCheckBox from "@/components/common/form/VantCheckBox.vue";
import VantMultiPicker from "@/components/common/form/VantMultiPicker.vue";
import VantDatetimePicker from "@/components/common/form/VantDatetimePicker.vue";
import VantPicker from "@/components/common/form/VantPicker.vue";
import VantUploader from "@/components/common/form/VantUploader.vue";
import VantUploaderImage from "@/components/common/form/VantUploaderImage.vue";
import VantFormPopup from "@/components/common/form/VantFormPopup.vue";
import VantCalendar from "@/components/common/form/VantCalendar.vue";
export default {
name: "VantForm",
components: {
ValidationProvider,
VantField,
VantPersonal,
VantAgent,
VantRadio,
VantCheckBox,
VantMultiPicker,
VantDatetimePicker,
VantPicker,
VantUploader,
VantUploaderImage,
VantFormPopup,
VantCalendar,
},
mixins: [formItemBase],
props: {
formModel: {
// 表单初始化数据结构
type: Array,
default: function () {
return [];
},
},
value: {
// 表单的值
type: Object,
default: function () {
return {};
},
},
title: {
type: String,
default: function () {
return "";
},
},
},
model: {
prop: "value",
event: "changed",
},
data() {
return {
formData: {}, // 表单绑定的数据
formModelOptions: {}, // form表单字段配置显示隐藏参数配置
};
},
methods: {
/**
* 表单提交函数
*/
onSubmit() {
this.$refs.validationObserver.validate().then((success) => {
// success结果返回布尔值
if (!success) return;
// 处理请求
// this.$emit("success", this.formData);
this.$nextTick(() => {
// 清除验证状态,需注意值不会清除要自己手动清除
this.$refs.validationObserver.reset();
});
});
},
/**
* 表单校验
*/
validate() {
return new Promise((resolve) => {
this.$refs.validationObserver.validate().then((success) => {
resolve(success);
// if (success) {
// this.$nextTick(() => {
// // 清除验证状态,需注意值不会清除要自己手动清除
// window.validationObserver = this.$refs.validationObserver;
// this.$refs.validationObserver.reset();
// });
// }
});
});
},
reset() {
this.$refs.validationObserver.reset();
},
/**
* 表单值发生改变回调函数
* item: 表单字段数据模板
*/
changed(item) {
this.$emit("formItemChanged", item, this.formData); // 值变更,暴露变化的事件及值,父类中可以处理业务校验等逻辑
this.$emit("changed", this.formData); // 值变更双向绑定
},
/**
* 动态设置fromData的值,主要用于字段之间的依赖计算
* name:属性名
* value: 值
*/
setFormDataByName(name, value) {
this.$set(this.formData, name, value);
},
/**
* 动态设置formModelOptions的值,主要用于字段之间联动显示等逻辑控制
* name + "Hidden" 是动态组装的属性名
* value对应属性的名字
*/
setFormModelOptionsByIndex(name, value) {
this.$set(this.formModelOptions, name + "Hidden", value);
},
/**
* @param {*} fieldName 字段key值
* @param {*} model form模板
* @param {*} node form节点
*/
setFieldToDiabled(fieldName, disabled) {
let refNode = null;
if (fieldName) {
refNode = this.getFieldNode(fieldName);
if (refNode && refNode.setDisabled) {
refNode.setDisabled(disabled);
}
}
},
/**
* 初始化数据
*/
initData() {
let isEdit = false;
if (!this.$utils.isEmpty(this.value)) {
isEdit = true;
}
// model带{{i18nKey}}资源国家化
this.handleI18n(this.formModel);
// 动态给属性form data添加属性,后期作为field控件的v-model的变量
for (let i = 0; i < this.formModel.length; i++) {
if (isEdit) {
// 编辑数据复制
this.formModel[i].value = this.value[this.formModel[i].key];
}
// 组装表格绑定的model
this.$set(
this.formData,
this.formModel[i].key,
this.formModel[i].value
);
/**
* 字段的配置属性集合
* 可以配置多个,根据需要进行扩展
**/
let formModelItem = this.formModel[i];
// 字段显示控制字段,命名规则:[key]+Hidden
this.$set(
this.formModelOptions,
formModelItem.key + "Hidden",
formModelItem.options.hidden ? true : false
);
}
},
/**
* 返回表单字段的ref引用
*/
getFieldRefNode(keyName) {
return this.$refs[keyName];
},
/**
* 返回表单字段的ref引用
*/
getFieldNode(keyName) {
let refsNode = this.$refs[keyName];
return refsNode && refsNode.length ? refsNode[0] : null;
},
},
created() {
this.initData();
},
watch: {
formModel() {
this.initData();
},
value(newVal, oldVal) {
console.info(oldVal);
// 编辑数据,处理数据回填
if (newVal.isEdit) {
this.formData = newVal;
}
// 数据回填,把编辑状态重置成非编辑状态,必备表单改变,重复处理数据
this.isEdit = false;
},
},
};
</script>
<style scoped lang="less">
.v-form-conatiner {
font-size: 14px;
color: #646566;
text-align: left;
}
.required {
.van-cell-group__title:before {
position: absolute;
left: 2.133333vw;
color: #ee0a24;
font-size: 3.733333vw;
content: "*";
}
}
.v-cell-title {
margin: 0;
padding: 16px 16px 16px;
color: rgba(69, 90, 100, 0.6);
font-weight: normal;
font-size: 14px;
line-height: 16px;
background-color: #f7f8fa;
}
</style>
2.form表单子组件:
<script>
import { ValidationProvider } from "vee-validate";
import formItemBase from "./mixins/FormBase";
// 表单子组件
import VantField from "@/components/common/form/VantField.vue";
import VantPersonal from "@/components/common/form/VantPersonal.vue";
import VantAgent from "@/components/common/form/VantAgent.vue";
import VantRadio from "@/components/common/form/VantRadio.vue";
import VantCheckBox from "@/components/common/form/VantCheckBox.vue";
import VantMultiPicker from "@/components/common/form/VantMultiPicker.vue";
import VantDatetimePicker from "@/components/common/form/VantDatetimePicker.vue";
import VantPicker from "@/components/common/form/VantPicker.vue";
import VantUploader from "@/components/common/form/VantUploader.vue";
import VantUploaderImage from "@/components/common/form/VantUploaderImage.vue";
import VantFormPopup from "@/components/common/form/VantFormPopup.vue";
import VantCalendar from "@/components/common/form/VantCalendar.vue";
3.处理校验逻辑:
使用ValidationObserver和ValidationProvider实现校验逻辑
<ValidationObserver slim ref="validationObserver">
<template v-for="item in formModel">
<!-- 输入框、选择框、日期选择器、文件上传、单选框、复选框等可输入组件渲染 -->
<ValidationProvider
v-if="!formModelOptions[item.key + 'Hidden']"
:key="item.key"
:name="item.options.label"
:rules="item.options.rules ? item.options.rules : ''"
v-slot="{ errors }"
slim
>
<van-cell
v-if="item.componentType === 'van-cell'"
:title="item.options.label"
:value="formData[item.key]"
/>
<!-- 动态渲染form表单的子组件 -->
<component
v-else
:is="item.componentType"
:ref="`${item.key}`"
:data="item.options"
:keyName="item.key"
v-model="formData[item.key]"
@changed="changed(item)"
:error-message="errors[0]"
/>
</ValidationProvider>
</template>
</ValidationObserver>
4. 定义form表单Json数据模板
CreateContactModel.js
import {
getPositionOptions,
getCustomerPortraitOptions,
getProjectRoleOptions,
} from "../../../store/constants";
export const contactInformationModel = [
{
key: "contactName", // 联系人姓名
componentType: "vant-field",
value: "",
options: {
label: "联系人姓名",
type: "text",
placeholder: "{{pleaseInput}}",
rules: "required",
required: true,
},
},
{
key: "contactNumber", // 联系人电话
componentType: "vant-field",
value: "",
options: {
label: "联系人电话",
type: "tel",
placeholder: "{{pleaseInput}}",
rules: "required|phone",
required: true,
},
},
{
key: "department", // 所属部门
componentType: "vant-field",
value: "",
options: {
label: "所属部门",
type: "text",
placeholder: "{{pleaseInput}}",
},
},
{
key: "position", // 职位
componentType: "vant-radio",
value: "",
options: {
label: "职位",
data: getPositionOptions(),
valueType: "single-dict", // 数据类型
},
},
{
key: "portrait", // 客户画像
componentType: "vant-picker",
value: "",
options: {
label: "客户画像",
placeholder: "{{pleaseSelect}}",
data: getCustomerPortraitOptions(),
valueType: "single-dict", // 数据类型
},
},
{
key: "projectRole", // 项目中角色
componentType: "vant-multi-picker",
value: "",
options: {
label: "项目中角色",
placeholder: "{{pleaseSelect}}",
span: 8,
data: getProjectRoleOptions(),
valueType: "multi-dict", // 数据类型
},
},
];
5.动态生成form表单
<template>
<div class="create-opportunity">
<!-- 联系人信息 -->
<vant-form
ref="contactInformation"
title="联系人信息(可添加多个联系人)"
:formModel="contactInformationModel"
v-model="contactInformationFormData"
class="required"
>
</vant-form>
<!-- 机会保存-->
<van-button
v-if="!isCreateProject"
type="info"
@click="onSubmit"
block
round
>保存</van-button
>
</div>
</template>
<script>
import VantForm from "@/components/common/VantForm.vue";
import { contactInformationModel } from "@/components/common/business/CreateContactModel";
export default {
name: "Test",
components: {
VantForm,
},
data() {
return {
//提交表单数据
preparerInformationFormData: {}, // 联系人信息form表单
contactInformationModel, // 联系人信息model
};
},
};
</script>
效果图:
校验: