Encapsulate the general el-form form (2 ways)

1. Preamble

         Project address: git clone  form-demo: Encapsulate general el-form

        The most common form of a background management system is the form . The most common form is the input box, drop-down selection, date selection, radio selection, check box, etc. When adding several modules to the system, copy and paste several el-form, el-form -item , one thing to say, it is fast to complete the requirements, but there are too many redundant parts of the code, can you automatically generate el-form and el-form-item through configuration

        Before wrapping the code:

        After wrapping the code:

       The variable and method names of the two encapsulation methods are basically the same!

2. Encapsulate el-form in the way of custom components

        2.1. Encapsulation

        (1) Create a new commentForm folder and create an index.vue file

         (2) in index.vue

<template>
  <div>
    <el-form ref="form" :model="form" :rules="rules" :inline="inline" :label-width="labelWidth">
      <template v-for="(item, index) in formItemList">
        <!-- 输入框类型 -->
        <template v-if="item.type === 'input'">
          <el-form-item :label="item.label" :prop="item.model">
            <el-input v-model.trim="form[item.model]" :type="`${item.category || 'text'}`"
              :style="`width: ${item.width || '250px'}`" :clearable="item.clearable === undefined || item.clearable"
              filterable :placeholder="item.placeholder" />
          </el-form-item>
        </template>

        <!-- 下拉选择框 -->
        <template v-if="item.type === 'select'">
          <el-form-item :label="item.label" :prop="item.model">
            <el-select v-model.trim="form[item.model]" :style="`width:${item.width || '250px'}`" clearable
              :placeholder="item.placeholder || ''">
              <el-option v-for="(i, key) in  item.options" :key="i[item.value] || key" :label="i[item.label] || i.label"
                :value="i[item.value] || i.value">
              </el-option>
            </el-select>
          </el-form-item>
        </template>

        <!-- 日期选择器 -->
        <template v-if="item.type === 'date-picker'">
          <el-form-item :prop="item.model" :label="item.label">
            <el-date-picker v-model.trim="form[item.model]" type="daterange" range-separator="至" start-placeholder="开始日期"
              end-placeholder="结束日期">
            </el-date-picker>
          </el-form-item>
        </template>

        <!-- 开关 -->
        <template v-if="item.type === 'switch'">
          <el-form-item :prop="item.model" :label="item.label">
            <el-switch v-model="form[item.model]"></el-switch>
          </el-form-item>
        </template>

        <!-- 复选框 -->
        <template v-if="item.type === 'checkbox'">
          <el-form-item :prop="item.model" :label="item.label">
            <el-checkbox-group v-model="form[item.model]">
              <el-checkbox :label="item.label" :key="index" v-for="(item, index) in item.options"></el-checkbox>
            </el-checkbox-group>
          </el-form-item>
        </template>

        <!-- 单选框 -->
        <template v-if="item.type === 'radio'">
          <el-form-item :prop="item.model" :label="item.label">
            <el-radio-group v-model="form[item.model]">
              <el-radio :label="item.label" :key="index" v-for="(item, index) in item.options"></el-radio>
            </el-radio-group>
          </el-form-item>
        </template>

        <!-- 自己拓展... -->
      </template>
    </el-form>
  </div>
</template>

<script>
export default {
  props: {
    form: {
      type: Object,
      default: () => { }
    },
    // 生成el-form-item的数组
    formItemList: {
      type: Array,
      default: () => [],
    },
    // 是否行内表单模式
    inline: {
      type: Boolean,
      default: false,
    },
    // el-form-item的label宽度
    labelWidth: {
      type: String,
      default: '80px',
    },
    // 表单校验规则
    rules: {
      type: Object,
      default: () => { },
    },
  },
  data() {
    return {
    }
  },
  methods: {
    // 返回经过校验的表单
    returnForm() {
      // 表单校验 
      this.$refs['form'].validate((valid) => {
        if (valid) {
          this.$emit('getForm', this.form)
        }
      })
    },
    // 重置表单
    resetForm() {
      // 重置复选框
      this.$refs.form.resetFields();
    }
  }
}
</script>

