前端配置化表单组件设计方法

科技资讯 投稿 9000 0 评论

前端配置化表单组件设计方法

一、背景

二、技术方案

•配置化表单组件的入参及说明

参数 说明 类型 可选值 默认值
labelWidth 表单元素label所占宽度 String —— 150px
columnList 表单元素所组成的配置,是一个数组 Array —— []
formData 表单元素值的集合 Object —— {}
columnSpan 表单排布分栏 Number —— 24
size 表单元素尺寸 String medium / small / mini medium
newColumnList( {
  const newColumnList= []
  const row = Math.floor(24 / this.columnSpan
  let newColumnItem = []
  for(let i=0; i< this.columnList.length; i++ {
    newColumnItem.push(this.columnList[i]
    if(newColumnItem.length === row || i === this.columnList.length-1 {
      newColumnList.push(newColumnItem
      newColumnItem = []
    }
  }
  return newColumnList
}

•通过上面得到的二维数组进行循环渲染,首先循环渲染行,其次循环渲染列。本方案采用element中的表单,当然也可以用其他组件库或者原生表单进行渲染,其原理通用。最终将会根据参数column.type决定加载哪一个具体的表单元素。

<el-form ref="form" :model="formData" :label-width="labelWidth" :size="size">
    <el-row :gutter="20" v-for="(element,index in newColumnList" :key="index+'formRow'">
      <template v-for="(item, index in element" >
        <column
          :key="index + 'formView'"
          :columnSpan="columnSpan"
          :column="item"
          :formData="formData"
        />
      </template>
    </el-row>
</el-form>

•column组件最终根据type加载具体的表单元素。下面展示column组件的入参及其说明,通过component加载不同的表单元素

参数 说明 类型 可选值 默认值
column 表单元素的具体配置 Object —— {}
formData 表单元素值的集合 Object —— {}
columnSpan 表单排布分栏 Number —— 24
<el-col :span="columnSpan">
     <component
      :is="column.type + 'View'"
      :column="column"
      :formData="formData"
      v-model="formData[column.name]"
      :columnSpan="columnSpan"/>
 </el-col>

•这里主要以select表单元素为例进行说明,表单元素的双向绑定、校验以及值更新等问题

参数 说明 类型 可选值 默认值
column 表单元素的具体配置 Object —— {}
value 表单元素值 Number/String/Array —— ——
参数 说明 类型 可选值 默认值
placeholder 空值说明 String —— ——
required 是否必填 Boolean —— ——
rules 校验规则 Array —— ——
title 表单元素label String —— ——
name 表单元素值名称 String —— ——
multiple 是否多选 Boolean —— ——
filterable 是否过滤 Boolean —— ——
disabled 是否禁用 Boolean —— ——
dictionary 下拉选项枚举 Array —— ——
changeFunction 值改变时的回调函数 Function —— ——
<el-form-item :label="column.title + ':'" :prop="column.name" :rules="rules">
    <el-select
      v-model="val"
      clearable
      :multiple="column.multiple"
      :filterable="column.filterable"
      :placeholder="'请选择' + column.title"
      :disabled="column.disabled"
      style="width: 100%"
      @change="onChange"
      @clear="onClear">
      <el-option v-for="item in column.dictionary" :key="item.code" :label="item.name" :value="item.code">
      </el-option>
    </el-select>
</el-form-item>

rules:  [
    {
      required: this.column.required,
      message: this.column.placeholder placeholder ? this.column.placeholder : `请输入${this.column.title}`,
      trigger: 'change'
    },
    ...this.column.rules
 ]

onChange({
  this.$emit('input',this.val
  if(this.column && this.column.changeFunction{
    this.column.changeFunction(this.val
  }
},
onClear({
  this.onChange(
}

三、项目实践

•配置化表单为bs-form,在页面中引入bs-form表单组件

<bs-form ref="formDemo"
     :columnList="columnList"
     :formData="formData"
     :columnSpan="columnSpan"
     labelWidth="120px">
</bs-form>
<el-row style="text-align: center;">
  <el-button type="primary"
             @click="onSave">保存</el-button>
  <el-button @click="onCancel">取消</el-button>
</el-row>

•formData参数

formData: {
    name: '',
    yearIncome: '', // 业务类型
    goodsCategoryId: '', // 托寄物品类id
    projectManagerErp: '', // 项目经理erp
    projectName: '', // 项目名称
    projectStage: '', // 项目阶段编码
    projectStandardName: '', // 标准名称
    projectYear: 2023, // 年份
    startRegionId: '', // 始发区域id
    startBattleId: '', // 始发战区id
    address: [], // 省市
    category: null, //图文类型
    range: [] //发布范围
 }

•分栏参数

•表单配置参数

columnList({
  const self = this
  return [
    {
      type: 'text',
      name: 'name',
      title: '项目名称',
      required: true,
      maxlength: 20,
      showwordlimit: true,
      placeholder: '请输入'
    },
    {
      name: 'category',
      type: 'radio',
      dictionary: [
        {
          code: 1,
          name: '类型一'
        },
        {
          code: 2,
          name: '类型二'
        }
      ],
      title: '图文类型',
      required: true
    },
    {
      name: 'range',
      type: 'checkbox',
      title: '发布范围',
      dictionary: [
        {
          code: 1,
          name: '范围一'
        },
        {
          code: 2,
          name: '范围二'
        }
      ],
      required: true
    },
    {
      type: 'text',  // 字段类型文本框
      name: 'yearIncome',  //与后台对接字段
      title: '年均收入',  // 前端展示字段
      required: true, // 必填项设置
      maxlength: 50,  // 字符串长度限制
      showwordlimit: true, // 是否显示字符串长度
      placeholder: '请输入', // 占位文本提示
      rules: [
        { pattern: /(^[1-9]([0-9]+?(.[0-9]{1,2}?$|(^(0{1}$|(^[0-9].[0-9]([0-9]?$/, message: '请输入数字最多两位小数' }
      ],
    },
    {
      type: 'select',
      name: 'goodsCategoryId',
      title: '托寄物品类',
      required: true,
      filterable: true,
      placeholder: '请选择',
      dictionary: [{
        name: '苹果',
        code: '1'
      },{
        name: '手机',
        code: '2'
      },{
        name: '测试',
        code: '3'
      },{
        name: '樱桃',
        code: '7'
      },{
        name: '荸荠',
        code: '9'
      }]
    },
    {
      type: 'select',
      name: 'startRegionId',
      title: '区域',
      required: true,
      placeholder: '请选择',
      dictionary: [{
        name: '销售-华北区域',
        code: '1'
      },{
        name: '销售-华东区域',
        code: '2'
      },{
        name: '销售-华南区域',
        code: '3'
      },{
        name: '销售-西南区域',
        code: '4'
      },{
        name: '销售-华中区域',
        code: '5'
      },{
        name: '销售-东北区域',
        code: '6'
      }],
      // 点击下来触发切换联动的事件,为一个函数
      changeFunction: function (val {
      }
    }, {
      type: 'select',
      name: 'startBattleId',
      title: '战区',
      required: true,
      placeholder: '请选择',
      dictionary: this.battleByRegionList
    }, {
      type: 'select',
      name: 'projectStage',
      title: '项目阶段',
      required: true,
      placeholder: '请选择',
      dictionary: [{
        name: '项目发起阶段',
        code: '10'
      },{
        name: '项目调研阶段',
        code: '20'
      },{
        name: '可行性分析阶段',
        code: '30'
      },{
        name: '立项阶段',
        code: '40'
      }]
    }, {
      type: 'text',
      name: 'projectStandardName',
      title: '标准名称',
      required: true,
      placeholder: '请输入',
      append: '.com',  // 文本框后置内容
    }, {
      type: 'text',
      name: 'projectManagerErp',
      title: '项目经理',
      required: true,
      placeholder: '请输入'
    },{
      type: 'cascader',  // 字段类型下拉框
      name: 'address',   //与后台对接字段
      title: '省市区',  // 前端展示字段
      required: true, // 必填项设置
      placeholder:'请选择',  // 占位文本提示
      dictionary: [{
        value: 'shanxi',
        label: '陕西省',
        children: [{
          value: 'xian',
          label: '西安市',
          children: [{
            value: 'yanta',
            label: '雁塔区'
          }, {
            value: 'beilin',
            label: '碑林区'
          }, {
            value: 'xincheng',
            label: '新城区'
          }, {
            value: 'weiyang',
            label: '未央区'
          }]
        }]
      }],
      // 点击下来触发切换联动的事件,为一个函数
      changeFunction: function({}
    },{
      type: 'static',
      name: 'projectYear',
      title: '年份'
    }
  ]
}

•表单保存

// 保存
async onSave( {
  const valid = await this.$refs.formDemo.onValidate(
  if(valid {
    this.$message.success('校验通过'
  }else {
    this.$message.error('校验失败'
  }
}

四、成果展示

作者:京东物流 田雷雷

编程笔记 » 前端配置化表单组件设计方法

赞同 (46) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