参考网上黄龙的表格树进行完善,并添加固定表头等的功能,目前是在iview的项目中实现,如果想在element中实现的话修改对应的元素标签及相关写法即可。

<!--
@events @on-row-click 单击行或者单击操作按钮方法
@on-selection-change 多选模式下 选中项变化时触发
@on-sort-change 排序时有效,当点击排序时触发
@props data 显示的结构化数据
columns 表格列的配置描述 sortable:true 开启排序功能
showHeader 是否显示表头
type: 'selection'为多选功能 type: 'template' 为操作功能 slot为插槽名
-->
<template>
<div ref="table" class='autoTable tree-grid'>
<!-- <div ref="table" :style="{width:treeGridWidth}" class='autoTable tree-grid'> -->
<div ref="header" class="tree-grid-header" v-if="showHeader">
<table class="table table-bordered hl-tree-table">
<colgroup>
<col v-for="(column, index) in cloneColumns" :width="column.width" :align="column.align" :key="index">
<col v-if="showVerticalScrollBar" :width="scrollBarWidth"/>
</colgroup>
<thead>
<tr>
<th v-for="(column,index) in cloneColumns" :key="column.key">
<div v-if="column.type === 'selection'" class="selection-wrapper">
<Checkbox v-model="checks" @click="handleCheckAll"></Checkbox>
<input type="checkbox" v-model="checks" @click="handleCheckAll" class="selection-checkbox">
</div>
<div v-else class="tree-grid-cell">{{renderHeader(column, index)}}
<span class="ivu-table-sort" v-if="column.sortable">
<Icon type="md-arrow-dropup" :class="{on: column._sortType === 'asc'}" @click.native="handleSort(index, 'asc')" />
<Icon type="md-arrow-dropdown" :class="{on: column._sortType === 'desc'}" @click.native="handleSort(index, 'desc')" />
</span>
</div>
</th>
<th v-if="showVerticalScrollBar" rowspan="1"></th>
</tr>
</thead>
</table>
</div>
<div ref="body" class="tree-grid-body" :style="{height:tBodyHeight}">
<table ref="bodyTable" class="table table-bordered hl-tree-table">
<colgroup ref="bodyColgroup">
<col v-for="(column, index) in cloneColumns" :width="column.width" :align="column.align" :key="index">
</colgroup>
<tbody>
<tr v-for="(item,index) in initGridData" :key="item.id" v-show="show(item)" :class="{'child-tr':item.parent}">
<td v-for="(column,snum) in columns" :key="column.key">
<div v-if="column.type === 'selection'" class="selection-wrapper">
<CheckboxGroup v-model="checkGroup">
<Checkbox :label="item.id"><span></span></Checkbox>
</CheckboxGroup>
<input type="checkbox" :value="item.id" v-model="checkGroup" @click="handleCheckClick(item,$event,index)" class="selection-checkbox">
</div>
<div v-if="column.type === 'template'" class="tree-grid-cell">
<slot :name="column.slot" :scope="item"></slot>
</div>
<div @click="toggle(index,item)" v-if="!column.type" class="tree-grid-cell">
<template v-if='snum===iconRow()'>
<i v-html='item.spaceHtml'></i>
<span v-if="item.children&&item.children.length>0" class="tree-grid-arrow" :class="{'tree-grid-arrow-open': item.expanded}" >
<i class="ivu-icon ivu-icon-ios-arrow-forward"></i>
</span>
<i v-else class="ms-tree-space"></i>
</template>{{renderBody(item,column)}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
let cached
export default {
name: 'treeGrid',
props: {
columns: {
type: Array,
default: () => []
},
data: {
type: Array,
default: () => []
},
showHeader: {
type: Boolean,
default: true
},
height: {
type: [Number, String]
}
},
data () {
return {
initGridData: [], // 处理后数据数组
cloneColumns: [], // 处理后的表头数据
showVerticalScrollBar: false,
scrollBarWidth: undefined,
checkGroup: [], // 复选框数组
checks: false, // 全选
screenWidth: document.body.clientWidth, // 自适应宽
headerHeight: 0,
columnsWidth: {},
timer: false, // 控制监听时长
dataLength: 0 // 树形数据长度
}
},
computed: {
treeGridWidth () {
let treeGridWidth = this.$el ? this.$el.offsetWidth : '1000'
return treeGridWidth
},
tBodyHeight () {
return parseFloat(this.height) > 0 ? (parseFloat(this.height) - parseFloat(this.headerHeight)) + 'px' : 'auto'
}
},
watch: {
screenWidth (val) {
if (!this.timer) {
this.screenWidth = val
this.timer = true
setTimeout(() => {
this.timer = false
}, 400)
}
},
data () {
if (this.data) {
this.dataLength = this.Length(this.data)
this.initData(this.deepCopy(this.data), 1, null)
this.checkGroup = this.renderCheck(this.data)
if (this.checkGroup.length === this.dataLength) {
this.checks = true
} else {
this.checks = false
}
this.showScrollBar()
}
},
columns: {
handler () {
this.cloneColumns = this.makeColumns()
},
deep: true
},
checkGroup (data) {
this.checkAllGroupChange(data)
}
},
mounted () {
if (this.data) {
this.dataLength = this.Length(this.data)
this.initData(this.deepCopy(this.data), 1, null)
this.cloneColumns = this.makeColumns()
this.checkGroup = this.renderCheck(this.data)
if (this.checkGroup.length === this.dataLength) {
this.checks = true
} else {
this.checks = false
}
}
// 绑定onresize事件 监听屏幕变化设置宽
this.$nextTick(() => {
this.screenWidth = document.body.clientWidth
this.headerHeight = this.showHeader ? this.$refs.header.clientHeight : 0
this.cloneColumns = this.makeColumns()
this.showScrollBar()
})
window.onresize = () => {
return (() => {
window.screenWidth = document.body.clientWidth
window.screenHeight = document.body.clientHeight
this.screenWidth = window.screenWidth
this.headerHeight = this.showHeader ? this.$refs.header.clientHeight : 0
this.cloneColumns = this.makeColumns()
this.showScrollBar()
})()
}
},
methods: {
showScrollBar () {
this.$nextTick(() => {
// console.error(this.$refs.bodyTable.clientHeight, this.$refs.body.clientHeight)
if (this.$refs.bodyTable.clientHeight > this.$refs.body.clientHeight) {
if (!this.showVerticalScrollBar) {
this.showVerticalScrollBar = true
this.scrollBarWidth = this.getScrollBarSize()
}
} else {
if (this.showVerticalScrollBar) {
this.showVerticalScrollBar = false
}
}
})
},
// 有无多选框折叠位置优化
iconRow () {
for (let i = 0, len = this.columns.length; i < len; i++) {
if (this.columns[i].type === 'selection') {
return 1
}
}
return 0
},
// 排序事件
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)
let tableWidth = this.$el.offsetWidth
let noWidthLength = 0
let nanWidthLength = 0
let widthSum = 0
columns.forEach((column, index) => {
column._index = index
column._sortType = 'normal'
if (column.width) {
if (!/^(-?\d+)(\.\d+)?$/.test(column.width)) {
let width = column.width
if (width.slice(-1) === '%') {
let percent = (column.width).slice(0, -1)
column.width = ''
column._width = ''
nanWidthLength += 1
column._nanWidth = percent
} else {
this.$Message.error('请输入正确的宽度:数字(例如:100)或者百分比(例如:10%)')
}
} else {
widthSum += column.width
column._width = column.width
}
} else {
noWidthLength += 1
column._width = ''
}
column._width = column.width ? column.width : ''
})
if (nanWidthLength > 0) {
columns.forEach((column, index) => {
if (column._nanWidth) {
column.width = parseInt((tableWidth - widthSum) * column._nanWidth / 100)
// column.width = (tableWidth - widthSum) * column._nanWidth / 100
column._width = column.width
widthSum += column.width
}
})
}
if (noWidthLength > 0) {
columns.forEach((column, index) => {
if (column._width === '') {
column.width = parseInt((tableWidth - widthSum) / noWidthLength)
// column.width = (tableWidth - widthSum) / noWidthLength
column._width = column.width
}
})
}
return columns
},
// 数据处理 增加自定义属性监听
initData (data, level, parent) {
this.initGridData = []
let spaceHtml = ''
for (let i = 1; i < level; i++) {
spaceHtml += '<i class="ms-tree-space"></i>'
}
data.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 ? 1 : false)
})
this.initGridData.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 (let 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.initGridData.splice((index + childIndex + 1), 0, child)
// 设置监听属性
this.$set(this.initGridData[index + childIndex + 1], 'parent', item)
this.$set(this.initGridData[index + childIndex + 1], 'level', level)
this.$set(this.initGridData[index + childIndex + 1], 'spaceHtml', spaceHtml)
this.$set(this.initGridData[index + childIndex + 1], 'isShow', true)
this.$set(this.initGridData[index + childIndex + 1], 'expanded', false)
})
}
}
}
this.showScrollBar()
},
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勾选框, 父子不相关联
handleCheckClick (data, event, index) {
data.isChecked = !data.isChecked
if (data.isChecked) {
this.checkGroup.push(data.id)
} else {
for (let i = 0; i < this.checkGroup.length; i++) {
if (this.checkGroup[i] === data.id) {
this.checkGroup.splice(i, 1)
}
}
}
this.checkGroup = this.getArray(this.checkGroup)
let itemsIds = this.getArray(this.checkGroup.concat(this.All(this.data)))
if (this.checkGroup.length === itemsIds.length) {
this.checks = true
} else {
this.checks = false
}
},
// checkbox 全选 选择事件
handleCheckAll () {
this.checks = !this.checks
if (this.checks) {
this.checkGroup = this.getArray(this.checkGroup.concat(this.All(this.data)))
} else {
this.checkGroup = []
}
// this.$emit('on-selection-change', this.checkGroup)
},
// 数组去重
getArray (a) {
let hash = {}
let len = a.length
let result = []
for (let 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) {
let t = this.type(data)
let o
let i
let 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) {
let toString = Object.prototype.toString
let 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)]
},
getScrollBarSize (fresh) {
if (this.$isServer) return 0
if (fresh || cached === undefined) {
const inner = document.createElement('div')
inner.style.width = '100%'
inner.style.height = '200px'
const outer = document.createElement('div')
const outerStyle = outer.style
outerStyle.position = 'absolute'
outerStyle.top = 0
outerStyle.left = 0
outerStyle.pointerEvents = 'none'
outerStyle.visibility = 'hidden'
outerStyle.width = '200px'
outerStyle.height = '150px'
outerStyle.overflow = 'hidden'
outer.appendChild(inner)
document.body.appendChild(outer)
const widthContained = inner.offsetWidth
outer.style.overflow = 'scroll'
let widthScroll = inner.offsetWidth
if (widthContained === widthScroll) {
widthScroll = outer.clientWidth
}
document.body.removeChild(outer)
cached = widthContained - widthScroll
}
return cached
}
},
beforeDestroy () {
window.onresize = null
}
}
</script>
<style lang="less">
.tree-grid {
@keyframes opacityChild{
0% { opacity: 0; }
50% { opacity: .5; }
100% { opacity: 1; }
}
width: 100%;
color: #1f2d3d;
color: #495167;
&.autoTable {
overflow: auto;
}
.tree-grid-body {
overflow: auto;
}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
&.hl-tree-table {
&>tbody{
&>tr {
height: 50px;
background-color: #fff;
border-bottom: 1px solid #e8eaec;
&:hover {
background-color: #ebf7ff;
}
}
&>.child-tr {
background-color: #fff;
}
}
th>label {
display: inline-block;
margin: 0 12px;
}
}
.ivu-icon {
font-size: 18px;
}
.tree-grid-arrow {
cursor: pointer;
width: 14px;
text-align: center;
display: inline-block;
i {
position: relative;
top: -1px;
transition: all .2s ease-in-out;
font-size:14px;
vertical-align:middle;
}
&-open {
i {
transform:rotate(90deg);
}
}
}
}
.table>tbody>tr>td,
.table>tbody>tr>th,
.table>thead>tr>td,
.table>thead>tr>th {
vertical-align: middle;
box-sizing: border-box;
&:last-child {
border-right: 0;
}
}
// .table>tbody>tr>td,
// .table>tbody>tr>th {
// border-right: 1px solid #ccc;
// }
.table>thead>tr>th {
text-align: left;
}
.table-bordered>thead>tr>td,
.table-bordered>thead>tr>th {
height: 32px;
padding: 0;
vertical-align: middle;
background: #E1E4E5;
border-right: 1px solid #fff;
.tree-grid-cell {
padding: 0 12px;
}
}
.tree-grid-cell {
padding: 0 12px;
font-size: 12px;
}
.ms-tree-space {
position: relative;
top: 1px;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1em;
width: 14px;
height: 14px;
}
.ms-tree-space::before {
content: "";
}
.selection-wrapper {
position: relative;
text-align: center;
width: 18px;
height: 18px;
margin: 0 auto;
vertical-align: middle;
.selection-checkbox {
position: absolute;
left: 0;
top: 0;
z-index: 2;
width: 18px;
height: 18px;
vertical-align: middle;
opacity: 0;
cursor: pointer;
}
.ivu-checkbox-wrapper {
position: absolute;
left: 0;
top: 0;
z-index: 1;
margin: 0;
line-height: 15px;
}
}
}
</style>

