您的当前位置:首页正文

Vue.js实现可排序的表格组件功能示例

2020-11-27 来源:钮旅网

本文实例讲述了Vue.js实现可排序的表格组件功能。分享给大家供大家参考,具体如下:

我们基于 Vue.js 实现一个可根据某列进行排序的表格组件。

一个表格包含表头和数据两部分内容。因此,我们定义两个数组,columns 表示表头信息,在 <thread> 中渲染,并可在此指定某一列是否需要排序;data 表示数据。

html:

<div id="app" v-cloak>
 <v-table :data="data" :columns="columns"></v-table>
 <button @click="add">新增</button>
</div>

把父组件中定义的 data 与 columns 传入 v-table 组件。

js:

Vue.component('vTable', {
 props: {
 //表头列名称
 columns: {
 type: Array,
 default: function () {
 return [];
 }
 },
 //数据
 data: {
 type: Array,
 default: function () {
 return [];
 }
 }
 },
 //为了不影响原始数据,这里定义了相应的需要操作的数据对象
 data: function () {
 return {
 currentColumns: [],
 currentData: []
 }
 },
 //render 实现方式
 render: function (createElement) {
 var that = this;
 /**
 * 创建列样式与表头
 */
 var ths = [];//<th> 标签数组
 var cols = [];//<cols> 标签数组
 this.currentColumns.forEach(function (col, index) {
 if (col.width) {//创建列样式
 cols.push(createElement('col', {
 style: {
 width: col.width
 }
 }))
 }
 if (col.sortable) {
 ths.push(createElement('th', [
 createElement('span', col.title),
 //升序
 createElement('a', {
 class: {
 on: col.sortType === 'asc'
 },
 on: {
 click: function () {
 that.sortByAsc(index)
 }
 }
 }, '↑'),
 //降序
 createElement('a', {
 class: {
 on: col.sortType === 'desc'
 },
 on: {
 click: function () {
 that.sortByDesc(index);
 }
 }
 }, '↓')
 ]));
 } else {
 ths.push(createElement('th', col.title));
 }
 });
 /**
 * 创建内容
 */
 var trs = [];//<tr> 标签数组
 this.currentData.forEach(function (row) {//遍历行
 var tds = [];//<td> 标签数组
 that.currentColumns.forEach(function (cell) {//遍历单元格
 tds.push(createElement('td', row[cell.key]));
 });
 trs.push(createElement('tr', tds));
 });
 return createElement('table', [
 createElement('colgroup', cols),
 createElement('thead', [
 createElement('tr', ths)
 ]),
 createElement('tbody', trs)
 ])
 },
 methods: {
 //初始化表头
 initColumns: function () {
 this.currentColumns = this.columns.map(function (col, index) {
 //新建字段,标识当前列排序类型;默认为“不排序”
 col.sortType = 'normal';
 //新建字段,标识当前列在数组中的索引
 col.index = index;
 return col;
 });
 },
 //初始化数据
 initData: function () {
 this.currentData = this.data.map(function (row, index) {
 //新建字段,标识当前行在数组中的索引
 row.index = index;
 return row;
 });
 },
 //排序
 order: function (index, type) {
 this.currentColumns.forEach(function (col) {
 col.sortType = 'normal';
 });
 //设置排序类型
 this.currentColumns[index].sortType = type;
 //设置排序函数
 var sortFunction;
 var key = this.currentColumns[index].key;
 switch (type) {
 default://默认为 asc 排序
 case 'asc':
 sortFunction = function (a, b) {
 return a[key] > b[key] ? 1 : -1;
 };
 break;
 case 'desc':
 sortFunction = function (a, b) {
 return a[key] < b[key] ? 1 : -1;
 };
 break;
 }
 this.currentData.sort(sortFunction);
 },
 //升序
 sortByAsc: function (index) {
 this.order(index, 'asc');
 },
 //降序
 sortByDesc: function (index) {
 this.order(index, 'desc');
 }
 },
 watch: {
 data: function () {
 this.initData();
 //找出排序字段
 var sortedColumn = this.currentColumns.filter(function (col) {
 return col.sortType !== 'normal';
 });
 if (sortedColumn.length > 0) {
 if (sortedColumn[0].sortType === 'asc') {
 this.sortByAsc(sortedColumn[0].index);
 } else {
 this.sortByDesc(sortedColumn[0].index);
 }
 }
 }
 },
 mounted() {
 this.initColumns();
 this.initData();
 }
});
var app = new Vue({
 el: '#app',
 data: {
 //title 、key 与 width 必填;sortable 选填
 columns: [
 {
 title: '名称',
 key: 'name',
 width:'60%'
 },
 {
 title: '数量',
 key: 'num',
 width:'20%',
 sortable: true
 },
 {
 title: '单价',
 key: 'unitPrice',
 width:'20%',
 sortable: true
 }
 ],
 data: [
 {
 name: '真果粒牛奶饮品',
 num: 2,
 unitPrice: 59.9
 },
 {
 name: '苏泊尔(SUPOR)电压力锅 ',
 num: 1,
 unitPrice: 378.0
 },
 {
 name: '乐事(Lay\'s)薯片',
 num: 3,
 unitPrice: 63.0
 }
 ]
 },
 methods:{
 add:function () {
 this.data.push( {
 name: '良品铺子 休闲零食大礼包',
 num: 5,
 unitPrice: 59.80
 });
 }
 }
});

