版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/shaotaiban1097/article/details/91444746
声明:文中代码整体思路来源于 梁灏 编著的 【Vue.JS 实战】一书,学习过程中因觉得该组件效果不错,比较实用,所以记录一份并做了详细的注释以供学习
效果图
代码
- index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>表格内容排序组件</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="style.css" />
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
</head>
<body>
<div id="demo" v-cloak>
<v-table :thead="thead" :tbody="tbody"></v-table>
<button @click="handleAddData">随机添加一条数据</button>
</div>
<script src="table.js"></script>
<script src="index.js"></script>
</body>
</html>
- index.js
var demo = new Vue({
el: '#demo',
data: {
//构造表头
thead: [{
title: 'name',
key: 'name',
},
{
title: 'age',
key: 'age',
sortable: true,
}, {
title: 'birthday',
key: 'birthday',
sortable: true,
}, {
title: 'address',
key: 'address',
}
],
//构造表数据
tbody: [{
name: '张三',
age: 18,
birthday: '1937-10-01',
address: '深圳龙华区'
}, {
name: '李四',
age: 20,
birthday: '1999-05-18',
address: '北京海淀区',
}, {
name: '王五',
age: 23,
birthday: '1996-02-15',
}, ],
},
methods: {
/* 添加按钮触发事件,随机添加一笔数据 */
handleAddData: function () {
/*随机汉字生成*/
eval("var firstname=" + '" \\u' + (Math.round(Math.random() * 20901) + 19968).toString(16) + '"');
eval("var lastname=" + '" \\u' + (Math.round(Math.random() * 20901) + 19968).toString(16) + '"');
var starttime =
this.tbody.push({
/* 构造两个字的随机用户名 */
name: firstname + lastname,
/* 随机数[0, 20]向上取整。新学习的写法,现学现用 */
age: ~~(Math.random() * 20) + 1,
// birthday: '2019-06-10',
/* 随机构造一个日期,因时间戳转换代码太过繁琐,且这里并非重点,只是为了体现排序,所以用字符串构造(还会出现2月30日) */
birthday: (1989 + ~~(Math.random() * 20)) + '-' + (~~(Math.random() * 11) + 1) + '-' + (~~(Math.random() * 29) + 1),
address: '广东河源',
})
}
},
})
- table.js
Vue.component('vTable', {
/* 接受父组件参数 */
props: {
/* 表头:Array 类型,默认为空数组 */
thead: {
type: Array,
default: function () {
return [];
}
},
/* 表身:Array 类型,默认为空数组 */
tbody: {
type: Array,
default: function () {
return [];
}
}
},
/* 组件中的数据 */
data() {
return {
/* 初始化表头数组 */
currentHead: [],
/* 初始化表身数组 */
currentBody: [],
}
},
/* render 函数构造表格 */
render: function (createElement) {
/**
* 定义 _this 临时存储 Vue 实例。
*/
var _this = this;
/* 定义数组。用来存储表头信息,含节点 */
var ths = [];
/* 遍历表头信息 */
this.currentHead.forEach((ele, index) => {
/* 如果当前列允许排序 即 sortable 为 Truely */
if (ele.sortable) {
/**
* 构造节点并push 到 ths 数组
* 节点基本结构如下:
* <th>
* <span>ele.title</span>
* <a class="on" @click="handleSortByAsc">↑</a>
* <a class="on" @click="handleSortByDesc">↓</a>
* </th>
*/
ths.push(createElement('th', [
createElement('span', ele.title),
createElement('a', {
class: {
on: ele._sortType === 'asc'
},
on: {
click: function () {
_this.handleSortByAsc(index);
}
}
}, '↑'),
createElement('a', {
class: {
on: ele._sortType === 'desc'
},
on: {
click: function () {
_this.handleSortByDesc(index);
}
}
}, '↓'),
]));
/* 如果当前列不允许排序操作,即 sortable 为 Falsely */
} else {
/**
* 节点基本结构如下:
* <th>
* <span>ele.title</span>
* </th>
*/
ths.push(createElement('th', ele.title));
}
});
/* 定义数组。用来存储表身信息,含节点 */
var trs = [];
/* 遍历表身信息 */
this.currentBody.forEach((ele, index) => {
/* 循环内定义临时数组,存储当前 tr 的数据 */
var tds = [];
/* 遍历表头,根据 key 值获取当前 tr 对应的 td 的内容,并 push 到 tds 数组*/
this.currentHead.forEach(cell => {
tds.push(createElement('td', ele[cell.key]));
});
/* 将取到的 tr 数据 push 到 tr 数组中 */
trs.push(createElement('tr', tds));
});
/**
* 返回节点。
* 节点基本结构如下:
* <table>
* <thead>
* <tr>
* ...ths数组的内容...
* </tr>
* </thead>
* <tbody>
* ...trs 数组的内容...
* </tbody>
* </table>
*/
return createElement('table', [
createElement('thead', [
createElement('tr', ths)
]),
createElement('tbody', trs)
])
},
methods: {
/* 初始化组件中表头数组,数据源来自父组件 */
makeHead: function () {
this.currentHead = this.thead.map((col, index) => {
/* 默认排序类型为 normal */
col._sortType = 'normal';
col._index = index;
return col;
});
},
/* 初始化组件中表身数组,数据源来自父组件 */
makeBody: function () {
this.currentBody = this.tbody.map((row, index) => {
row._index = index;
return row;
})
},
/* 正序排列 从小到大 */
handleSortByAsc: function (index) {
/* 获取当前点击列的 key 值 */
var key = this.currentHead[index].key;
/* 将表头所有列的排序类型置为默认 normal */
this.currentHead.forEach(col => {
col._sortType = 'normal';
});
/* 修改当前点击列的排序类型为 asc */
this.currentHead[index]._sortType = 'asc';
/* 对表身数组进行正序排列 */
this.currentBody.sort((a, b) => {
return a[key] > b[key] ? 1 : -1;
})
},
/* 倒序排列 从大到小 */
handleSortByDesc: function (index) {
/* 获取当前点击列的 key 值 */
var key = this.currentHead[index].key;
/* 将表头所有列的排序类型置为默认 normal */
this.currentHead.forEach(col => {
col._sortType = 'normal';
});
/* 修改当前点击列的排序类型为 desc */
this.currentHead[index]._sortType = 'desc';
/* 对表身数组进行倒序排列 */
this.currentBody.sort((a, b) => {
return a[key] < b[key] ? 1 : -1;
})
}
},
/* 定义监听器 */
watch: {
/* 监听表身数据。当表身数据发生变化时,触发该事件 */
tbody: function () {
/* 初始化表身 */
this.makeBody();
/* 过滤排序类型不为 normal 的表头,即获取指定了排序类型的字段 */
var sortedColumn = this.currentHead.filter(col => {
return col._sortType !== 'normal';
});
/* 如果指定了排序类型 */
if (sortedColumn.length > 0) {
if (sortedColumn[0]._sortType === 'asc') {
/* 正序排列 */
this.handleSortByAsc(sortedColumn[0]._index);
} else {
/* 倒序排列 */
this.handleSortByDesc(sortedColumn[0]._index);
}
}
}
},
/* 钩子函数。当Vue实例挂载后执行 */
mounted() {
/* 初始化表头 */
this.makeHead();
/* 初始化表身 */
this.makeBody();
},
})
- style.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 {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
white-space: nowrap;
}
table th,
table td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
table th a {
display: inline-block;
margin: 0 4px;
cursor: pointer;
}
table th a.on {
color: #3399FF;
}
table th a:hover {
color: #3399FF;
}