vue + elementUI动态生成表格,并实现内容可编辑

最终效果如下,可自由编辑表头、数据单元格,增删行列,并保持数据绑定
vue + elementUI动态生成表格,并实现内容可编辑_第1张图片

1. 首先最基本的,vue elementUI根据数据生成固定表项的表格

从官网摘个Demo过来:

tableData: [
	{date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄'},
	{date: '2016-05-04', name: '王小虎', address: '上海市普陀区金沙江路 1517 弄'}
]
<el-table :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="日期" width="180">el-table-column>
    <el-table-column prop="name" label="姓名" width="180">el-table-column>
    <el-table-column prop="address" label="地址">el-table-column>
el-table>

样式如下:
vue + elementUI动态生成表格,并实现内容可编辑_第2张图片

2. 如果表项也动态生成呢?

常用的写法:

testCols: ["No","name","age","city","tel"],
testDatas: [
	{No: 1, name: '张三', age: 24, city: '广州', tel: '13312345678'},
	{No: 2, name: '李四', age: 25, city: '九江', tel: '18899998888'},
	{No: 3, name: '王五', age: 26, city: '六盘水', tel: '13600001111'},
	{No: 4, name: '赵二', age: 27, city: '菏泽', tel: '13145209420'},
]
<el-table :data="testDatas" border stripe style="width: 100%">
  <el-table-column
  	v-for="(col, idx) in testCols"
  	:prop="col"
  	:label="col"
  	:key="idx">
  el-table-column>
el-table>

显示:
vue + elementUI动态生成表格,并实现内容可编辑_第3张图片

3. 单元格可编辑

网上早有网友讨论过这个功能,我采用的是将单元格数据转换成对象,添加属性show来控制其在文字与输入框间切换。听起来就不想下手,又要转换数据格式了。。

// 表项(头)
testCols: [ // 以键值存储表头,值可改变,键不变用以绑定数据
	{col: "name", txt: 'name', show: true},
	{col: "age", txt: 'age', show: true},
	{col: "city", txt: 'city', show: true},
	{col: "tel", txt: 'tel', show: true}
],
// 数据
testDatas: [{
		name: {content: '张三', show: true},
		age: {content: 24, show: true},
		city: {content: '广州', show: true},
		tel: {content: '13312345678', show: true}
	},{
		name: {content: '李四', show: true},
		age: {content: 25, show: true},
		city: {content: '九江', show: true},
		tel: {content: '18899998888', show: true}
	}
]
<el-table :data="testDatas" border style="width: 100%">
	
	<el-table-column v-if="testCols.length > 0" type="index" :label="'编号'" :width="50">el-table-column>
    <el-table-column v-for="(column, idx) in testCols" :key="idx">
		
	  	<template slot="header" slot-scope="scope1">
	  		
	  		<p v-show="column.show" @dblclick="column.show=false">
	  			{{column.txt}} 
	  			<i class="el-icon-edit-outline" @click="column.show=false">i>
	  		p>
	  		
	  		<el-input
	  			size="mini"
	  			v-show="!column.show"
	  			v-model="column.txt"
	  			@blur="column.show=true">
	  		el-input>
	    template>

		
	  	<template slot-scope="scope">
	  		
	  		
	  		<p v-show="scope.row[column.col].show" @dblclick="scope.row[column.col].show=false">
	  			{{scope.row[column.col].content}} 
	  			<i class="el-icon-edit-outline" @click="scope.row[column.col].show=false">i>
	  		p>
	  		
	  		<el-input
	  			type="textarea"
	  			:autosize="{minRows:2,maxRows:4}"
	  			v-show="!scope.row[column.col].show"
	  			v-model="scope.row[column.col].content"
	  			@blur="scope.row[column.col].show=true">
	  		el-input>
	  	template>
	el-table-column>
el-table>

vue + elementUI动态生成表格,并实现内容可编辑_第4张图片

4. 插入、删除一行或一列数据

做到第三点,这一步就没什么难度了,无非就是更改数组。
末尾添加行、删除末行、末尾添加列、删除末列,都能直接实现,但想要实现对每一行每一列的操作,要用到Element表格插件提供的一些属性方法了。

el-table-column绑定下标

<el-table-column v-for="(column, idx) in testCols" :key="idx" :index="idx">
...

给el-table绑定表头右键事件(@header-contextmenu=“colRightClick”)

colRightClick(column,event) {
	window.event.returnValue = false; //阻止浏览器自带的右键菜单弹出
	this.curColumn = column.index // 鼠标右键的列下标
	this.showMenu = true // 菜单块显隐
	var ele = document.getElementById('contextmenu') // 菜单块定位
	ele.style.top = event.clientY + 'px';
	ele.style.left = event.clientX + 'px';
	if(window.innerWidth - 140 < event.clientX) {
		ele.style.left = 'unset'
		ele.style.right = 0
	}
},

这样,就控制插入新列、删除列的位置了。
vue + elementUI动态生成表格,并实现内容可编辑_第5张图片
关于行的操作,虽然ElementUI提供了行、单元格的右键事件,但并不知道row的下标,如果存在“主键”一样的属性,可以通过它找到具体下标。

坑:
新增列的时候,要为所有行添加新的对象,通过一般的添加对象属性是无法被vue监听到的。具体参考这篇文章:关于vue无法侦听数组及对象属性的变化的解决方案

addColumn(idx) { // 新增列
	var obj = {col: 'col_' + this.count_col++, txt: '', show: true} // 新增列对象
	this.testDatas.map(p => {
		_this.$set(p, obj.col, {content: '', show: true})
		//	p[obj.col] = {content: '', show: true}
	})
}

vue + elementUI动态生成表格,并实现内容可编辑_第6张图片
vue + elementUI动态生成表格,并实现内容可编辑_第7张图片

表格单元格编辑代码:Git链接

<template>
  <div id="hello">	
		<h4 style="display: inline-block;margin:0;">测试表</h4>
		<div style="display: inline-block;float: right;">
			<el-button size="mini" type="primary" @click="consoleDatas">打印数据</el-button>
			<el-button size="mini" type="primary" @click="addRow">增加行</el-button>
			<el-popconfirm title="确定删除最后行吗?" @onConfirm="delLastRow">
				<el-button slot="reference" type="primary" size="mini">删除末行</el-button>
			</el-popconfirm>
		</div>
<el-table :data="testDatas" border style="width: 100%;margin-top:10px" @header-contextmenu="colRightClick">
	<el-table-column v-if="testCols.length > 0" type="index" :label="'编号'" :width="50"></el-table-column>
  <el-table-column v-for="(column, idx) in testCols" :key="idx" :index="idx">
		<!--label-->
  	<template slot="header" slot-scope="scope1">
  		<p v-show="column.show" @dblclick="column.show=false">
  			{{column.txt}} 
  			<i class="el-icon-edit-outline" @click="column.show=false"></i>
  		</p>
  		<el-input
  			size="mini"
  			v-show="!column.show"
  			v-model="column.txt"
  			@blur="column.show=true">
  		</el-input>
    </template>
		<!--prop-->
  	<template slot-scope="scope">
  		<p v-show="scope.row[column.col].show" @dblclick="scope.row[column.col].show=false">
  			{{scope.row[column.col].content}} 
  			<i class="el-icon-edit-outline" @click="scope.row[column.col].show=false"></i>
  		</p>
  		<el-input type="textarea" :autosize="{minRows:2,maxRows:4}"
  			v-show="!scope.row[column.col].show"
  			v-model="scope.row[column.col].content"
  			@blur="scope.row[column.col].show=true">
  		</el-input>
  	</template>
  </el-table-column>
</el-table>

		<div v-show="showMenu" id="contextmenu">
			<i class="el-icon-circle-close hideContextMenu" @click="showMenu=false"></i>
			<el-button size="mini" type="primary" @click="addColumn(curColumn)">前方插入一列</el-button>
			<el-button size="mini" type="primary" @click="addColumn(curColumn+1)">后方插入一列</el-button>
			<el-popconfirm title="确定删除该列吗?" @onConfirm="delColumn">
				<el-button slot="reference" type="primary" size="mini">删除当前列</el-button>
			</el-popconfirm>
		</div>

  </div>
</template>

<script>
export default {
 name: 'demo',
  data(){
	 return{
			testCols: [
				{col: "name", txt: 'name', show: true},
				{col: "age", txt: 'age', show: true},
				{col: "city", txt: 'city', show: true},
				{col: "tel", txt: 'tel', show: true}
			],
			testDatas: [{
					name: {content: '张三', show: true},
					age: {content: 24, show: true},
					city: {content: '广州', show: true},
					tel: {content: '13312345678', show: true}
				},{
					name: {content: '李四', show: true},
					age: {content: 25, show: true},
					city: {content: '九江', show: true},
					tel: {content: '18899998888', show: true}
				}
			],
			count_col: 0,
			showMenu: false,
			curColumn: null,
	 }
  },
	methods:{
		colRightClick(column,event) {
			window.event.returnValue = false; //阻止浏览器自带的右键菜单弹出
			if(!column.index && column.index !== 0) return;
			this.curColumn = column.index
			this.showMenu = true
			var ele = document.getElementById('contextmenu')
			ele.style.top = event.clientY + 'px';
			ele.style.left = event.clientX + 'px';
			if(window.innerWidth - 140 < event.clientX) {
				ele.style.left = 'unset'
				ele.style.right = 0
			}
		},
		addRow() { // 新增行
			this.showMenu = false
			var obj = {}
			this.testCols.map(p => {
				obj[p.col] = {content: '', show: true}
			})
			this.testDatas.push(obj)
		},
		// 当row中存在一“主键”可唯一标识row的下标时(如:编号放在testDatas内),可借此实现行的自由插入与删除
		addColumn(idx) { // 新增列
			this.showMenu = false
			var obj = {col: 'col_' + this.count_col++, txt: '', show: true}
			if(idx || idx === 0) this.testCols.splice(idx, 0, obj);
			else this.testCols.push(obj);
			var _this = this
			this.testDatas.map(p => { // 新增的对象无法被vue监听到
				_this.$set(p, obj.col, {content: '', show: true})
//				p[obj.col] = {content: '', show: true}
			})
		},
		delColumn() { // 删除列
			this.showMenu = false
			var colKey = this.testCols[this.curColumn].col;
			this.testCols.splice(this.curColumn, 1);
			this.testDatas.map(p => {
				delete p[colKey];
			});
		},
		delLastRow() { // 删除行
			this.showMenu = false
			var len = this.testDatas.length;
			if(len > 0) this.testDatas.splice(len - 1, 1);
			else this.$message.error('没有可删除行');
		},
		consoleDatas() {
			console.log('表头',this.testCols);
			console.log('数据',this.testDatas);
		}
	}
}
</script>
  
<style scoped>
#hello {position: relative;}
#contextmenu {
	position:absolute;
	top: 0;
	left: 0;
  height:auto;
  width:120px;
  border-radius: 3px;
  border: 1px solid #999999;
  background-color: #f4f4f4;
  padding: 10px;
	z-index: 12;
}
#contextmenu button {display: block;margin: 0 0 5px;}
.hideContextMenu {position: absolute;top: 5px;right: 5px;}
</style>

你可能感兴趣的:(前端)