这期内容当中小编将会给大家带来有关使用Vue.js怎么实现一个可排序的表格组件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
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; }
上述就是小编为大家分享的使用Vue.js怎么实现一个可排序的表格组件了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。