vue 表格树 固定表头的更多相关文章

  1. vue表格实现固定表头首列

    前言 最近在做vue移动端项目,需要做一个可以固定表头首列的表格,而且由于一些原因不能使用任何UI插件,网上找了很久也没什么好方法,所以在解决了问题之后,写下了这篇文章供后来人参考,文章有什么错漏的问 ...

  2. bootstrap-table固定表头固定列

    1.引入 bootstrap依赖于jquery bootstrap-table依赖于bootstrap,所以都需要引入 2. bootstrap-table有两种方式,html.js <tabl ...

  3. bootstrap 冻结表格,冻结表头

    需要的文件下载: bootstrap-table:https://github.com/wenzhixin/bootstrap-table bootstrap-table-fiex-column:ht ...

  4. 固定表头,单元格td宽度自适应,多内容出现-横向纵向滚动条数据表格的<前世今生>

    固定表头,单元格td宽度自适应,多内容出现-横向纵向滚动条数据表格的<前世今生>     先上图例   & 无论多少数据--都完美! 背景:由于我司行业方向,需要很多数据报表,则t ...

  5. 使用bootstrap table 插件固定表头时 表头与表格内容无法对齐

    在使用bootstrap table开发后台管理系统,表格利用bootstrap-table插件来实现,使用bootstrap-table过程中,会出现表头错位的情况 表头对不齐效果: 解决的方法: ...

  6. jQuery制作多表格固定表头、切换表头的特效

    做了好几天的固定表头特效,总算是搞定了.先说明一下基本功能:我们在一个网页上浏览很多份表格数据的时候,肯定会碰到很多分不清表头,也分不清表 格是哪个的情况,这个时候我们就希望能有一种功能,就是我们再下 ...

  7. Qt实现表格树控件-支持多级表头

    目录 一.概述 二.效果展示 三.实现方式 四.多级表头 1.数据源 2.表格 3.QStyledItemDelegate绘制代理 五.测试代码 六.相关文章 原文链接:Qt实现表格树控件-支持多级表 ...

  8. css 固定表头的表格,和 width:auto, margin:auto等 自计算方法

    实现思路: 外层用一个table,里面写好Header,然后里面再写一个table里面写好header.然后自己控制overflow的值使内部的tablemargin-top和外层的行高一致就可以实现 ...

  9. vue表格之tableHeaderColor(修改表头背景色)

    <el-table :header-cell-style="tableHeaderColor"></el-table> // 更改表头样式 tableHea ...

