Vue+ElementUI实现表单动态渲染、可视化配置的方法


Posted in Javascript onMarch 07, 2018

动态渲染就是有一个异步的数据,大概长这样:

{
 "inline": true,
 "labelPosition": "right",
 "labelWidth": "",
 "size": "small",
 "statusIcon": true,
 "formItemList": [
 {
 "type": "input",
 "label": "姓名",
 "disable": false,
 "readonly": false,
 "value": "",
 "placeholder": "请输入姓名",
 "rules": [],
 "key": "name",
 "subtype": "text"
 },
 {
 "type": "radio",
 "label": "性别",
 "value": "",
 "button": false,
 "border": true,
 "rules": [],
 "key": "gender",
 "options": [
 {
  "value": "1",
  "label": "男",
  "disabled": false
 },
 {
  "value": "0",
  "label": "女",
  "disabled": false
 }
 ]
 }
 ]
}

然后你需要把这个json渲染成这样:

Vue+ElementUI实现表单动态渲染、可视化配置的方法 

最后提交表单的数据长这样:

{
 "name": "Genji",
 "gender": "1"
}

然后我们目标就是封装这样一个组件:

<dynamic-form :config="someConfig" v-model="someData" />

实现

开始之前,你需要知道 v-model 的工作原理 :

<input v-model="something">

这不过是以下示例的语法糖:

<input
 :value="something"
 @input="something = $event.target.value">

了解这些后,我们再来一步一步实现这个组件。

首先,把配置转发到 el-form :

<template>
 <el-form 
 class="dynamic-form" 
 :inline="formConfig.inline" 
 :model="value" 
 :label-position="formConfig.labelPosition" 
 :label-width="formConfig.labelWidth" 
 :size='formConfig.size' 
 :status-icon="formConfig.statusIcon">
 <slot/>
 </el-form>
</template>
<script>
export default {
 props: {
 formConfig: {
 type: Object,
 required: true
 },
 value: {
 type: Object,
 required: true
 }
 },
}
</script>

第二步,设置默认值。

因为在每个 form-item 都会需要一个 v-model ,所以在渲染之前,保证每个字段都有值。这里需要注意一点,组件内不要直接修改父组件传入的 prop ,所以我们在这里用 {...this.value} 快速拷贝一份,最后别忘了通知父组件。代码如下:

export default {
 props: {
 formConfig: {...},
 value: {...},
 },
 methods: {
 setDefaultValue() {
 const formData = { ...this.value }
 // 设置默认值
 this.formConfig.formItemList.forEach(({ key, value }) => {
 if (formData[key] === undefined || formData[key] === null) {
  formData[key] = value
 }
 })
 this.$emit('input', formData)
 }
 },
 mounted() {
 this.setDefaultValue()
 },
}

第三步,渲染 form-item 。

如何把下面的数据渲染为我们熟悉的 el-form-item ?

{
 "type": "input",
 "label": "姓名",
 "disable": false,
 "readonly": false,
 "value": "",
 "placeholder": "请输入姓名",
 "rules": [],
 "key": "name",
 "subtype": "text"
}

第一种,利用 vue 内置的 component 组件,写起来可能像这样:

<el-form-item>
 <component :is="`el-${item.type}`" />
</el-form-item>

第二种,使用 v-if 逐个判断:

<el-form-item>
 <el-input v-if="item.type === 'input'" />
 <span v-else>未知控件类型</span>
</el-form-item>

考虑到每种表单控件的处理逻辑千差万别,楼主采用了第二种方式。

根据这个思路,我们来封装一个 dynamic-form-item ,接收一个 item ,渲染一个 el-form-item :

<template>
 <el-form-item :rules="item.Rules" :label="item.label" :prop="item.key">
 <el-input 
 v-if="item.type==='input'" 
 v-bind="$attrs" v-on="$listeners" 
 :type="item.subtype" 
 :placeholder="item.placeholder" 
 :disabled="item.disable" 
 :readonly="item.readonly" 
 :autosize="item.autosize"></el-input>
 <el-select 
 v-else-if="item.type==='select'" 
 v-bind="$attrs" v-on="$listeners"
 :multiple="item.multiple" 
 :disabled="item.disabled" 
 :multiple-limit="item.multipleLimit">
  <el-option 
  v-for="o in item.options" 
  :key="o.value" 
  :label="o.label" 
  :value="o.value" 
  :disabled="o.disabled">
  </el-option>
 </el-select>
 <!--突然有点想念JSX-->
 ...
 <span v-else>未知控件类型</span>
 </el-form-item>
