下面是一个可以实现编辑的表格,并且该表格嵌入在form表单内,可以对已修改的内容进行保存提交;而且实现了对表格数据进行关键字搜索和分类筛选的功能,实现了前端分页的效果。
主要难点:
(1)当el-table列数据过多时,我们可以通过设置 type="expand" 和 Scoped slot
可以开启展开行功能,然后在每一行设置一个可编辑按钮,对展开的数据中的某条数据进行再编辑。
(2)当你设置可编辑按钮时,还要对此行展开状态进行判断,判断该行是否为展开,若已展开,则编辑,若未展开,则展开进行编辑。
(3)当对某一行进行编辑时,如该行未确认完成编辑,则不能对其他行再进行编辑,需要对该行确认完成编辑,才可对其他行进行编辑操作。
(4)对数据进行分页展示。
(5)对数据进行关键字搜索和根据分类对每页数据进行筛选。
(6)保存和提交数据的检验。
效果图如下:
![效果图(1)](https://i-blog.csdnimg.cn/blog_migrate/017f61d16c7a126e9721c7c631298bc3.png)
效果视频如下:
可编辑el-table表格演示视频
下面是代码部分:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>可编辑表格</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<script src="../vue/vue.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../elementUI/index.js" type="text/javascript" charset="utf-8"></script>
<script src="../vue/axios.min.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" type="text/css" href="../elementUI/index.css" />
<style type="text/css">
.menu {
width: 100%;
height: 100%;
}
.container_table {
width: 100%;
}
.el-table .cell {
text-align: center;
}
.postForm {
width: 340px;
height: 40px;
float: right;
margin-top: -40px;
}
/* 展开的form样式 */
.demo-table-expand label {
width: 90px;
color: #99a9bf;
}
.el-form--label-left .el-form-item__label {
text-align: right;
}
.demo-table-expand .el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 30%;
}
.container_table .fy {
margin-top: 20px;
}
</style>
</head>
<body>
<div id="menu" class="menu">
<el-container>
<div class="container_table">
<el-form :model="tableData" @submit.native.prevent>
<el-table ref="refTable" height="500" :data="tableData.slice((currentPage-1)*pagesize,currentPage*pagesize)"
style="width: 100%" @expand-change="expandChange">
<el-table-column type="expand">
<template slot-scope="props">
<el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="日期">
<span>{{ props.row.date }}</span>
</el-form-item>
<el-form-item label="姓名">
<span>{{ props.row.name }}</span>
</el-form-item>
<el-form-item label="手机号">
<span v-if="props.row.isSet">{{props.row.phone}}</span>
<span v-else>
<el-input clearable v-model="props.row.phone"></el-input>
</span>
</el-form-item>
<el-form-item label="地址">
<span v-if="props.row.isSet">{{props.row.address}}</span>
<span v-else>
<el-input type="textarea" clearable autosize v-model="props.row.address"></el-input>
</span>
</el-form-item>
<el-form-item label="部门">
<span>{{ props.row.tag }}</span>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column prop="date" label="日期" sortable width="120" column-key="date">
</el-table-column>
<el-table-column prop="name" label="姓名">
</el-table-column>
<el-table-column prop="phone" label="手机号">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
<el-table-column prop="tag" label="部门" width="140" :filters="sectorData" :filter-method="filterTag">
<template slot-scope="scope">
<el-tag size="medium" disable-transitions>{{scope.row.tag}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="searchInfo" width="200">
<template slot="header" slot-scope="scope">
<el-input clearable v-model="search" prefix-icon="el-icon-search" size="mini" placeholder="输入姓名关键字搜索" @input="searchName" />
</template>
<template slot-scope="scope">
<el-button :disabled="btnDisabled" plain icon="el-icon-edit" v-if="scope.row.isSet" @click="handleEdit(scope.$index, scope.row)"
size="mini">编辑</el-button>
<el-button plain v-else @click="saveEdit(scope.$index, scope.row)" size="mini">确定</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-pagination class="fy" :current-page="currentPage" :page-sizes="[5, 10, 20]" :page-size="pagesize" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange" @current-change="handleCurrentChange" :total="tableData.length" background>
</el-pagination>
<div class="postForm">
<el-button icon="el-icon-finished" type="primary" @click="postForm">提交</el-button>
<el-button icon="el-icon-check" type="success" @click="saveForm">保存</el-button>
<el-button icon="el-icon-back" type="info" @click="backtrack">撤回</el-button>
</div>
</div>
</el-container>
</div>
</body>
<script type="text/javascript">
var vm = new Vue({
el: '#menu',
data() {
return {
/* 表格数据 */
tableData: [],
/* 原始表格数据 */
oldtableData: [],
/* 部门数据 */
sectorData: [],
pagesize: 5, //每页的数据条数
currentPage: 1, //默认开始页面
search: '', //搜索关键字
btnDisabled: false,
};
},
methods: {
/* 部门筛选 */
filterTag(value, row, column) {
console.log(value);
// console.log(row);
return row.tag === value;
},
/* 分页方法 */
handleSizeChange(val) {
// console.log(`每页 ${val} 条`);
this.pagesize = val;
},
handleCurrentChange: function(val) {
// console.log(`当前页: ${val}`);
this.currentPage = val;
},
/* 姓名关键字搜索 */
searchName(search) {
var newList = [];
// console.log(search);
if (search != '') {
vm.oldtableData.forEach(item => {
if (item.name.indexOf(search) != -1) {
//空字符串包含在所有字符串中,因此控制会返回所有列表
newList.push(item);
}
});
return this.tableData = newList;
} else {
return this.tableData = this.oldtableData;
}
},
/* 更改每一行的张开/关闭状态 */
expandChange(row, expandedRows) {
row.isExpand = !row.isExpand;
},
/* 开启编辑单元格 */
handleEdit(index, row, e) {
for (const i of this.tableData) {
if (!i.isSet) {
return this.$message.warning('请先保存当前编辑项');
}
}
if (!row.isExpand) {
//调用,table的方法,展开/折叠 行
this.$refs.refTable.toggleRowExpansion(row);
}
row.isSet = false;
},
/* 保存单元格,进行编辑验证 */
saveEdit(index, row, column) {
// console.log(row);
if ((row.address.replace(/\s+/g, '') == '' || (row.phone.replace(/\s+/g, '') == ''))) {
row.isSet = false;
return this.$message.error('数据不能为空');
} else {
row.isSet = true;
}
//调用,table的方法,展开/折叠 行
this.$refs.refTable.toggleRowExpansion(row);
},
/* 提交表单 */
postForm() {
if (!this.btnDisabled) {
return this.$message.error('请先保存');
} else {
this.$confirm('是否提交?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then((action) => {
this.$message({
type: 'success',
message: '提交成功!',
});
if (action === 'confirm') {
//提交表单
console.log(this.tableData);
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消提交!'
});
});
}
},
/* 保存表单 */
saveForm() {
for (var i = 0; i < this.tableData.length; i++) {
if (!this.tableData[i].isSet) {
this.$message.warning('请先保存当前编辑项');
return this.btnDisabled = false;
} else {
this.btnDisabled = true;
}
}
},
/* 撤回保存 */
backtrack() {
this.btnDisabled = false;
},
/* 获取内容数据 */
getTableData() {
axios.get('../data/tableData.json', {})
.then(function(res) {
for (var i = 0; i < res.data[0].tableData.length; i++) {
// 添加单元格是否可以编辑的属性值
res.data[0].tableData[i].isSet = true;
// 添加行是否展开的属性值
res.data[0].tableData[i].isExpand = false;
}
console.log(res.data);
// 赋值
vm.tableData = res.data[0].tableData;
vm.oldtableData = res.data[0].tableData;
vm.sectorData = res.data[1].sectorData;
}).catch(function(error) {
console.log(error);
});
}
},
mounted() {
// 获取内容数据
this.getTableData();
}
});
</script>
</html>
data数据如下:
[{
"tableData": [{
"date": "2016-05-01",
"name": "张晓明",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 11 弄",
"tag": "设计三所"
}, {
"date": "2016-05-02",
"name": "李米中",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 12 弄",
"tag": "设计二所"
}, {
"date": "2016-05-03",
"name": "张晓明",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 13 弄",
"tag": "南京小组"
}, {
"date": "2016-05-04",
"name": "李米中",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 14 弄",
"tag": "设计一所"
}, {
"date": "2016-05-05",
"name": "向飒飒",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 15 弄",
"tag": "南京小组"
}, {
"date": "2016-05-06",
"name": "王小虎",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 16 弄",
"tag": "设计三所"
},
{
"date": "2016-05-07",
"name": "张晓明",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 17 弄",
"tag": "设计二所"
}, {
"date": "2016-05-08",
"name": "李米中",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 18 弄",
"tag": "南京小组"
}, {
"date": "2016-05-09",
"name": "向飒飒",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 19 弄",
"tag": "设计一所"
}, {
"date": "2016-05-10",
"name": "王小虎",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 20 弄",
"tag": "设计三所"
},
{
"date": "2016-05-11",
"name": "张晓明",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 21 弄",
"tag": "设计三所"
}, {
"date": "2016-05-12",
"name": "李米中",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 22 弄",
"tag": "设计二所"
}, {
"date": "2016-05-13",
"name": "向飒飒",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 23 弄",
"tag": "南京小组"
}, {
"date": "2016-05-14",
"name": "王小虎",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 24 弄",
"tag": "设计一所"
}, {
"date": "2016-05-15",
"name": "王小虎",
"phone": "18852067999",
"address": "上海市普陀区金沙江路 25 弄",
"tag": "设计二所"
}
]
},
{
"sectorData": [{
"itemid": 10001,
"text": "设计一所",
"value": "设计一所"
}, {
"itemid": 10002,
"text": "设计二所",
"value": "设计二所"
},
{
"itemid": 10003,
"text": "设计三所",
"value": "设计三所"
},
{
"itemid": 10004,
"text": "南京小组",
"value": "南京小组"
}
]
}
]
代码中使用的框架有:
vue链接:vue
elementUI链接:elementUI
axios.js链接:axios.min.js
主要的难点的简单讲解:
(1)在el-table中通过设置 type="expand" 和 Scoped slot
可以开启展开行功能,el-table-column
的模板会被渲染成为展开行的内容,展开行可访问的属性与使用自定义列模板时的 Scoped slot
相同。然后在表格的最后一列设置两个按钮,但是两个按钮要根据 v-if="scope.row.isSet"来进行“编辑和确定”的切换。
(2)在每条数据中设置一个isExpand="false"的属性,默认该行为未展开状态,当展开或关闭该行时,记录下该状态,如下:
/* 更改每一行的张开/关闭状态 */
expandChange(row, expandedRows) {
row.isExpand = !row.isExpand;
},
(3)在点击编辑按钮时,对其他行进行验证,如其他行存在编辑状态,则先保存对其他行的编辑,然后才能对该行进行编辑;点击改变某条数据变为可编辑状态,确认时,对数据进行非空校验。如下:
/* 开启编辑单元格 */
handleEdit(index, row, e) {
for (const i of this.tableData) {
if (!i.isSet) {
return this.$message.warning('请先保存当前编辑项');
}
}
if (!row.isExpand) {
//调用,table的方法,展开/折叠 行
this.$refs.refTable.toggleRowExpansion(row);
}
row.isSet = false;},
/* 保存单元格,进行编辑验证 */
saveEdit(index, row, column) {
// console.log(row);
if ((row.address.replace(/\s+/g, '') == '' || (row.phone.replace(/\s+/g, '') == ''))) {
row.isSet = false;
return this.$message.error('数据不能为空');
} else {
row.isSet = true;
}
//调用,table的方法,展开/折叠 行
this.$refs.refTable.toggleRowExpansion(row);
},
(4)分页展示的核心代码是在获取的数据后,写入到el-table中,如下红色部分:
<el-table ref="refTable" height="500" :data="tableData.slice((currentPage-1)*pagesize,currentPage*pagesize)"
style="width: 100%" @expand-change="expandChange">/* 分页方法 */
handleSizeChange(val) {
// console.log(`每页 ${val} 条`);
this.pagesize = val;
},
handleCurrentChange: function(val) {
// console.log(`当前页: ${val}`);
this.currentPage = val;
},
(5) 此案例中是对姓名中的关键字进行搜索,对字符串使用indexOf属性,对搜索到的数据添加到一个新的数组中,并返回给el-table;在筛选中,是element中的,可去案例中参考。如下:
<el-table-column prop="tag" label="部门" width="140" :filters="sectorData" :filter-method="filterTag">
<template slot-scope="scope">
<el-tag size="medium" disable-transitions>{{scope.row.tag}}</el-tag>
</template>
</el-table-column><template slot="header" slot-scope="scope">
<el-input clearable v-model="search" prefix-icon="el-icon-search" size="mini" placeholder="输入姓名关键字搜索" @input="searchName" />
</template>/* 部门筛选 */
filterTag(value, row, column) {
console.log(value);
// console.log(row);
return row.tag === value;
},/* 姓名关键字搜索 */
searchName(search) {
var newList = [];
// console.log(search);
if (search != '') {
vm.oldtableData.forEach(item => {
if (item.name.indexOf(search) != -1) {
//空字符串包含在所有字符串中,因此控制会返回所有列表
newList.push(item);
}
});
return this.tableData = newList;
} else {
return this.tableData = this.oldtableData;
}
},
(6)下面就是对保存和提交的简单校验,在提交时要确认数据已经保存,保存的数据不可再编辑
/* 提交表单 */
postForm() {
if (!this.btnDisabled) {
return this.$message.error('请先保存');
} else {
this.$confirm('是否提交?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then((action) => {
this.$message({
type: 'success',
message: '提交成功!',});
if (action === 'confirm') {
//提交表单
console.log(this.tableData);
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消提交!'
});
});
}
},
/* 保存表单 */
saveForm() {
for (var i = 0; i < this.tableData.length; i++) {
if (!this.tableData[i].isSet) {
this.$message.warning('请先保存当前编辑项');
return this.btnDisabled = false;
} else {
this.btnDisabled = true;
}
}
},
/* 撤回保存 */
backtrack() {
this.btnDisabled = false;
},
这次vue小实例就写到这,如果内容、代码有不妥的地方,望斧正,谢谢。