随机推荐

  1. kubeadm安装k8s集群

    安装kubeadm kubectl kubelet 对于Ubuntu/debian系统,添加阿里云k8s仓库key,非root用户需要加sudo apt-get update && a ...

  2. C++STL标准库学习笔记(三)multiset

    C++STL标准库学习笔记(三)multiset STL中的平衡二叉树数据结构 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标 ...

  3. 共享资源库(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 既然要共享资源库,那就得先建一个可供共享的资源库文件,好吧,说得这么高大上,其实就是一个里面只有资源数据的mpp项目文件, ...

  4. 资源工作表中与资源有关的操作(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 这个内容,我需要专门写一篇吗? 不写吧,好像对不起我那股学习的劲:写吧,实在是--一句话就够了:所有与任务有关的新建.修改 ...

  5. c++模板类的使用,编译的问题

    1,模板类编译的问题 前两天在写代码时,把模板类的声明和分开放在两个文件中了,类似于下面这样: stack.hpp: #ifndef _STACK_HPP #define _STACK_HPP tem ...

  6. 嵌入式实验一:LED灯点亮

    实验一:LED灯程序 一. 实验环境 开发机环境 ​ 操作系统:ubuntu 12.04 ​ 交叉编译环境:arm-linux-gcc 4.3.2 ​ 6410板子内核源码:linux-3.0.1 目 ...

  7. wordpress的.htaccess很容易就被挂马啊

    wordpress的.htaccess很容易就被挂马啊 修改成这样吧: # BEGIN WordPress<IfModule mod_rewrite.c>RewriteEngine OnR ...

  8. 使用WebUploader进行文件图片上传

    官方文档:http://fex.baidu.com/webuploader/getting-started.html 引入Webuploader的css和js文件,下载地址:http://fex.ba ...

  9. 【LeetCode】370. Range Addition 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 只修改区间起终点 日期 题目地址:https://le ...

  10. 【LeetCode】146. LRU Cache 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典+双向链表 日期 题目地址:https://le ...