<style scoped></style>

        (3) Matters needing attention:

  •  The value bound to v-model in el-checkbox must exist in advance, otherwise it will report an error that the length attribute cannot be found

            So declare an array in data in advance, v-model binds this array, when returning the form to the parent component, add it to the form object, when resetting the form, make this array empty to realize the reset form function!

  • The reason for the unsuccessful reset may be: (1) No prop has been added (2) The value bound by the prop is inconsistent with the property corresponding to the object bound by the model

        2.2. Use

Focus areas:

(1) The type in formItemList determines what type of form items are generated, such as input boxes, drop-down options, etc.

(2) The model in formItemList indicates the name of the two-way binding of the form item, and it is also the attribute name of the object returned by the child component to the object in the parent component

(3) The options in formItemList represent the option values ​​of drop-down options, check boxes, and radio buttons

(4) The event bound by @getForm can get the verified form returned from the child component to the parent component. Usually, the child component does not make network requests, and the parent component makes network requests.

(5) The parent component obtains the component instance through $refs to call the child component to reset the form and submit the form

<template>
  <div id="app">
    <CommonForm ref="form" :form="form" :rules="rules" :formItemList="formItemList" @getForm="getForm">
    </CommonForm>
    <el-row>
      <el-button type="primary" @click="submitForm">提交</el-button>
      <el-button @click="resetForm">重置</el-button>
    </el-row>
  </div>
</template>

<script>
import CommonForm from '@/components/commonForm'
export default {
  name: 'App',
  components: {
    CommonForm,
  },
  data() {
    return {
      form: {},
      editForm: {
        name: '张三',
        region: 'shanghai',
        delivery: true,
        resource: '线上品牌商赞助',
        startTime: [
          '2023-05-10',
          '2023-05-11'
        ],
        types: '线下主题活动',
      },
      formItemList: [
        { label: '活动名称', type: 'input', model: 'name', placeholder: '请输入活动名称' },
        { label: '活动区域', type: 'select', model: 'region', placeholder: '请选择状态', options: [{ label: '上海', value: 'shanghai' }, { label: '北京', value: 'beijing' }] },
        { label: '活动时间', type: 'date-picker', model: 'startTime', },
        { label: '即时配送', type: 'switch', model: 'delivery' },
        { label: '活动性质', type: 'checkbox', model: 'type', modelString: 'types', options: [{ label: '美食/餐厅线上活动' }, { label: '地推活动' }, { label: '线下主题活动' }, { label: '单纯品牌曝光' },] },
        { label: '特殊资源', type: 'radio', model: 'resource', options: [{ label: '线上品牌商赞助' }, { label: '线下场地免费' }] },
      ],
      rules: {
        name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
      }
    }
  },
  created() {
    // 表单初始化
    this.formInit()
  },
  methods: {
    // 表单初始化
    formInit() {
      this.form = JSON.stringify(this.editForm) ? this.editForm : {}

      // checkbox类型的组件需要初始化数组
      this.formItemList.forEach(item => {
        if (['checkbox'].includes(item.type)) {
          if (!this.form[item.modelString]) {
            this.$set(this.form, item.model, [])
          } else {
            this.$set(this.form, item.model, this.form[item.modelString]?.split(','))
          }
        }
      })
    },
    // 接受子组件传回来的form表单内容
    getForm(val) {
      this.formItemList.forEach(item => {
        // 
        if (['checkbox'].includes(item.type)) {
          if (this.form[item.model] && this.form[item.model]?.length != 0) {
            val[item.modelString] = val[item.model]?.join(',')
          }
        }
      })
      console.log('val:', val);
      // 此处可以请求后台了
    },
    // 点击提交,触发表单校验
    submitForm() {
      this.$refs.form.returnForm();
    },
    // 重置表单
    resetForm() {
      this.$refs.form.resetForm();
    }
  }
}
</script>

<style>
#app {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #2c3e50;

}
</style>

        2.3. Echo data

        Prepare an object: editForm simulates that the form already has data, and judges whether the editForm is not empty. If there is a value, it will be assigned to the form , and if there is no value, it will be assigned {}