</template>
<script>
export default {
 props: {
 item: {
 type: Object,
 required: true
 }
 }
}
</script>

tips: 使用 v-bind="$attrs" v-on="$listeners" 可以方便地转发父组件的 v-model 指令,详见vue高阶组件。

最后,我们就可以循环输出一个完整的表单了:

<dynamic-form-item
 v-for="item in formConfig.formItemList"
 :key="item.key"
 v-if="value[item.key]!==undefined"
 :item="item"
 :value="value[item.key]"
 @input="handleInput($event, item.key)" />

这里不能用 v-model="value[item.key]" ,上文说了,组件内不能直接修改props,所以这里我们还是转发一下。

methods: {
 handleInput(val, key) {
 // 这里element-ui没有上报event,直接就是value了
 this.$emit('input', { ...this.value, [key]: val })
 },
 setDefaultValue() {...}
},

完整代码地址:  src/components/dynamic-form/form.vue

扩展功能

1.数字显示单位,限制小数位数

element-ui 没有做这个功能,不过我觉得还是挺常见的,所以使用 el-input 手动封装了一个 input-number :

Vue+ElementUI实现表单动态渲染、可视化配置的方法 

<!--普通使用-->
<input-number 
 v-model="someNumber"
 :min="1" 
 :max="99" 
 :decimal1="2" 
 append="元"></input-number>
<!--在dynamic-form-item中的应用-->
<input-number 
 v-else-if="item.type==='number'" 
 v-bind="$attrs" v-on="$listeners" 
 :min="item.min" 
 :max="item.max" 
 :decimal1="item.decimal1" 
 :append="item.append" 
 :prepend="item.prepend" 
 :disabled="item.disabled"></input-number>

完整代码: src/components/dynamic-form/input-number.vue

2.异步验证

得益于 async-validator ,我们可以很方便地自定义验证规则。

Vue+ElementUI实现表单动态渲染、可视化配置的方法 

在配置中

{
 "type": "input",
 ...
 "rules":[
  {
   "sql": "SELECT {key} FROM balabala",
   "message": "xx已被占用",
   "trigger": "blur"
  }
 ]
}

dynamic-form-item 组件中, 遍历 item.rules , 将sql验证转化为自定义 validator 函数:

<template>
 <el-form-item :rules="Rules" >
  ...
 </el-form-item>
</template>
<script>
import request from '@/utils/request'
export default {
 props: {
 item: {...}
 },
 computed: {
 Rules() {
  const rules = this.item.rules
  if (rules === undefined) return undefined
  const R = []
  rules.forEach(rule => {
  if (rule.sql) {
   const validator = (rule2, value, callback) => {
   // 根据项目自行修改
   request('/api/validate', 'POST', {
    key: rule2.field,
    value,
    sql: rule.sql.replace(/{key}/ig, rule2.field)
   })
    .then(res => {
    callback(!res || undefined)
    })
    .catch(err => {
    this.$message.error(err.message)
    callback(false)
    })
   }
   R.push({ validator, message: rule.message, trigger: 'blur' })
  } else {
   R.push(rule)
  }
  })
  return R
 }
 },
}
</script>

3.省市区快捷配置

感谢 element-china-area-data 的作者。

在配置中:

{
 "type": "cascader",
 ...
 "areaShortcut": "provinceAndCityData"
}

在 dynamic-form-item 组件中:

<template>
 <el-form-item>
  ...
  <el-cascader 
   :options="item.options || require('element-china-area-data')[item.areaShortcut]"
   ></el-cascader>
 </el-form-item>
</template>

4.从远程加载选项

包括但不限于 radio 、 checkbox 、 cascader 、 select

在配置中:

{
 "type": "checkbox",
 ...
 "optionsUrl": "/api/some/options"
}

在 dynamic-form-item 组件中:

<template>
 <el-form-item>
  ...
  <el-select>
   <el-option 
    v-for="o in item.options || ajaxOptions"
    ></el-option>
  </el-select>
 </el-form-item>
</template>
<script>
import request from '@/utils/request'
export default {
 props: {
 item: {...}
 },
 computed: {...},
 data() {
 return {
  ajaxOptions: []
 }
 },
 created() {
 const { optionsUrl, key, type } = this.item
 if (optionsUrl) {
  // 根据项目自行修改
  request(`${optionsUrl}?key=${key}`, 'GET')
  .then(res => {
   this.ajaxOptions = res
  })
  .catch(err => { this.$message.error(err.message) })
 }
 }
}
</script>

