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

2025-12-13 0 978

一、背景

前端开发中涉及表单的页面非常多,看似功能简单,开发快速,实则占去了很大一部分时间。当某个表单包含元素过多时还会导致 html 代码过多,vue 文件过大。从而不容易查找、修改和维护。为了提高开发效率及降低维护成本,下面介绍表单配置组件的封装原理与封装方法

二、技术方案

如上图所示,封装表单配置化组件的关键点有三个一是如何解决表单元素排布的行列问题,二是表单数据的绑定问题,三是表单元素的参数配置校验等问题。下面分别介绍这三个问题的解决方法。

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

参数 说明 类型 可选值 默认值
labelWidth 表单元素 label 所占宽度 String —— 150px
columnList 表单元素所组成的配置,是一个数组 Array —— []
formData 表单元素值的集合 Object —— {}
columnSpan 表单排布分栏 Number —— 24
size 表单元素尺寸 String medium / small / mini medium

?计算配置化表单的行数,本表单通过基础的 24 分栏计算表单最终的行数和列数,通过下面方法最终得到一个关于行列的二维数组

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 —— ——

?column 参数

参数 说明 类型 可选值 默认值
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: [] //发布范围
 }

?分栏参数

columnSpan: 6

?表单配置参数

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(\'校验失败\')
  }
}

四、成果展示

作者:京东物流 田雷雷

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 前端配置化表单组件设计方法 https://www.zuozi.net/36208.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务