封装Vue Element的可编辑table表格组件
前一段时间,有博友在我那篇封装Vue Element的table表格组件的博文下边留言说有没有那种“表格行内编辑”的封装组件,我当时说我没有封装过这样的组件,因为一直以来在实际开发中也没有遇到过这样的需求,但我当时给他提供了一个思路。
时间过去了这么久,公司的各种需求也不停地往外冒,什么地图图表、表格行内编辑、动态新增表单等等,只有你做不到,没有产品想不到,贼鸡儿累。再加上很快又要过年了,大家工作的心态基本呈直线下滑趋势而玩忽职守、尸位素餐以致饱食终日。只是话虽如此,但越是到年底,需求开发却越是紧急,平时可能一两周的开发任务,现在却要压缩到一周左右就要完成,苦不堪言。这不公司刚刚评完了需求,年前就让开发完成并提测,说是等年后来了,测试同学搞定后就上线。
话说这表格行内编辑,不光要在表格一行内实现文字的编辑,而且还要实现可新增一行或多行表格行内编辑的功能,同时还希望实现表格行内表单的正则验证。听着复杂,想着实现起来也复杂,其实不然,我们完全可以参照element动态增减表单项的模式来搞。原理大概其就是利用每一行的索引来设置每一个表单所需的prop和v-model,如果需要新增一行可编辑的表格行,只需往数据源数组中push一行相同的数据即可。
多说一句,年底了,这马上就要放假了,公司里很多人已经回老家了,我们这些留下来的人有一个算一个实在是没心思工作,但你以为这就可以放松了?可以摸摸鱼、划划水了?美得你。听没听说过一个女人:亚里士多德的妹妹,珍妮玛士多?
不过话又说回来,我们作为打工人,本职工作就是打工。你不工作,你还有脸称自己是打工人吗?你不是打工人,你连饭都吃不到嘴里,你还有脸说自己是“干饭人”?你还真想“十年一觉扬州梦”?东方不亮西方亮,二哈啥样你啥样。好好干活吧你!!!
这两天,趁着中午休息的时候,就把前一段时间加班加点完成的开发需求中的一个表格行内编辑的封装组件给发出来,兹当是给大家又提供了一个轮子亦或是多了一种选择吧。
照例先来张效果图:
1、封装的可编辑表格组件TableForm.vue
<template>
<el-form :model="form" ref="form" size="small">
<el-form-item v-if="!isDetail">
<el-button type="primary" @click="add">新增</el-button>
</el-form-item>
<el-table :data="form.list" border>
<el-table-column v-for="x in columns" :key="x.prop" :label="x.label" :prop="x.prop" v-bind="x.attr">
<template slot-scope="{row, $index}">
<t-text v-if="!x.edit" :row="{x, row}" />
<template v-else>
<t-text v-if="isDetail" :row="{x, row}" />
<t-input v-else v-model="row[`${x.prop}`]" v-bind="componentAttrs(x, $index)" class="width100" />
</template>
</template>
</el-table-column>
</el-table>
<el-form-item v-if="!isDetail" style="margin-top:18px;">
<el-button type="primary" @click="submit">提交</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
props: {config: Object},
computed: {
isDetail(){
const { isDetail = false } = this.config || {}
return isDetail
}
},
components: {
TInput: {
functional: true,
props: ['prop', 'rules', 'type', 'options'],
render: (h, {props: { prop, rules, type, options = [] }, data, listeners: {input = _.identity}}) => {
const children = h => {
if(type == 'select') return h('el-select', {class: 'width100', props: {...data.attrs}, on: {change(v){input(v)}}}, options.map(o => h('el-option', {props: {...o, key: o.value}})))
else if(type == 'checkbox') return h('el-checkbox-group', {props: {...data.attrs}, on: {input(v) {input(v)}}}, options.map(o => h('el-checkbox', {props: {...o, label: o.value, key: o.value}}, [o.label])))
else if(type == 'switch') return h('el-switch', {props: {activeColor: '#13ce66'}, ...data})
else if(type == 'date') return h('el-date-picker', {props: {type: 'date', valueFormat: 'yyyy-MM-dd',}, ...data})
return h('el-input', data)
}
return h('el-form-item', {props: {prop, rules}}, [children(h)])
}
},
TText: {
functional: true,
props: ['row'],
render: (h, {props: { row: { x, row } }}) => {
if(!row[`${x.prop}`]) return h('span', '-')
else if(x.format && typeof x.format == 'function') return h('span', x.format(row))
else return h('span', row[`${x.prop}`])
}
},
},
data(){
const { columns = [], data = [] } = this.config || {}
return {
form: {
list: (data && data.length > 0) ? data.map(n => columns.reduce((r, c) => ({...r, [c.prop]: n[c.prop] == false ? n[c.prop] : (n[c.prop] || (c.type == 'checkbox' ? [] : ''))}), {})) : [columns.reduce((r, c) => ({...r, [c.prop]: c.type == 'checkbox' ? [] : (c.type == 'switch' ? false : '')}), {})]
},
columns,
rules: columns.reduce((r, c) => ({...r, [c.prop]: { required: c.required == false ? false : true, message: c.label + '必填'}}), {})
}
},
methods: {
componentAttrs(item, idx){
const {type, label} = item, attrs = Object.fromEntries(Object.entries(item).filter(n => !/^(prop|edit|label|attr|format)/.test(n[0]))),
placeholder = (/^(select|el-date-picker)/.test(type) ? '请选择' : '请输入') + label
Object.assign(attrs, {prop: `list.${idx}.${item.prop}`, rules: this.rules[item.prop]})
return {...attrs, placeholder}
},
add(){
const { columns = [] } = this.config || {}, obj = columns.reduce((r, c) => ({...r, [c.prop]: c.type == 'checkbox' ? [] : (c.type == 'switch' ? false : '')}), {})
this.form.list.push(obj)
},
submit(){
this.$refs.form.validate((valid) => {
if (valid) {
this.$emit('submit', this.form.list)
}
})
},
reset(){
this.$refs.form.resetFields();
},
}
}
</script>
<style scoped>
.width100{width: 100%;}
</style>
本次封装的可编辑的表格组件,基本把大家在表格中内嵌的一些常用表单如:input输入框、select下拉框/选择器、日期选择器、checkbox复选框、switch开关等都封装进去了,大家只需根据自己的实际需求去添加不同的type就可以了,如果你还有其他的表单组件需要加进去,你自己按照我这个套路给封装进去就完事了。
另外,本次封装有几个点,大家注意下:
1)本次封装的组件,不光可以实现表格行内的编辑,同样当你编辑完成下次需要回显这些数据的时候,你只需多传一个isDetail参数就可以了,该参数默认为false。你往这儿看:
computed: {
isDetail(){
const { isDetail = false } = this.config || {}
return isDetail
}
}
页面当中也加了这个isDetail计算属性的判断。
2)也许有同学已经注意到了,在本次封装所用到的data数据对象中,有一串很长的实现方法:
list: (data && data.length > 0) ? data.map(n => columns.reduce((r, c) => ({...r, [c.prop]: n[c.prop] == false ? n[c.prop] : (n[c.prop] || (c.type == 'checkbox' ? [] : ''))}), {})) : [columns.reduce((r, c) => ({...r, [c.prop]: c.type == 'checkbox' ? [] : (c.type == 'switch' ? false : '')}), {})]
这段代码是干嘛滴的呢?
它两种用途:
没有数据需要回显时的可编辑表格的数据源
有数据需要回显时的可编辑表格的数据源
n[c.prop] == false ? n[c.prop] : ...
这段代码的判断或许有人会疑惑。大家要知道element的switch组件的值是非false既true的,不加这个判断,当数据回显时,switch为false的值就回显不出来。
2、使用方法:
<template>
<TableForm :config="config" @submit="submit" style="margin:20px;" />
</template>
<script>
import TableForm from "./TableForm";
const repayTypeList = {
averageCapital: '等额本金',
averageInterest: '等额本息'
},
columns = [
{ prop: 'repaymentMethod', label: '还款方式', attr: {width: '180'}, format: ({ repaymentMethod }) => repayTypeList[repaymentMethod]},
{ prop: 'productPer', label: '期数', attr: {width: '180'}, format: ({ productPer }) => `${+ productPer + 1}期(${productPer}个月)` },
{ prop: 'costRate', label: '成本利率', attr: {minWidth: '110'}, edit: true, type: 'select', options: [{label: '5%', value: '5'}, {label: '10%', value: '10'}] },
{ prop: 'price', label: '单价', attr: {minWidth: '110'}, edit: true },
{ prop: 'company', label: '所属公司', attr: {minWidth: '110'}, edit: true },
{ prop: 'product', label: '产品', attr: {minWidth: '110'}, edit: true, type: 'checkbox', options: [{label: '橘子', value: 'orange'}, {label: '苹果', value: 'apple'}] },
{ prop: 'date', label: '日期', attr: {minWidth: '110'}, edit: true, type: 'date', required: false, },
{ prop: 'opt', label: '锁定', attr: {minWidth: '110'}, edit: true, type: 'switch' },
]
export default {
components: {
TableForm,
},
data(){
return {
config: {
columns,
data: [],
},
}
},
created(){
this.config.data = [
{repaymentMethod: 'averageCapital', productPer: '1', price: '5', company: '谷歌上海', date: '2021-01-03', opt: false},
{repaymentMethod: 'averageInterest', productPer: '3', costRate: '10', price: '', company: '雅虎北京', opt: true},
{repaymentMethod: 'averageInterest', productPer: '5', costRate: '5', price: '100', company: '上海你真美', opt: false},
]
},
methods: {
submit(res){
console.log(res)
}
}
}
</script>
在使用的过程中,有一个需要注意的地方就是在columns数组中有一个required的属性,该属性默认为true,这个属性主要是用来控制当前的表单是否需要必填的校验的。还有需要说明的是,本次封装只是封装了每个表单是否需要必填的正则校验rules,没有对其他的正则验证如数字类型、小数位数等加以封装,如有需要你可以自行添加。
最后,通过子组件触发父组件的submit函数的方式来获取表格中表单的输入值。
封装Vue Element的可编辑table表格组件的更多相关文章
- 封装Vue Element的table表格组件
上周分享了几篇关于React组件封装方面的博文,这周就来分享几篇关于Vue组件封装方面的博文,也好让大家能更好地了解React和Vue在组件封装方面的区别. 在封装Vue组件时,我依旧会交叉使用函数式 ...
- 封装Vue Element的form表单组件
前两天封装了一个基于vue和Element的table表格组件,阅读的人还是很多的,看来大家都是很认同组件化.高复用这种开发模式的,毕竟开发效率高,代码优雅,逼格高嘛.虽然这两天我的心情很糟糕,就像& ...
- [js开源组件开发]table表格组件
table表格组件 表格的渲染组件,demo请点击http://lovewebgames.com/jsmodule/table.html,git源码请点击https://github.com/tian ...
- 使用 iview Table 表格组件修改操作的显示隐藏
使用 iview Table 表格组件修改操作的显示隐藏,如下图 1.如何设置 table 操作按后台传入的状态值去渲染 不同的按钮? 解决方法 我们在vue2中,动态渲染html 使用的是 retu ...
- Vue + Element-ui实现后台管理系统(5)---封装一个Form表单组件和Table表格组件
封装一个Form表单组件和Table组件 有关后台管理系统之前写过四遍博客,看这篇之前最好先看下这四篇博客.另外这里只展示关键部分代码,项目代码放在github上: mall-manage-syste ...
- 封装Vue Element的dialog弹窗组件
我本没有想着说要封装一个弹窗组件,但有同行的朋友在问我,而且弹窗组件也确实在项目开发中用的比较多.思前想后,又本着样式统一且修改起来方便的原则,还是再为大家分享一个我所封装的弹窗组件吧. 其实,并不是 ...
- Vue3学习(十一)之 table表格组件的使用
一.前言 大约有两周没学习更文,不是懒,而是没心情,相亲路屡战屡败,着实很影响心情. 我想这世上对我而言,最难的事,莫过于恋爱结婚了,再一次经历了见光死的高光时刻. 二.又见Ant Design Vu ...
- VUE+Element UI实现简单的表格行内编辑效果
原理是通过Css控制绑定的输入控件与显示值,在选中行样式下对控件进行隐藏或显示 <!DOCTYPE html> <html> <head> <meta cha ...
- 封装Vue Element的upload上传组件
本来昨天就想分享封装的这个upload组件,结果刚写了两句话,就被边上的同事给偷窥上了,于是在我全神贯注地写分享的时候他就神不知鬼不觉地突然移动到我身边,腆着脸问我在干啥呢.卧槽你妈,当场就把我吓了一 ...
随机推荐
- Spring--AOP、通知的执行顺序
AOP执行顺序 如果我们在同一个方法自定义多个AOP,我们如何指定他们的执行顺序呢? 可以通过指定order,order越小越是最先执行. 配置AOP执行顺序的三种方式: 通过实现Ordered接口 ...
- Linux设置系统时区
https://www.xlsys.cn/1741.html 如果你的 Linux 系统时区配置不正确,必需要手动调整到正确的当地时区.NTP 对时间的同步处理只计算当地时间与 UTC 时间的偏移量, ...
- 在Windows中安装MongoDB--图文并茂
在Windows环境下安装MongoDB的方法 (1)下载MongoDB Windows版: 进入MongoDB官网 (2)设置数据文件和日志文件的存放目录: 打开刚刚安装MongoDB的目录咋bin ...
- 使用Jenkins+Pipline 持构建自动化部署之安卓源码打包、测试、邮件通知
一.引言 Jenkins 2.x的精髓是Pipeline as Code,那为什么要用Pipeline呢?jenkins1.0也能实现自动化构建,但Pipeline能够将以前project中的配置信息 ...
- awk中的if ,else
local pct="$(awk -v one="$1" -v two="$2" 'BEGIN{ if (two > 0) { printf & ...
- 【ORA】ora-39700解决
- go语言循环变量
阅读go语言圣经第五章第六节介绍到了捕获迭代变量 package main import ( "fmt" ) func main() { var lis []func() for ...
- 记录一下 ThreadLocal 与 WeakReference
ThreadLocal & WeakReference Thread整体的模块图 Thread -> ThreadLocalMap 对于继承了 WeakReference Entry本身 ...
- ObjectMapper将josn字符串转化为List
一.利用ObjectMapper将json字符串转为List Student.java package objectmapper; import java.io.Serializable; publi ...
- 集成多种协议、用于 USB-A 和 TYPE-C 双端口输出的快充协议芯片IP2726
1. 特性 支持 1A1C 支持 USB-A 和 TYPE-C 双端口输出 单口输出支持全部快充协议 双口同时插入时降压到 5V 快充规格 集成 QC2.0/QC3.0/QC4/QC4+输 ...