总结

以上所述是小编给大家介绍的Vue+ElementUI实现表单动态渲染、可视化配置的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery 1.4 15个你应该知道的新特性(译)
Jan 24 Javascript
javascript与有限状态机详解
May 08 Javascript
js如何判断用户是否是用微信浏览器
Jun 05 Javascript
一个支持任意尺寸的图片上下左右滑动效果
Aug 24 Javascript
JQuery实现当鼠标停留在某区域3秒后自动执行
Sep 09 Javascript
jQuery调取jSon数据并展示的方法
Jan 29 Javascript
JS判断来路是否是百度等搜索索引进行弹窗或自动跳转的实现代码
Oct 09 Javascript
jquery自定义表单验证插件
Oct 12 Javascript
JavaScript实现弹出广告功能
Mar 30 Javascript
vue.js 上传图片实例代码
Jun 22 Javascript
Ionic学习日记实现验证码倒计时
Feb 08 Javascript
压缩Vue.js打包后的体积方法总结(Vue.js打包后体积过大问题)
Feb 03 Javascript
浅谈webpack打包之后的文件过大的解决方法
Mar 07 #Javascript
解决iview打包时UglifyJs报错的问题
Mar 07 #Javascript
React Native使用fetch实现图片上传的示例代码
Mar 07 #Javascript
使用vue制作探探滑动堆叠组件的实例代码
Mar 07 #Javascript
基于vue-cli 打包时抽离项目相关配置文件详解
Mar 07 #Javascript
关于Vue的路由权限管理的示例代码
Mar 06 #Javascript
vue.js在标签属性中插入变量参数的方法
Mar 06 #Javascript
You might like
php读取目录所有文件信息dir示例
2014/03/18 PHP
PHP使用mysqli同时执行多条sql查询语句的实例
2019/03/22 PHP
ExtJS4 组件化编程,动态加载,面向对象,Direct
2011/05/12 Javascript
如何获取JQUERY AJAX返回的JSON结果集实现代码
2012/12/10 Javascript
js获得鼠标的坐标值的方法
2013/03/13 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
2015/05/18 Javascript
学习JavaScript设计模式之责任链模式
2016/01/18 Javascript
jQuery中判断对象是否存在的方法汇总
2016/02/24 Javascript
jQuery过滤选择器经典应用
2016/08/18 Javascript
Angular 2.x学习教程之结构指令详解
2017/05/25 Javascript
基于JS抓取某高校附近共享单车位置 使用web方式展示位置变化代码实例
2019/08/27 Javascript
Vue中keep-alive的两种应用方式
2020/07/15 Javascript
[00:47]DOTA2荣耀之路6:天火,天火!
2018/05/30 DOTA
[54:19]完美世界DOTA2联赛PWL S2 Magma vs PXG 第二场 11.28
2020/12/01 DOTA
Python中集合的内建函数和内建方法学习教程
2015/08/19 Python
浅析AST抽象语法树及Python代码实现
2016/06/06 Python
python 通过字符串调用对象属性或方法的实例讲解
2018/04/21 Python
Python Tkinter模块实现时钟功能应用示例
2018/07/23 Python
Django ModelForm组件使用方法详解
2019/07/23 Python
一行Python代码制作动态二维码的实现
2019/09/09 Python
python3实现raspberry pi(树莓派)4驱小车控制程序
2020/02/12 Python
Python3.6安装卸载、执行命令、执行py文件的方法详解
2020/02/20 Python
Python开发之pip安装及使用方法详解
2020/02/21 Python
Python求凸包及多边形面积教程
2020/04/12 Python
Tensorflow实现将标签变为one-hot形式
2020/05/22 Python
基于OpenCV的路面质量检测的实现
2020/11/04 Python
系统管理员的职责包括那些?管理的对象是什么?
2016/09/20 面试题
给定一个时间点,希望得到其他时间点
2013/11/07 面试题
工商管理专业实习大学生自我鉴定
2013/09/19 职场文书
优秀家长自荐材料
2014/08/26 职场文书
社区环境卫生倡议书
2015/04/29 职场文书
背起爸爸上学观后感
2015/06/08 职场文书
电工实训心得体会
2016/01/14 职场文书
商业计划书范文
2019/04/24 职场文书
vue实现简单数据双向绑定
2021/04/28 Vue.js
《吸血鬼:避世 血猎》官宣4.27发售 系列首款大逃杀
2022/04/03 其他游戏