我们在项目中经常会用到对于表格的增删查改操作,以下使用vue2+elementui来实现表格的增删查改
表格的基本属性
基础表格如下:(其中需要注意的是当el-table元素中注入data对象数组后,在el-table-column中用prop属性来对应对象中的键名即可填入数据,用label属性来定义表格的列名。可以使用width属性来定义列宽。)
<template>
<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>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
}
}
</script>
使用带斑马纹的表格,可以更容易区分出不同行的数据。
stripe属性可以创建带斑马纹的表格。它接受一个Boolean,默认为false,设置为true即为启用。
带边框表格:
默认情况下,Table 组件是不具有竖直方向的边框的,如果需要,可以使用border属性,它接受一个Boolean,设置为true即可启用。
带状态表格
可以通过指定 Table 组件的 row-class-name 属性来为 Table 中的某一行添加 class,表明该行处于某种状态。
<template>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName">
<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>
</template>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
export default {
methods: {
tableRowClassName({row, rowIndex}) {
if (rowIndex === 1) {
return 'warning-row';
} else if (rowIndex === 3) {
return 'success-row';
}
return '';
}
},
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄',
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄',
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}]
}
}
}
</script>
以上代码的效果是
具体的逻辑如下:(通过指定 Table 组件的 row-class-name 属性来为 Table 中的某一行添加 class,表明该行处于某种状态。下面这个函数传入的是一个对象,row表示的是每一行的数据信息,rowIndex表示的是索引下标。一下表示行标为1 的显示warning-row的style效果,行标为3的显示成功的效果)
tableRowClassName({row, rowIndex}) {
if (rowIndex === 1) {
return 'warning-row';
} else if (rowIndex === 3) {
return 'success-row';
}
return '';
}
我们可以在这里改需求,比如说如果想要奇数偶数页不同,我们可以改变函数
tableRowClassName ({ row, rowIndex }) {
if (rowIndex%2=== 0) {
return 'warning-row';
} else {
return 'success-row';
}
return '';
}
再比如修改日期为具体某一天的颜色
// 日期是五月一号的改颜色
tableRowClassName ({ row, rowIndex }) {
if (row.date=== '2016-05-01') {
return 'warning-row';
} else {
return 'success-row';
}
return '';
}
固定表头
只要在el-table元素中定义了height属性,即可实现固定表头的表格,而不需要额外的代码。
固定列
固定列需要使用fixed属性,它接受 Boolean 值或者leftright,表示左边固定还是右边固定。
固定列和表头可以同时使用,只需要将上述两个属性分别设置好即可。
排序
在列中设置sortable属性即可实现以该列为基准的排序,接受一个Boolean,默认为false。
筛选
在列中设置filters,filter-method属性即可开启该列的筛选,filters 是一个数组,filter-method是一个方法,它用于决定某些数据是否显示,会传入三个参数:value, row 和 column。
<template>
<el-table
:data="tableData"
border
:row-class-name="tableRowClassName"
style="width: 100%">
<!-- 其中这个prop一定要和下面tableData中的键保持一致 -->
<el-table-column
prop="date"
label="日期"
sortable
width="180"
:filters="[{ text: '2016-05-01', value: '2016-05-01' }, { text: '2016-05-02', value: '2016-05-02' }, { text: '2016-05-03', value: '2016-05-03' }, { text: '2016-05-04', value: '2016-05-04' }]"
:filter-method="filterHandler">
</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-column
prop="tag"
label="标签"
:filters="tagFilter"
:filter-method="filterHandler"
filter-placement="bottom-end">
<template slot-scope="scope">
//对表格里面的数据进行处理的话需要用到template,对于这个数据我们是不方便自定义的,如果要对数据进行更多的自定义,我们就可以使用template;scope相当于是一整个数据对象
//下面的意思是拿到的数据等于家,就显示primary这种样式,否则后面一种样式
<el-tag
:type="scope.row.tag === '家' ? 'primary' : 'success'"
disable-transitions>{{ scope.row.tag }}</el-tag>
</template>
</el-table-column>
</el-table>
</template>
<script>
export default {
name: 'Navbar',
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄',
tag: '家'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄',
tag: '公司'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄',
tag: '家'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄',
tag: '公司'
}]
};
},
computed: {
// filter中的数据是静态的,但是大多数项目中都是动态的,所以上面的filter需要进行变化
tagFilter () {
// 此时数据重复,需要进行降重处理
// return this.tableData.map(v=>({ text: v.tag, value: v.tag }))
let map = new Map();
for (let item of this.tableData) {
if (!map.has(item.tag)) {
map.set(item.tag,item)
}
};
// 但是当前map是一个二维数组,需要进行转换
let data = [...map.values()]
return data.map(v=>({ text: v.tag, value: v.tag }))
}
},
methods: {
// value代表的是当前的数据,row代表的是整个对象,column表示的是表格的特性(可以拿到某个属性的名字)
filterHandler (value, row, column) {
const property = column['property'];
// 这个的意思就是从表格里面筛选出来和value相同的值
return row[property] === value;
// row[property]:就比如说我拿到tableData中的date ,即tableData[date]
}
},
};
</script>
动态数据的增删查改
我们使用json-server模拟请求以及请求回来的过程。详情请看json-server的入门
- 拿到数据展示到页面
<template>
<el-table
:data="tableData"
border
style="width: 100%">
<!-- 其中这个prop一定要和下面tableData中的键保持一致 -->
<el-table-column
prop="title"
label="文章标题"
width="180"
>
</el-table-column>
<el-table-column
prop="user"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="date"
label="日期"
sortable
width="180"
>
</el-table-column>
<el-table-column
prop="id"
label="ID">
</el-table-column>
<el-table-column
prop="check"
label="状态">
</el-table-column>
</el-table>
</template>
<script>
import axios from 'axios';
export default {
name: 'Navbar',
data() {
return {
//动态数据需要对应传过来的键名
tableData: []
};
},
methods: {
},
created () {
axios.get('http://localhost:3009/userList').then(res => {
// this.tableData = res;
console.log(res.data);
this.tableData = res.data;
})
}
};
</script>
<style>
</style>
我们会发现没有状态的值,就算展示也不能直接展示布尔值,在这里我们可以用到标签
<el-table-column
prop="check"
label="状态">
<template slot-scope="scope">
<el-tag
:type="scope.row.check === 'false' ? 'primary' : 'success'"
disable-transitions>{{ scope.row.check }}</el-tag>
</template>
</el-table-column>
接着要进行文本格式化,转化为用户能够识别的文字。这个文本格式化要借助过滤器,然后通过管道的思想把它加添加进去
filters: {
checkFilter (v) {
switch (v) {
case true:
return '已发表'
case false:
return '未发表'
default:
return '未发表'
}
}
},
<el-tag
:type="scope.row.check ? 'success':'primary' "
disable-transitions>{{ scope.row.check| checkFilter }}</el-tag>
最后的效果:
增加操作项(编辑和删除)
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
我们将添加项放到最顶上
<el-button
type="success"
round
size="mini"
@click="handleSet('add',null)"
>增加</el-button>
但是这个时候出现了一个问题,vscode代码行会有很多红色的波浪行(原因是代码和vetur插件的格式不对应所以导致出现红色波浪线)解决办法有:1.在template下用一个div标签包裹住我们所写的代码,或者用el-card包裹住也可以。 2.设置vetur,将以下三个选项的勾选全部取消掉
但是,事实证明虽然没有红色波浪线了,但是运行的时候仍然会报错。所以最好还是采用第一种方法解决。
添加和编辑
首先可以先捋顺一下添加和编辑的思路,添加的话,弹出的对话框中的数据都是空的,而编辑的话里面则是都有之前的数据的
所以我们在添加点击事件的时候
<!-- 添加 -->
<el-button
type="success"
round
size="mini"
@click="handleSet('add',null)"
>增加</el-button>
<!-- 编辑 -->
<el-button
size="mini"
@click="handleSet('update',row)"
>编辑</el-button>
添加弹框(思路是:如果先通过三元运算符来判断是添加还是编辑,如果是添加的话就需要弹出来的框里面没有内容,如果是编辑的话就显示之前的内容;visible.sync这个是用来判断这个对话框是否弹出,默认不弹出false,触发了点击事件以后就将其变为true。其中还form表示的是整个弹框对象,当然里面有哪些内容还是要取决于tableData,比如table里面有标题title,姓名name,日期date,id,check状态等这些变量,那么我们的form里面就要在这个变量范围内,本次用的是form.user,form.title,form.date,form.check)
<el-dialog
:title="state=='add'?'添加':'编辑'"
:visible.sync="dialogTableVisible"
>
<!-- 其中下面这个form代表的是整个弹框对象 -->
<el-form :model="form">
<el-form-item label="发布作者">
<el-input v-model="form.user"></el-input>
</el-form-item>
<el-form-item label="文章标题">
<el-input v-model="form.title"></el-input>
</el-form-item>
<el-form-item label="日期">
<el-date-picker
v-model="form.date"
type="date"
placeholder="选择日期"
>
</el-date-picker>
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="form.check"
active-color="#13ce66"
inactive-color="#ff4949"
>
</el-switch>
</el-form-item>
</el-form>
<div
slot="footer"
class="dialog-footer"
>
<el-button @click="dialogTableVisible = false">取 消</el-button>
<el-button
type="primary"
@click="dialogTableVisible = false"
>确 定</el-button>
</div>
</el-dialog>
其中点击事件中,我们先要看是哪种状态,我们要把是add还是update传入到我们的state中,来判断到底是要添加还是编辑,接着将弹窗状态变为true
methods: {
handleSet (type, row) { //设置数据
this.state = type;
this.dialogTableVisible = true; //弹框的状态
}
},
结果为:点击添加,添加的弹窗如下;点击编辑,编辑的弹窗也如下(我们可以看到编辑的弹窗应该要有之前原本的内容,但是现在还没有加进来)
所以我们要在点击事件中,要判断类型,如果是添加则form的值为空,如果是编辑则form的值为row,见下面代码的最后一行
handleSet (type, row) { //设置数据
this.state = type;
this.dialogTableVisible = true; //弹框的状态
type == 'add' ? this.form = {} : this.form = row;
}
此时我们再试一次:
另外我们还可以看一下状态,此时未发表的状态是红色按钮,已发表的是绿色按钮。
但是此时还有一个bug(当我们修改了编辑中的日期,我们会发现表格中的日期会变成undefined,为什么会这样呢?原因在于这一块涉及到了深浅拷贝,简单来讲就是B复制了A,修改A时,如果B发生了变化就是浅拷贝,如果没有变化就是深拷贝)
解决办法:加扩展运算符扩展运算符是深拷贝还是浅拷贝?
handleSet (type, row) { //设置数据
this.state = type;
this.dialogTableVisible = true; //弹框的状态
type == 'add' ? this.form = {} : this.form = { ...row };
}
然后我们可以进行添加或者编辑操作了,但是现在我们的添加和编辑是同一个,那么到底是放到添加还是编辑里面呢?接下来就聊一聊弹框中数据的处理,这就是弹框中“确定”的处理。针对编辑和添加共用一个弹框的处理。
为“确定”按钮添加点击事件
<el-button
type="primary"
@click="updateData()"
>确 定</el-button>
我们可以通过接口文档,数据的添加是post请求,数据的编辑是put请求,
async updateData () { //提交(可能是编辑的,也有可能是添加的)
let method = this.state == 'add' ? 'post' : 'put'; //判断状态,如果是添加就是post,否则就是put
let res = await axios({ //在里面要发送请求,拿到具体的数据,里面包含以下几种
method: method, //请求的类型,如果是添加就是post,编辑就是put
url: 'data/${this.state}', //后面的值不确定是add还是update,所以可以通过${this.state}来获取
data: this.form //具体的数据对象
})
}
this.userList(); //写完以后再次调用数据的查询
我们会发现上面新增的时间出现问题,所以我们需要进行时间的转换,在过滤器filters中写
dateFilter (v) {
let d = new Date(v);
let timer = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + (d.getDate());
return timer;
}
然后在日期上面添加一个template
<el-table-column
prop="date"
label="日期"
sortable
width="180"
>
<template slot-scope={row}>
{{ row.date |dateFilter}}
</template>
</el-table-column>
结果如下: