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>

效果图:

 校验:

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