vue+iview 树型表格
·
作者:https://javascript.ctolib.com/article/wiki/55707
gitHub:https://github.com/huanglong6828/vue-tree-grid
1,创建treeGrid.vue(将文件放入components中),
其中树型表格的父元素图标不显示,我改了一下(改为ios-arrow-forward和ios-arrow-down),并添加了样式
<!--
* @Author: 黄龙
* @pageName: 'tree-grid 树型表格'
* @Date: 2017-07-17 16:48:44
* @Last Modified by: 黄龙
* @Last Modified time: 2017-07-17 16:48:44
* @events @on-row-click 单击行或者单击操作按钮方法
@on-selection-change 多选模式下 选中项变化时触发
@on-sort-change 排序时有效,当点击排序时触发
@props items 显示的结构化数据
columns 表格列的配置描述 sortable:true 开启排序功能
type: 'selection'为多选功能 type: 'action' 为操作功能 actions:[{}] 操作按钮
-->
<template>
<div :style="{width:tableWidth}" class='autoTbale'>
<table class="table table-bordered" id='hl-tree-table'>
<thead>
<tr>
<th v-for="(column,index) in cloneColumns">
<label v-if="column.type === 'selection'">
<input type="checkbox" v-model="checks" @click="handleCheckAll">
</label>
<label v-else>
{{ renderHeader(column, index) }}
<span class="ivu-table-sort" v-if="column.sortable">
<Icon type="arrow-up-b" :class="{on: column._sortType === 'asc'}" @click.native="handleSort(index, 'asc')" />
<Icon type="arrow-down-b" :class="{on: column._sortType === 'desc'}" @click.native="handleSort(index, 'desc')" />
</span>
</label>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in initItems" :key="item.id" v-show="show(item)" :class="{'child-tr':item.parent}">
<td v-for="(column,snum) in columns" :key="column.key" :style=tdStyle(column)>
<label v-if="column.type === 'selection'">
<input type="checkbox" :value="item.id" v-model="checkGroup" @click="handleCheckClick(item,$event,index)">
</label>
<div v-if="column.type === 'action'">
<i-button :type="action.type" size="small" @click="RowClick(item,$event,index,action.text)" v-for='action in (column.actions)' :key="action.text">{{action.text}}</i-button>
</div>
<label @click="toggle(index,item)" v-if="!column.type">
<span v-if='snum==iconRow()'>
<i v-html='item.spaceHtml'></i>
<i v-if="item.children&&item.children.length>0" class="ivu-icon" :class="{'ios-arrow-forward':!item.expanded,'ios-arrow-down':item.expanded }"></i>
<i v-else class="ms-tree-space"></i>
</span> {{renderBody(item,column) }}
</label>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'treeGrid',
props: {
columns: Array,
items: {
type: Array,
default: function() {
return [];
}
}
},
data() {
return {
initItems: [], //处理后数据数组
cloneColumns: [], //处理后的表头数据
checkGroup: [], //复选框数组
checks: false, //全选
screenWidth: document.body.clientWidth, //自适应宽
tdsWidth: 0, //td总宽
timer: false, //控制监听时长
dataLength: 0, //树形数据长度
}
},
computed: {
tableWidth() {
return this.tdsWidth > this.screenWidth && this.screenWidth > 0 ? this.screenWidth + 'px' : '100%'
}
},
watch: {
screenWidth(val) {
if (!this.timer) {
this.screenWidth = val
this.timer = true
setTimeout(() => {
this.timer = false
}, 400)
}
},
items() {
if (this.items) {
this.dataLength = this.Length(this.items)
this.initData(this.deepCopy(this.items), 1, null);
this.checkGroup = this.renderCheck(this.items)
if (this.checkGroup.length == this.dataLength) {
this.checks = true
} else {
this.checks = false
}
}
},
columns: {
handler() {
this.cloneColumns = this.makeColumns();
},
deep: true
},
checkGroup(data) {
this.checkAllGroupChange(data)
},
},
mounted() {
if (this.items) {
this.dataLength = this.Length(this.items)
this.initData(this.deepCopy(this.items), 1, null);
this.cloneColumns = this.makeColumns();
this.checkGroup = this.renderCheck(this.items)
if (this.checkGroup.length == this.dataLength) {
this.checks = true
} else {
this.checks = false
}
}
// 绑定onresize事件 监听屏幕变化设置宽
this.$nextTick(() => {
this.screenWidth = document.body.clientWidth
})
window.onresize = () => {
return (() => {
window.screenWidth = document.body.clientWidth
this.screenWidth = window.screenWidth
})()
}
},
methods: {
// 有无多选框折叠位置优化
iconRow() {
for (var i = 0, len = this.columns.length; i < len; i++) {
if (this.columns[i].type == 'selection') {
return 1
}
}
return 0
},
// 设置td宽度,td的align
tdStyle(column) {
var style = {}
if (column.align) {
style["text-align"] = column.align;
}
if (column.width) {
style["min-width"] = column.width + 'px';
}
return style;
},
// 排序事件
handleSort(index, type) {
this.cloneColumns.forEach((col) => col._sortType = 'normal');
if (this.cloneColumns[index]._sortType === type) {
this.cloneColumns[index]._sortType = 'normal'
} else {
this.cloneColumns[index]._sortType = type
}
this.$emit('on-sort-change', this.cloneColumns[index]['key'], this.cloneColumns[index]['_sortType'])
},
// 点击某一行事件
RowClick(data, event, index, text) {
let result = this.makeData(data)
this.$emit('on-row-click', result, event, index, text)
},
// 点击事件 返回数据处理
makeData(data) {
const t = this.type(data);
let o;
if (t === 'array') {
o = [];
} else if (t === 'object') {
o = {};
} else {
return data;
}
if (t === 'array') {
for (let i = 0; i < data.length; i++) {
o.push(this.makeData(data[i]));
}
} else if (t === 'object') {
for (let i in data) {
if (i != 'spaceHtml' && i != 'parent' && i != 'level' && i != 'expanded' && i != 'isShow' && i !=
'load') {
o[i] = this.makeData(data[i]);
}
}
}
return o;
},
// 处理表头数据
makeColumns() {
let columns = this.deepCopy(this.columns);
this.tdsWidth = 0
columns.forEach((column, index) => {
column._index = index;
column._width = column.width ? column.width : '';
column._sortType = 'normal';
this.tdsWidth += column.width ? parseFloat(column.width) : 0;
});
return columns;
},
// 数据处理 增加自定义属性监听
initData(items, level, parent) {
this.initItems = []
let spaceHtml = "";
for (var i = 1; i < level; i++) {
spaceHtml += "<i class='ms-tree-space'></i>"
}
items.forEach((item, index) => {
item = Object.assign({}, item, {
"parent": parent,
"level": level,
"spaceHtml": spaceHtml
});
if ((typeof item.expanded) == "undefined") {
item = Object.assign({}, item, {
"expanded": false
});
}
if ((typeof item.show) == "undefined") {
item = Object.assign({}, item, {
"isShow": false
});
}
if ((typeof item.isChecked) == "undefined") {
item = Object.assign({}, item, {
"isChecked": false
});
}
item = Object.assign({}, item, {
"load": (item.expanded ? true : false)
});
this.initItems.push(item);
if (item.children && item.expanded) {
this.initData(item.children, level + 1, item);
}
})
},
// 隐藏显示
show(item) {
return ((item.level == 1) || (item.parent && item.parent.expanded && item.isShow));
},
toggle(index, item) {
let level = item.level + 1;
let spaceHtml = "";
for (var i = 1; i < level; i++) {
spaceHtml += "<i class='ms-tree-space'></i>"
}
if (item.children) {
if (item.expanded) {
item.expanded = !item.expanded;
this.close(index, item);
} else {
item.expanded = !item.expanded;
if (item.load) {
this.open(index, item);
} else {
item.load = true;
item.children.forEach((child, childIndex) => {
this.initItems.splice((index + childIndex + 1), 0, child);
//设置监听属性
this.$set(this.initItems[index + childIndex + 1], 'parent', item);
this.$set(this.initItems[index + childIndex + 1], 'level', level);
this.$set(this.initItems[index + childIndex + 1], 'spaceHtml', spaceHtml);
this.$set(this.initItems[index + childIndex + 1], 'isShow', true);
this.$set(this.initItems[index + childIndex + 1], 'expanded', false);
})
}
}
}
},
open(index, item) {
if (item.children) {
item.children.forEach((child, childIndex) => {
child.isShow = true;
if (child.children && child.expanded) {
this.open(index + childIndex + 1, child);
}
})
}
},
close(index, item) {
if (item.children) {
item.children.forEach((child, childIndex) => {
child.isShow = false;
child.expanded = false;
if (child.children) {
this.close(index + childIndex + 1, child);
}
})
}
},
//点击check勾选框,判断是否有children节点 如果有就一并勾选
handleCheckClick(data, event, index){
data.isChecked = !data.isChecked;
var arr = data.children;
if(arr){
if(data.isChecked){
this.checkGroup.push(data.id);
for (let i=0; i<arr.length; i++){
this.checkGroup.push(arr[i].id)
}
}else {
for (var i=0; i<this.checkGroup.length; i++){
if(this.checkGroup[i] == data.id){
this.checkGroup.splice(i,1)
}
for (var j=0; j<arr.length; j++){
if(this.checkGroup[i] == arr[j].id){
this.checkGroup.splice(i,1);
}
}
}
}
}
},
//checkbox 全选 选择事件
handleCheckAll() {
this.checks = !this.checks;
if (this.checks) {
this.checkGroup = this.getArray(this.checkGroup.concat(this.All(this.items)))
} else {
this.checkGroup = []
}
// this.$emit('on-selection-change', this.checkGroup)
},
// 数组去重
getArray(a) {
var hash = {},
len = a.length,
result = [];
for (var i = 0; i < len; i++) {
if (!hash[a[i]]) {
hash[a[i]] = true;
result.push(a[i]);
}
}
return result;
},
checkAllGroupChange(data) {
if (this.dataLength > 0 && data.length === this.dataLength) {
this.checks = true;
} else {
this.checks = false;
}
this.$emit('on-selection-change', this.checkGroup)
},
All(data) {
let arr = []
data.forEach((item) => {
arr.push(item.id)
if (item.children && item.children.length > 0) {
arr = arr.concat(this.All(item.children));
}
})
return arr
},
// 返回树形数据长度
Length(data) {
let length = data.length
data.forEach((child) => {
if (child.children) {
length += this.Length(child.children)
}
})
return length;
},
// 返回表头
renderHeader(column, $index) {
if ('renderHeader' in this.columns[$index]) {
return this.columns[$index].renderHeader(column, $index);
} else {
return column.title || '#';
}
},
// 返回内容
renderBody(row, column, index) {
return row[column.key]
},
// 默认选中
renderCheck(data) {
let arr = []
data.forEach((item) => {
if (item._checked) {
arr.push(item.id)
}
if (item.children && item.children.length > 0) {
arr = arr.concat(this.renderCheck(item.children));
}
})
return arr
},
// 深度拷贝函数
deepCopy(data) {
var t = this.type(data),
o, i, ni;
if (t === 'array') {
o = [];
} else if (t === 'object') {
o = {};
} else {
return data;
}
if (t === 'array') {
for (i = 0, ni = data.length; i < ni; i++) {
o.push(this.deepCopy(data[i]));
}
return o;
} else if (t === 'object') {
for (i in data) {
o[i] = this.deepCopy(data[i]);
}
return o;
}
},
type(obj) {
var toString = Object.prototype.toString;
var map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
};
return map[toString.call(obj)];
}
},
beforeDestroy() {
window.onresize = null
}
}
</script>
<style>
.autoTbale {
overflow: auto;
}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
}
.table-bordered {
font-size: 13px;
border: 1px solid #EBEBEB;
}
.table>tbody>tr>td,
.table>tbody>tr>th,
.table>thead>tr>td,
.table>thead>tr>th {
border-top: 1px solid #e7eaec;
line-height: 1.42857;
padding: 8px;
vertical-align: middle;
}
.table-bordered>tbody>tr>td,
.table-bordered>tbody>tr>th,
.table-bordered>tfoot>tr>td,
.table-bordered>tfoot>tr>th,
.table-bordered>thead>tr>td,
.table-bordered>thead>tr>th {
border: 1px solid #e7e7e7;
}
.table>thead>tr>th {
border-bottom: 1px solid #DDD;
}
.table-bordered>thead>tr>td,
.table-bordered>thead>tr>th {
background-color: #F5F5F6;
}
#hl-tree-table>tbody>tr {
background-color: #fbfbfb;
}
#hl-tree-table>tbody>.child-tr {
background-color: #fff;
}
label {
margin: 0 8px;
}
.ms-tree-space {
position: relative;
top: 1px;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1;
width: 14px;
height: 14px;
}
.ms-tree-space::before {
content: ""
}
#hl-tree-table th>label {
margin: 0;
}
.ios-arrow-forward:before {
content: "\F11F";
}
.ios-arrow-down:before {
content: "\F116";
}
</style>
2,在相应页面添加组件,点击事件若用不到,直接删除即可
<template>
<tree-grid
:items='data'
:columns='columns'
@on-row-click='rowClick'
@on-selection-change='selectionClick'
@on-sort-change='sortClick'
></tree-grid>
</template>
<script>
import TreeGrid from './components/TreeGrid'
export default {
data() {
return {
columns: [{
type: 'selection',
width: '50',
}, {
title: '编码',
key: 'code',
sortable: true,
width: '150',
}, {
title: '名称',
key: 'name',
width: '150',
}, {
title: '状态',
key: 'status',
width: '150',
}, {
title: '备注',
key: 'remark',
width: '150',
}, {
title: '操作',
type: 'action',
actions: [{
type: 'primary',
text: '编辑'
}, {
type: 'error',
text: '删除'
}],
width: '150',
}],
data: [{
id: '1',
code: '0001',
name: '测试数据1',
status: '启用',
remark: '测试数据测试数据',
_checked: true
}, {
id: '2',
code: '0002',
name: '测试数据2',
status: '启用',
remark: '测试数据测试数据',
children: [{
id: '01',
code: '00001',
name: '测试数据01',
status: '启用',
remark: '测试数据测试数据',
}, {
id: '02',
code: '00002',
name: '测试数据02',
status: '启用',
remark: '测试数据测试数据',
}]
}, {
id: '3',
code: '0003',
name: '测试数据3',
status: '启用',
remark: '测试数据测试数据'
}, {
id: '4',
code: '0004',
name: '测试数据4',
status: '启用',
remark: '测试数据测试数据'
}]
}
},
components: {
TreeGrid
},
methods: {
rowClick(data, index, event) {
console.log('当前行数据:' + data)
console.log('点击行号:' + index)
console.log('点击事件:' + event)
},
selectionClick(arr) {
console.log('选中数据id数组:' + arr)
},
sortClick(key, type) {
console.log('排序字段:' + key)
console.log('排序规则:' + type)
}
}
}
</script>
完成,仅作备录,细节可点击链接查看。
效果图如下,项目需求,所以表格的某些功能(例如选择框)没用到的就删了没有展示,表格的宽度和数据也改了,就表示一下大体意思:
更多推荐
已为社区贡献4条内容
所有评论(0)