this.form = JSON.stringify(this.editForm) ? this.editForm : {}

        I just said that the value bound by the v-model of the checkbox in elementUI needs to be a real array. If an additional variable is instantiated in the form component to deal with the checkbox, it will not be so intelligent. 

        How to pass an {} object to the form component without reporting the above error, and can also echo the data, this requires initializing the object in the component that references the form component

        (1) V-models such as checkboxes in elementui , Cascader cascading selectors, date components, etc. are bound to arrays, and sometimes the back-end interface documents are not arrays, and you need to manually process them. For example:

        The front end uses multiple selected values ​​(these values ​​exist in an array) in string form and the values ​​inside are separated by commas, for example:

[1, 2, 3, 4] => "1,2,3,4" , so pass it to the backend, and the backend returns "1,2,3,4" , asking you to convert it into an array [1,2 ,3,4] render to the elementui component.

split(',') : Convert the comma-separated elements in the string into an array, [1, 2, 3, 4] => "1,2,3,4"

join(',') : Convert array elements into comma-separated strings,    "1,2,3,4" => [1, 2, 3, 4]

$set(array/object, property/index, specific value) : Object.defineProperty is used in vue2 to monitor responsive changes. The monitoring of arrays and objects is not so friendly. Many times the data changes but the view does not change. $ set appeared to solve the problem

 // 表单初始化
    formInit() {
      this.form = JSON.stringify(this.editForm) ? this.editForm : {}

      // checkbox类型的组件需要初始化数组
      this.formItemList.forEach(item => {
        if (['checkbox'].includes(item.type)) {
          if (!this.form[item.modelString]) {
            this.$set(this.form, item.model, [])
          } else {
            this.$set(this.form, item.model, this.form[item.modelString]?.split(','))
          }
        }
      })
    }
 // 接受子组件传回来的form表单内容
    getForm(val) {
      this.formItemList.forEach(item => {
        // 
        if (['checkbox'].includes(item.type)) {
          if (this.form[item.model] && this.form[item.model]?.length != 0) {
            val[item.modelString] = val[item.model]?.join(',')
          }
        }
      })
      console.log('val:', val);
      // 此处可以请求后台了
    },

3. JSX way to encapsulate el-form

The jsx encapsulation method is borrowed from this article: element-ui general form encapsulation and VUE JSX application - Nuggets

        3.1. Encapsulation

(1) The previous blog briefly introduced jsx: two ways to repackage el-table_code programming blog-CSDN blog

(2) Create a new jsxForm folder and create an index.js file

(3) Package code:

The code logic diagram is as follows:

