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

前端,配置,表单,组件,设计,方法 · 浏览次数 : 92

小编点评

**代码展示作者:京东物流 田雷雷。归纳总结以上内容,生成内容时需要带简单的排版** **代码展示:** ```javascript 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: '10' },{ name: '类型二', code: '20' },{ name: '可行性分析阶段', code: '30' },{ name: '立项阶段', code: '40' }] }, { 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('校验失败') }}四、成果展示作者:京东物流 田雷雷。归纳总结以上内容,生成内容时需要带简单的排版**

正文

一、背景

前端开发中涉及表单的页面非常多,看似功能简单,开发快速,实则占去了很大一部分时间。当某个表单包含元素过多时还会导致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('校验失败')
  }
}

四、成果展示

作者:京东物流 田雷雷

与前端配置化表单组件设计方法相似的内容:

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

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

实现动态表单的一种思路

区别于传统表单前后端配合联调的开发实现方式,动态表单通过一种基于元数据管理的配置化方法来实现表单的动态生成,并能根据配置自由增改删指定字段。实现特定需求的自助化。

如何在Spring Boot框架下实现高效的Excel服务端导入导出?

前言 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。今天我们就使用纯前对按表格控件带大家了解,如何在Spring Boot框架下实现Excel服务端导

Spring Boot框架下实现Excel服务端导入导出

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。今天我们就使用纯前对按表格控件带大家了解,如何在Spring Boot框架下实现Excel服务端导入导出

Redux与前端表格施展“组合拳”,实现大屏展示应用的交互增强

本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 Redux 是 JavaScript 状态容器,提供可预测化的状态管理。它可以用在 react、angular、vue 等项目中, 但与 react 配合使用更加方便一

配置式表单渲染器的实现

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。。 本文作者:奇铭(掘金) 需求背景 前段时间,离线计算产品接到改造数据同步表单的需求。 一方面,数据同步模块的代码可读性和可维护性比较差,相对应的,在数据同步模块开发新

618技术揭秘:探究竞速榜页面核心前端技术

本文将探究京东竞速榜H5页面的核心前端技术,包括动画、样式配置化、皮肤切换、海报技术、调试技巧等方面,希望能够为广大前端开发者提供一些有用的参考和思路。

BI系统打包Docker镜像及部署的技术难度和实现

BI系统打包Docker镜像及部署的技术难度和实现 随着容器化技术盛行,Docker在前端领域也有着越来越广泛的应用;传统的前端部署方式需要我们将项目打包生成一系列的静态文件,然后上传到服务器,配置nginx文件;如果我们使用容器化部署,将部署操作都命令化,集中成一个脚本就可以完成原来复杂的部署过程

RedisStack部署/持久化/安全/与C#项目集成

前言 Redis可好用了,速度快,支持的数据类型又多,最主要的是现在可以用来向量搜索了。 本文记录一下官方提供的 redis-stack 部署和配置过程。 关于 redis-stack redis-stack installs a Redis server with additional datab

[转帖]Redis故障检查:识别慢查询操作

https://weibo.com/ttarticle/p/show?id=2309404650615585505652 使用SLOWLOG命令查看Redis中的慢查询操作。 ​​前几篇日志总结了下对Redis部署时的一些配置,Redis启动后,面对各种请求,数据持久化到硬盘,很可能会出现内存不足等