龙空技术网

配置化el-form的二次封装之思路分析附上代码可直接使用

水冗水孚 84

前言:

此刻看官们对“textarea数据回显”都比较关怀,各位老铁们都想要学习一些“textarea数据回显”的相关内容。那么小编同时在网上搜集了一些对于“textarea数据回显””的相关知识,希望看官们能喜欢,小伙伴们一起来了解一下吧!

问题描述

个人愚见编写代码其实就是:

学习规则(看官方文档)使用规则(在使用的过程中进一步理解官方文档)最终基于原有底层官方文档规则再自定义新规则(封装新的规则,便于复用)

所以本文讲述一下基于原有的el-form的规则,进行二次封装自定义新的规则的思路,以及附上能直接用的代码。我们先看一下效果图:

效果图思路分析

最终效果是配置化“写代码”,就像echarts一样,写不同的配置,出现不同的效果,自然是配置,所以就要提前考虑好有哪些需要配置。当然也要考虑数据的回显。

配置表单项类型(组件中要加上校验规则)配置表单项的名字配置表单项的字段配置表单项是否必填配置输入框的单位(如果有的话)配置placeholder的文本提示配置下拉框选项数据数据(如果是固定的下拉框可以传过去)如果是枚举值类型的下拉框就需要发请求获取下拉框选项数组数据等...

这里要多提一下表单项类型

配置表单项~输入框的类型

首先我们要清楚form表单项的类型,这里为了便于理解,只举例三种大类型,当然大类型中也包含小类型,同时也要做校验。至于别的类型,大家理解了这几个类型以后,就可以自己写了。

输入框类型文本输入框类型(校验得填写,不能为空)数字输入框类型(校验输入的数字类型,比如需要正整数、需要保留两位小数等)下拉框类型固定选项的下拉框类型(这里直接写死,传过去即可,比如性别下拉框,只有男女两种类型选项)枚举多个选项的单选下拉框类型(需要提前发请求获取数据,或者visible-change事件发请求获取)枚举多个选项的单选多选下拉框类型(同上)时间选择器范围类型注意绑定的结果值是数组即可

最后不要忘了回显逻辑哦

el-form表头数据举例

子组件表单数据根据根据父组件传递过来的formHeader动态渲染。即v-for中搭配v-if去呈现,先简单看一下formHeader数据结构,具体在后边代码中都有的

// 表头数组数据      formHeader: [        {          itemType: "text", // 输入框类型          labelName: "姓名", // 输入框名字          propName: "name", // 输入框字段名          isRequired: true, // 是否必填          placeholder: "请填写名字", // 输入框placeholder提示语加上,可用于告知用户规则        },        {          itemType: "number",          labelName: "年龄",          propName: "age",          isRequired: true,          unit: "year", // 数字类型的要有单位          placeholder: "请输入年龄(大于0的正整数)",        },        {          itemType: "selectOne", // 下拉框类型一,固定的选项可以写死在配置里,比如性别只有男女          labelName: "性别",          propName: "gender",          isRequired: true,          placeholder: "请选择性别",          optionsArr: [            {              label: "男",              value: 1,            },            {              label: "女",              value: 2,            },          ],        },      ],
完整代码

建议复制粘贴,运行跑起来,这样效果更加明显,更便于理解。

毕竟:no words,show codes

父组件传递配置数据