export default {
  name: 'jsxForm',
  props: {
    // 生成el-form-item的数组
    formItemList: {
      type: Array,
      default: () => [],
    },
    // 是否行内表单模式
    inline: {
      type: Boolean,
      default: false,
    },
    // el-form-item的label宽度
    labelWidth: {
      type: String,
      default: '100px',
    },
    // 表单校验规则
    rules: {
      type: Object,
      default: () => { },
    },
    // 按钮列表
    buttonList: {
      type: Array,
      default: () => [],
    }
  },
  data() {
    return {
      form: {},
      checkboxList: []
    }
  },
  methods: {
    // 生成选项
    generateOption(itemObj) {
      let options = []
      for (let index = 0; index < itemObj.options.length; index++) {
        const item = itemObj.options[index]
        switch (itemObj.type) {
          // 下拉菜单
          case 'select':
            options.push(<el-option label={item.label} value={item.value}></el-option>)
            break
          // 多选框
          case 'checkbox':
            options.push(<el-checkbox label={item.label}></el-checkbox>)
            break
          // 单选框
          case 'radio':
            options.push(<el-radio label={item.label}>{item.label}</el-radio>)
            break
        }
      }
      return options
    },
    // 生成下拉菜单
    generateSelect(item) {
      return <el-select v-model={this.form[item.model]}>{this.generateOption(item)}</el-select>
    },
    // 生成多选框
    generateCheckbox(item) {
      this.form[item.model] = this.checkboxList
      return <el-checkbox-group v-model={this.checkboxList}>{this.generateOption(item)}</el-checkbox-group>
    },
    // 生成单选
    generateRadio(item) {
      return <div>
        <el-radio-group v-model={this.form[item.model]}>{this.generateOption(item)}</el-radio-group>
      </div>
    },
    // 生成输入框
    generateInput(item) {
      return (<div>
        <el-input v-model={this.form[item.model]} style={
   
   { width: `${this.formItemContentWidth}` }}></el-input>
      </div>)
    },
    // 生成开关
    generateSwitch(item) {
      return <div>
        <el-switch v-model={this.form[item.model]}></el-switch>
      </div>
    },
    // 生成日期组件
    generateDate(item) {
      return <div>
        <el-date-picker v-model={this.form[item.model]} type={"daterange"} range-separator={"至"}
          start-placeholder={"开始日期"} end-placeholder={"结束日期"}>
        </el-date-picker>
      </div >
    },
    // 生成表单项
    generateFormItems(list = []) {
      let formItems = []
      list.forEach(item => {
        let formItemContent = ''
        switch (item.type) {
          // 下拉菜单
          case 'select':
            formItemContent = this.generateSelect(item)
            break
          // 单选框
          case 'radio':
            formItemContent = this.generateRadio(item)
            break
          // 输入框
          case 'input':
            formItemContent = this.generateInput(item)
            break;
          // 开关
          case 'switch':
            formItemContent = this.generateSwitch(item)
            break;
          // 日期
          case 'date-picker':
            formItemContent = this.generateDate(item)
            break;
          // 复选框
          case 'checkbox':
            formItemContent = this.generateCheckbox(item)
            break;
          default:
            break
        }
        formItems.push(<el-form-item label={item.label} prop={item.model}>{formItemContent}</el-form-item>)
      })
      return formItems
    },
    // 按钮列表
    generateBtnList() {
      let buttons = []

      this.buttonList?.forEach(item => {
        buttons.push(<el-button type={item.type} onClick={() => item.event()}>{item.text}</el-button>)
      })
      return buttons
    },
    // 重置表单
    resetForm() {
      // 重置复选框
      this.checkboxList = []
      this.$refs.form.resetFields();
    },

    // 返回经过校验的表单
    returnForm() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          this.$emit('submitForm', this.form)
        }
      })
    }
  },
  render() {
    return (
      <div>
        <el-form ref="form" props={
   
   { model: this.form }} rules={this.rules} inline={this.inline} label-width={this.labelWidth || '150px'}>
          {this.generateFormItems(this.formItemList)}
          <el-form-item>
            {this.generateBtnList()}
          </el-form-item>
        </el-form>
      </div>
    )
  }
}

        3.2. Use

<template>
  <div id="app">
    <jsxForm ref="form" :buttonList="buttonList" :rules="rules" :formItemList="formItemList" @submitForm="getForm">
    </jsxForm>
  </div>
</template>

<script>
import jsxForm from '@/components/jsxForm';
export default {
  name: 'App',
  components: {
    JsxForm,
  },
  data() {
    return {
      formItemList: [
        { label: '活动名称', type: 'input', model: 'name', placeholder: '请输入活动名称' },
        { label: '活动区域', type: 'select', model: 'region', placeholder: '请选择状态', options: [{ label: '上海', value: 'shanghai' }, { label: '北京', value: 'beijing' }] },
        { label: '活动时间', type: 'date-picker', model: 'startTime', },
        { label: '即时配送', type: 'switch', model: 'delivery' },
        { label: '活动性质', type: 'checkbox', model: 'type', options: [{ label: '美食/餐厅线上活动' }, { label: '地推活动' }, { label: '线下主题活动' }, { label: '单纯品牌曝光' },] },
        { label: '特殊资源', type: 'radio', model: 'resource', options: [{ label: '线上品牌商赞助' }, { label: '线下场地免费' }] },
      ],
      rules: {
        name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
      },
      buttonList: [
        { text: '提交', type: 'primary', event: this.submitForm },
        { text: '重置', event: this.resetForm },
      ]
    }
  },
  methods: {
    // 接受子组件传回来的form表单内容
    getForm(val) {
      console.log('val:', val);
    },
    // 点击提交,触发表单校验
    submitForm() {
      this.$refs.form.returnForm();
    },
    // 重置表单
    resetForm() {
      this.$refs.form.resetForm();
    }
  }
}
</script>

Guess you like

Origin blog.csdn.net/weixin_42375707/article/details/130177717