为了让排序后的 columns 与 data 不影响原始数据,我们在组件的 data 中定义了相应的当前数据对象。因此在 method 中使用传入的值,初始化这些数据对象,最后在 mounted() 调用这些初始化方法。

columns 中的每一项都是包含 title(列名)、key(对应 data 中的字段名)、width(宽度) 以及 sortable(是否可排序) 的对象。其中,只有 sortable 为可选项,如果设定为 true,则表示该列可点击排序。

map() 会对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组。

排序分为升序与降序,因为只能对某一列进行排序,所以是互斥操作。我们为每一列新增一个 sortType ,用于标识该列的排序类型,初始值为 normal,表示不排序。

因为排序字段可能是任意列,所以我们为每一列新增一个 index,用于标识当前列在数组中的索引。

在 Render 函数中,首先创建列样式与表头,接着创建内容。

Render 函数中的 createElement 可以简写为 h,这样代码会变得更简洁:

render: function (h) {
 var that = this;
 /**
 * 创建列样式与表头
 */
 var ths = [];//<th> 标签数组
 var cols = [];//<cols> 标签数组
 this.currentColumns.forEach(function (col, index) {
 if (col.width) {//创建列样式
 cols.push(h('col', {
 style: {
 width: col.width
 }
 }))
 }
 if (col.sortable) {
 ths.push(h('th', [
 h('span', col.title),
 //升序
 h('a', {
 class: {
 on: col.sortType === 'asc'
 },
 on: {
 click: function () {
 that.sortByAsc(index)
 }
 }
 }, '↑'),
 //降序
 h('a', {
 class: {
 on: col.sortType === 'desc'
 },
 on: {
 click: function () {
 that.sortByDesc(index);
 }
 }
 }, '↓')
 ]));
 } else {
 ths.push(h('th', col.title));
 }
 });
 /**
 * 创建内容
 */
 var trs = [];//<tr> 标签数组
 this.currentData.forEach(function (row) {//遍历行
 var tds = [];//<td> 标签数组
 that.currentColumns.forEach(function (cell) {//遍历单元格
 tds.push(h('td', row[cell.key]));
 });
 trs.push(h('tr', tds));
 });
 return h('table', [
 h('colgroup', cols),
 h('thead', [
 h('tr', ths)
 ]),
 h('tbody', trs)
 ])
}

创建内容时,我们首先遍历所有行,然后在循环内部遍历所有列,得出 <td> 与 <tr> 内容。

创建表头时,对是否排序做了相应的处理,并绑定了相应的点击事件。

点击事件定义在 methods 中,因为升序与降序逻辑大体相同,所以又封装了一层 order() 排序函数。

order() 排序函数内部使用了数组的 sort() 方法。sort() 方法会调用每个数组项的 toString() 方法,然后比较得到的字符串,即使数组中的每一项是数值,比较的也是字符串。这里传入了一个比较函数作为参数。为了兼容所有浏览器,在比较函数中,我们返回的是 1 或者 -1。

排序之前,先把所有列的排序类型都设置为不排序,然后再更新当前列的排序状态。这就会对应到 render 函数里绑定 <a> 标签的 class 中的 on 样式,即当前列排序状态会被高亮显示。

表格被初始化渲染之后,如果 data 发生变化,那么表格组件数据应该也要同步更新。因此,我们在 watch 中做了数据更新以及数据重排操作。

css:

[v-cloak] {
 display: none;
}
table {
 width: 100%;
 margin-bottom: 24px;
 /*合并边框模型*/
 border-collapse: collapse;
 border-spacing: 0;
 /*在空单元格周围绘制边框*/
 empty-cells: show;
 border: 1px solid #e9e9e9;
}
table th {
 font: bold 14px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
 background: #CAE8EA;
 color: #5c6b77;
 /*设置文本粗细*/
 font-weight: 600;
 /*段落中的文本不进行换行*/
 white-space: nowrap;
 border-top: 1px solid #C1DAD7;
}
table td, table th {
 padding: 8px 16px;
 text-align: left;
 border-right: 1px solid #C1DAD7;
 border-bottom: 1px solid #C1DAD7;
}
table th a {
 /*不独占一行的块级元素*/
 display: inline-block;
 margin: 0 4px;
 cursor: pointer;
}
table th a.on {
 color: #3399ff;
}
table th a:hover {
 color: #3399ff;
}

效果:

点击此处查看本文示例代码

PS:感兴趣的朋友还可以使用如下在线工具测试上述代码:

在线HTML/CSS/JavaScript前端代码调试运行工具:
http://tools.jb51.net/code/WebCodeRun

在线HTML/CSS/JavaScript代码运行工具:
http://tools.jb51.net/code/HtmlJsRun

希望本文所述对大家vue.js程序设计有所帮助。

显示全文