<template>  <div class="myWrap">    <h2>填写表单</h2>    <br />    <my-form      ref="myForm"      :formHeader="formHeader"      @submitForm="submitForm"      @resetForm="resetForm"    ></my-form>    <h2>表单数据回显</h2>    <el-button size="small" type="primary" @click="showData"      >点击按钮回显数据</el-button    >  </div></template><script>import myForm from "./myForm.vue";export default {  components: {    myForm,  },  data() {    return {      // 表头数组数据      formHeader: [        /**         * 输入框类型3种         *    1. 普通文本输入框 text         *    2. 数字类型输入框 number         *    3. 文本域输入框 textarea         *         * 下拉框select类型2中         *    1. 固定配置的el-option selectOne         *    2. 枚举值的el-option单选 selectTwo         *    2. 枚举值的el-option多选 selectThree         *         * 时间选择器类型1种         *    1. 两个时间选择器、选取一个范围         *         * 等等,还有其他类型,这里举三种类型,别的类型仿照着即可写出来         * 组件封装适可而止。如果是比较复杂(奇葩)的需要联动的表单,建议一个个写         * 毕竟过度的封装,会导致代码不好维护(个人愚见)         *         * */        {          itemType: "text", // 输入框类型          labelName: "姓名", // 输入框名字          propName: "name", // 输入框字段名          isRequired: true, // 是否必填          placeholder: "请填写名字", // 输入框placeholder提示语加上,可用于告知用户规则        },        {          itemType: "number",          labelName: "年龄",          propName: "age",          isRequired: true,          unit: "year", // 数字类型的要有单位          placeholder: "请输入年龄(大于0的正整数)",        },        {          itemType: "number",          labelName: "工资",          propName: "salary",          isRequired: true,          unit: "元/月", // 数字类型的要有单位          placeholder: "请输入每月工资金额(大于0且保留两位小数)",        },        {          itemType: "textarea",          labelName: "备注",          propName: "remark",          isRequired: true,          placeholder: "请填写备注",        },        {          itemType: "selectOne", // 下拉框类型一,固定的选项可以写死在配置里,比如性别只有男女          labelName: "性别",          propName: "gender",          isRequired: true,          placeholder: "请选择性别",          optionsArr: [            {              label: "男",              value: 1,            },            {              label: "女",              value: 2,            },          ],        },        {          itemType: "selectTwo", // 下拉框类型二,枚举值单选,在点击下拉选项时根据枚举id发请求,获取枚举值          labelName: "可选职业",          propName: "job",          isRequired: true,          placeholder: "请选择职业",          enumerationId: "123123123",        },        {          itemType: "selectTwo", // 下拉框类型二,枚举值单选,在点击下拉选项时根据枚举id发请求,获取枚举值          labelName: "愿望",          propName: "wish",          isRequired: true,          placeholder: "请选择愿望",          enumerationId: "456456456",        },        {          itemType: "selectThree", // 下拉框类型三,枚举值多选,在点击下拉选项时根据枚举id发请求,获取枚举值          labelName: "爱好",          propName: "hobby",          isRequired: true,          placeholder: "请选择爱好",          enumerationId: "789789789",        },        {          itemType: "selectThree", // 下拉框类型三,枚举值多选,在点击下拉选项时根据枚举id发请求,获取枚举值          labelName: "想买手机",          propName: "wantPhone",          isRequired: true,          placeholder: "请选择手机",          enumerationId: "147258369",        },        {          itemType: "dateRange", // 日期范围类型          labelName: "日期",          propName: "date",          isRequired: true,        },      ],    };  },  mounted() {    // 数据回显的时候,要先发请求获取枚举值下拉框的值才能够正确的回显,所以    // 就提前发请求获取对应下拉框的值了,这里要注意!注意!注意!    this.formHeader.forEach((item) => {      if ((item.itemType == "selectTwo") | (item.itemType == "selectThree")) {        this.$refs.myForm.getOptionsArrData(item);      }    });  },  methods: {    showData() {      let apiData = {        name: "孙悟空",        age: 500,        salary: 6666.66,        remark: "齐天大圣是也",        gender: 1, // 1代表男        job: 1, // 1医生 2教师 3公务员        wish: 3, // 1成为百万富翁 2长生不老 3家人健康幸福平安        hobby: [1, 2, 3], // 1乒乓球 2羽毛球 3篮球        wantPhone: [1, 2, 4], // 1华为 2小米 3苹果 4三星        date: ["2018-06-06", "2022-05-05"],      };      setTimeout(() => {        this.$refs.myForm.form = apiData;      }, 300);    },    submitForm(form) {      console.log("表单提交喽", form);    },    resetForm() {      console.log("表单重置喽");    },  },};</script><style lang='less' scoped>.myWrap {  width: 100%;  height: 100%;  box-sizing: border-box;  padding: 25px;  overflow-y: auto;}</style> 
封装的子组件根据传递的配置数据动态渲染
<template>  <div class="formWrap">    <el-form ref="form" label-position="top" :model="form" label-width="80px">      <template v-for="(item, index) in formHeader">        <!-- 当类型为普通文本输入框时 -->        <el-form-item          v-if="item.itemType == 'text'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: 'blur', // 触发方式,失去焦点                    itemType: 'text', // 当前类型,文字输入框                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                  },                ]              : []          "        >          <el-input            :placeholder="item.placeholder"            v-model.trim="form[item.propName]"            clearable            size="small"          ></el-input>        </el-form-item>        <!-- 当类型为数字类型输入框时 -->        <el-form-item          v-if="item.itemType == 'number'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: 'blur', // 触发方式,失去焦点                    itemType: 'number', // 当前类型,文字输入框                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                  },                ]              : []          "        >          <el-input            :placeholder="item.placeholder"            v-model.trim="form[item.propName]"            @change="checkInput(item)"            clearable            size="small"          >            <span slot="suffix">{{ item.unit }}</span>          </el-input>        </el-form-item>        <!-- 当类型为文本域输入框时 -->        <el-form-item          v-if="item.itemType == 'textarea'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: 'blur', // 触发方式,失去焦点                    itemType: 'textarea', // 当前类型,文本域输入框                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                  },                ]              : []          "        >          <el-input            type="textarea"            :placeholder="item.placeholder"            v-model.trim="form[item.propName]"            clearable            size="small"          ></el-input>        </el-form-item>        <!-- 当类型为下拉框一时,固定下拉选项 -->        <el-form-item          v-if="item.itemType == 'selectOne'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: '', // blur 或 change 这里就不指定触发方式了,保存提交时再校验                    itemType: 'selectOne', // 当前类型,固定下拉框类型                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                  },                ]              : []          "        >          <el-select            v-model="form[item.propName]"            :placeholder="item.placeholder"            clearable            size="small"          >            <el-option              v-for="(ite, ind) in item.optionsArr"              :key="ind"              :label="ite.label"              :value="ite.value"            ></el-option>          </el-select>        </el-form-item>        <!-- 当类型为下拉框二时,属于枚举值(单选)下拉框,需要根据枚举id发请求获取枚举值 -->        <el-form-item          v-if="item.itemType == 'selectTwo'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: '', // blur 或 change 这里就不指定触发方式了,保存提交时再校验                    itemType: 'selectTwo', // 当前类型,枚举值单选                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                  },                ]              : []          "        >          <el-select            v-model="form[item.propName]"            :placeholder="item.placeholder"            clearable            @visible-change="              (flag) => {                getOptionsArr(flag, item);              }            "            :loading="loadingSelect"            size="small"          >            <el-option              v-for="(ite, ind) in selectTwoOptionsObj[item.propName]"              :key="ind"              :label="ite.label"              :value="ite.value"            ></el-option>          </el-select>        </el-form-item>        <!-- 当类型为下拉框三时,属于枚举值(多选)下拉框,需要根据枚举id发请求获取枚举值 -->        <el-form-item          v-if="item.itemType == 'selectThree'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: 'blur', // 这里用blur,防止初次默认校验触发                    itemType: 'selectThree', // 当前类型,枚举值多选                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                    type: 'number',                  },                ]              : []          "        >          <el-select            v-model="form[item.propName]"            :placeholder="item.placeholder"            clearable            @visible-change="              (flag) => {                getOptionsArr(flag, item);              }            "            :loading="loadingSelect"            multiple            collapse-tags            size="small"          >            <el-option              v-for="(ite, ind) in selectTwoOptionsObj[item.propName]"              :key="ind"              :label="ite.label"              :value="ite.value"            ></el-option>          </el-select>        </el-form-item>        <!-- 当类型为日期范围 -->        <el-form-item          v-if="item.itemType == 'dateRange'"          :key="index"          :label="item.labelName"          :prop="item.propName"          :rules="            item.isRequired              ? [                  {                    required: true, // 是否必填 是                    trigger: '',                    itemType: 'dateRange', // 当前类型,枚举值多选                    labelName: item.labelName, // 当前输入框的名字                    value: form[item.propName], // 输入框输入的绑定的值                    validator: validateEveryData, // 校验规则函数                  },                ]              : []          "        >          <el-date-picker            v-model="form[item.propName]"            format="yyyy-MM-dd"            value-format="yyyy-MM-dd"            clearable            type="daterange"            range-separator="至"            start-placeholder="开始日期"            end-placeholder="结束日期"            size="small"          >          </el-date-picker>        </el-form-item>      </template>    </el-form>    <!-- 提交表单和重置表单部分 -->    <div class="btns">      <el-button type="primary" @click="submitForm" size="small"        >保存</el-button      >      <el-button @click="resetForm" size="small">重置</el-button>    </div>  </div></template><script>export default {  props: {    // 父组件传递过来的表头的数据    formHeader: {      type: Array,      default: () => {        return [];      },    },  },  data() {    var validateEveryData = (rule, value, callback) => {      //   console.log("callback", callback);      //   console.log("校验某一项的规则对象", rule);      //   console.log("用户输入的值", value);      // 对输入框类型的校验      if (value) {        if ((value + "").length > 0) {          // 用于回显时候的校验,因为输入的时候是字符串类型的数字,但是回显的时候可能就是数字          callback(); // cb函数告知校验结果,必须要加          return;        }      }      // 对下拉框类型的校验      if (        (rule.itemType == "selectOne") |        (rule.itemType == "selectTwo") |        (rule.itemType == "selectThree")      ) {        if (value) {          if ((value + "").length > 0) {            // 注意枚举值是数字类型的,所以这里要转换成为字符串类型的            callback();            return;          }        }      }      // 根据不同的类型给予不同的校验提示      switch (rule.itemType) {        case "text":          callback(new Error(rule.labelName + "不能为空")); // 文本类型的规则简单,就是得填写          break;        case "number":          callback(new Error(rule.labelName + "请按规则填写")); // 数字类型的规则比较繁多          break;        case "textarea":          callback(new Error(rule.labelName + "不能为空")); // 文本域类型的规则也简单,就是得填写          break;        case "selectOne":          callback(new Error("请选择" + rule.labelName)); // 下拉框类型一 得填写          break;        case "selectTwo":          callback(new Error("请选择" + rule.labelName)); // 下拉框类型二 得填写          break;        case "selectThree":          callback(new Error("请选择" + rule.labelName)); // 下拉框类型三 多选数组得填写          break;        case "dateRange":          callback(new Error("请选择" + rule.labelName + "范围")); // 下拉框类型三 多选数组得填写          break;        default:          break;      }    };    return {      // 此对象用于存储各个下拉框的数组数据值,其实也可以挂在vue的原型上,不过个人认为写在data中好些      selectTwoOptionsObj: {},      // 用于下拉框加载时的效果      loadingSelect: false,      // 绑定的数据      form: {},      // 校验规则      validateEveryData: validateEveryData,    };  },  methods: {    // 获取下拉框数据    async getOptionsArr(flag, item) {      //   console.log(flag, item);      // 为true时表示展开,这里模拟根据枚举值id发请求,获取下拉框的值的      if (flag) {        this.loadingSelect = true; // 使用了加载中效果,最好加上一个try catch捕获异常        // let result = await this.$api.getEnumList({id:item.enumerationId})        this.getOptionsArrData(item);      } else {        // 解决多选下拉框失去焦点校验规则仍然存在问题        if (item.itemType == "selectThree") {          //   console.log("关闭时校验多选值", this.form[item.propName]);          if (this.form[item.propName].length > 0) {            //  如果至少选择一个了,说明符合要求,就再校验一次,这样校验规则就去掉了            this.$refs.form.validateField(item.propName);          }        }      }    },    getOptionsArrData(item) {      setTimeout(() => {        this.loadingSelect = false;        if (item.enumerationId == "123123123") {          this.selectTwoOptionsObj[item.propName] = [            {              label: "医生",              value: 1,            },            {              label: "教师",              value: 2,            },            {              label: "公务员",              value: 3,            },          ];        }        if (item.enumerationId == "456456456") {          this.selectTwoOptionsObj[item.propName] = [            {              label: "成为百万富翁",              value: 1,            },            {              label: "长生不老",              value: 2,            },            {              label: "家人健康幸福平安",              value: 3,            },          ];        }        if (item.enumerationId == "789789789") {          this.selectTwoOptionsObj[item.propName] = [            {              label: "乒乓球",              value: 1,            },            {              label: "羽毛球",              value: 2,            },            {              label: "篮球",              value: 3,            },          ];        }        if (item.enumerationId == "147258369") {          this.selectTwoOptionsObj[item.propName] = [            {              label: "华为",              value: 1,            },            {              label: "小米",              value: 2,            },            {              label: "苹果",              value: 3,            },            {              label: "三星",              value: 4,            },          ];        }        this.$forceUpdate(); // 这里需要强制更新一下,否则渲染不出来下拉框选项      }, 300);    },    // 数字类型加校验规则    checkInput(item) {      console.log("数字类型的再细分规则,可以根据item.labelName再写判断", item);      if (item.labelName == "年龄") {        let reg = /^[1-9]\d*$/;        if (reg.test(this.form[item.propName] * 1)) {          // console.log("符合要求,年龄大于0的正整数");        } else {          this.form[item.propName] = null;        }      }      if (item.labelName == "工资") {        let reg = /^((0{1}\.\d{1,2})|([1-9]\d*\.{1}\d{1,2})|([1-9]+\d*))$/;        if (reg.test(this.form[item.propName] * 1)) {          // console.log("符合要求,工资保留两位小数");          this.form[item.propName] = (this.form[item.propName] * 1).toFixed(2);        } else {          this.form[item.propName] = null;        }      }      if ("某个数字类型字段值") {        // 加对应规则      }    },    // 保存提交表单    async submitForm() {      this.$refs.form.validate((valid) => {        if (valid) {          this.$emit("submitForm", this.form);        } else {          console.log("error submit!!");          return false;        }      });    },    // 重置表单    resetForm() {      this.$refs.form.resetFields();      this.form = {}; // 这里重置完了以后,要重新初始化数据,否则会出现输入不上去的问题      this.$emit("resetForm");    },  },};</script><style lang='less' scoped>.formWrap {  width: 100%;  /deep/ .el-form {    display: flex;    flex-wrap: wrap;    justify-content: space-between;    .el-form-item {      width: 47%;      margin-bottom: 12px !important;      .el-form-item__label {        padding: 0 !important;        line-height: 24px !important;      }      .el-form-item__content {        // 给文本域类型定高度        .el-textarea {          textarea {            height: 75px !important;          }        }        // 给下拉框指定宽度百分比        .el-select {          width: 100% !important;        }        // 时间选择器指定宽度百分比        .el-date-editor {          width: 100% !important;          .el-range-separator {            width: 10% !important;          }        }        .el-form-item__error {          padding-top: 1px !important;        }      }    }  }  .btns {    width: 100%;    text-align: center;    margin-top: 12px;  }}</style>

好记性不如烂笔头,记录一下吧。欢迎批评指正^_^

标签: #textarea数据回显