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的让textarea自适应高度的插件
Aug 03 Javascript
Javascript 命名空间模式
Nov 01 Javascript
Bootstrap布局组件应用实例讲解
Feb 17 Javascript
JavaScript 链式结构序列化详解
Sep 30 Javascript
jQuery插件HighCharts实现的2D回归直线散点效果示例【附demo源码下载】
Mar 09 Javascript
借助node实战JSONP跨域实例
Mar 30 Javascript
详解Vue用axios发送post请求自动set cookie
May 10 Javascript
js+html5实现复制文字按钮
Jul 15 Javascript
jQuery实现简单的计时器功能实例分析
Aug 29 jQuery
开发一个Parcel-vue脚手架工具(详细步骤)
Sep 22 Javascript
js实现通过开始结束控制的计时器
Feb 25 Javascript
详解小程序开发经验:多页面数据同步
May 18 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 json与xml序列化/反序列化
2013/10/28 PHP
php判断文件夹是否存在不存在则创建
2015/04/09 PHP
Javascript typeof 用法
2008/12/28 Javascript
JQuery 确定css方框模型(盒模型Box Model)
2010/01/22 Javascript
jquery对表单操作2
2011/04/06 Javascript
jQuery控制图片的hover效果(smartRollover.js)
2012/03/18 Javascript
javaScript 页面自动加载事件详解
2014/02/10 Javascript
JQuery实现动态表格点击按钮表格增加一行
2014/08/24 Javascript
javascript事件冒泡实例分析
2015/05/13 Javascript
jQuery EasyUI学习教程之datagrid点击列表头排序
2016/07/09 Javascript
基于jQuery实现表格内容的筛选功能
2016/08/21 Javascript
对称加密与非对称加密优缺点详解
2017/02/06 Javascript
webpack学习教程之publicPath路径问题详解
2017/06/17 Javascript
详解JS中的柯里化(currying)
2017/08/17 Javascript
node前端模板引擎Jade之标签的基本写法
2018/05/11 Javascript
微信小程序网络请求封装示例
2018/07/24 Javascript
微信小程序使用npm支持踩坑
2018/11/07 Javascript
Vue el-autocomplete远程搜索下拉框并实现自动填充功能(推荐)
2019/10/25 Javascript
element-plus一个vue3.xUI框架(element-ui的3.x 版初体验)
2020/12/02 Vue.js
Python中列表元素转为数字的方法分析
2016/06/14 Python
Python编程之变量赋值操作实例分析
2017/07/24 Python
解决项目pycharm能运行,在终端却无法运行的问题
2019/01/19 Python
Python assert语句的简单使用示例
2019/07/28 Python
div或img图片高度随宽度自适应的方法
2020/02/06 HTML / CSS
英国最大的汽车交易网站:Auto Trader UK
2016/09/23 全球购物
美国在线打印网站:Overnight Prints
2018/10/11 全球购物
Kendra Scott官网:美国领先的时尚配饰品牌
2020/10/22 全球购物
大学生毕业自我鉴定
2013/11/06 职场文书
心理健康心得体会
2014/01/02 职场文书
航空学院求职信
2014/06/11 职场文书
志愿者服务活动总结报告
2015/05/06 职场文书
2015年音乐教研组工作总结
2015/07/22 职场文书
2016年“5.12”国际护士节活动总结
2016/04/06 职场文书
pytorch model.cuda()花费时间很长的解决
2021/06/01 Python
NGINX 权限控制文件预览和下载的实现原理
2022/01/18 Servers
一篇文章弄清楚Ajax请求的五个步骤
2022/03/17 Javascript