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 相关文章推荐
Javascript判断图片尺寸大小实例分析
Jun 16 Javascript
javascript修改图片src的方法
Jan 27 Javascript
jquery使用each方法遍历json格式数据实例
May 18 Javascript
js实现简洁的TAB滑动门效果代码
Sep 06 Javascript
12个超实用的JQuery代码片段
Nov 02 Javascript
详解Vue方法与事件
Mar 09 Javascript
javascript实现滑动解锁功能
Mar 22 Javascript
Angular2入门教程之模块和组件详解
May 28 Javascript
jQuery中each循环的跳出和结束实例
Aug 16 jQuery
微信小程序实现简单跑马灯效果
May 26 Javascript
图文详解vue框架安装步骤
Feb 12 Javascript
JavaScript高阶教程之“==”隐藏下的类型转换
Apr 11 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 xfocus防注入资料
2008/04/27 PHP
PHP 用数组降低程序的时间复杂度
2009/12/04 PHP
跟我学Laravel之安装Laravel
2014/10/15 PHP
老生常谈PHP面向对象之命令模式(必看篇)
2017/05/24 PHP
PHP如何获取Cookie并实现模拟登录
2020/07/16 PHP
获取任意Html元素与body之间的偏移距离 offsetTop、offsetLeft (For:IE5+ FF1 )[
2006/12/22 Javascript
javascript 实现父窗口引用弹出窗口的值的脚本
2007/08/07 Javascript
用方法封装javascript的new操作符(一)
2010/12/25 Javascript
JavaScript 操作table,可以新增行和列并且隔一行换背景色代码分享
2013/07/05 Javascript
JQuery对id中含有特殊字符的转义处理示例
2013/09/06 Javascript
购物车选中得到价格实现示例
2014/01/26 Javascript
分享2个jQuery插件--jquery.fileupload与artdialog
2014/12/26 Javascript
五步轻松实现zTree的使用
2017/11/01 Javascript
浅谈es6 javascript的map数据结构
2017/12/14 Javascript
webpack-dev-server自动更新页面方法
2018/02/22 Javascript
vue实现的网易云音乐在线播放和下载功能案例
2019/02/18 Javascript
vue中使用props传值的方法
2019/05/08 Javascript
解决vue页面渲染但dom没渲染的操作
2020/07/27 Javascript
[02:51]DOTA2战队出征照拍摄花絮 TI3明星化身时尚男模
2013/07/22 DOTA
[45:38]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第一局
2016/03/02 DOTA
python通过ftplib登录到ftp服务器的方法
2015/05/08 Python
分析并输出Python代码依赖的库的实现代码
2015/08/09 Python
python用reduce和map把字符串转为数字的方法
2016/12/19 Python
浅谈Python生成器generator之next和send的运行流程(详解)
2017/05/08 Python
pycharm 关掉syntax检查操作
2020/06/09 Python
Python 实现自动登录+点击+滑动验证功能
2020/06/10 Python
python自动化发送邮件实例讲解
2021/01/04 Python
HTML5中form如何关闭自动完成功能的方法
2018/07/02 HTML / CSS
html5实现九宫格抽奖可固定抽中某项奖品
2020/06/15 HTML / CSS
璀璨的珍珠、密钉和个性化珠宝:Lily & Roo
2021/01/21 全球购物
党员公开承诺书
2014/03/25 职场文书
中医学专业自荐信范文
2014/04/01 职场文书
活动总结格式范文
2014/04/26 职场文书
食品药品安全责任书
2015/05/11 职场文书
超级实用的公文标题大全!
2019/07/19 职场文书
如何用Navicat操作MySQL
2021/05/12 MySQL