一、安裝組件
npm i element-ui -S
二、創(chuàng)建組件
- 創(chuàng)建目錄treeTable
- 創(chuàng)建數(shù)組轉(zhuǎn)換文件
'use strict'
import Vue from 'vue'
export default function treeToArray (data, expandAll, parent = null, level = null) {
let tmp = []
Array.from(data).forEach(function (record) {
if (record._expanded === undefined) {
Vue.set(record, '_expanded', expandAll)
}
let _level = 1
if (level !== undefined && level !== null) {
_level = level + 1
}
Vue.set(record, '_level', _level)
// 如果有父元素
if (parent) {
Vue.set(record, 'parent', parent)
}
tmp.push(record)
if (record.children && record.children.length > 0) {
const children = treeToArray(record.children, expandAll, record, _level)
tmp = tmp.concat(children)
}
})
return tmp
}
- 創(chuàng)建組件
在目錄中創(chuàng)建組件:index.vue
<template>
<div>
<el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
<el-table-column v-if="columns.length===0" width="150">
<template slot-scope="scope">
<span v-for="space in scope.row._level" class="ms-tree-space" :key="space"></span>
<span
class="tree-ctrl"
v-if="iconShow(0,scope.row)"
@click="toggleExpanded(scope.$index)"
>
<i v-if="!scope.row._expanded" class="el-icon-plus"></i>
<i v-else class="el-icon-minus"></i>
</span>
{{scope.$index}}
</template>
</el-table-column>
<el-table-column
v-else
v-for="(column, index) in columns"
:key="column.value"
:label="column.text"
:width="column.width"
>
<template slot-scope="scope">
<span
v-if="index === 0"
v-for="space in scope.row._level"
class="ms-tree-space"
:key="space"
></span>
<span
class="tree-ctrl"
v-if="iconShow(index,scope.row)"
@click="toggleExpanded(scope.$index)"
>
<i v-if="!scope.row._expanded" class="el-icon-plus"></i>
<i v-else class="el-icon-minus"></i>
</span>
<el-checkbox-group
v-if="Array.isArray(scope.row[column.value])"
v-model="scope.row.selectchecked"
@change="handleCheckedCitiesChange(scope.$index, scope.row,scope.row[column.option])"
>
<el-checkbox
v-for="(interset) in scope.row[column.value]"
:label="interset.id"
:key="interset.id"
>{{interset.description}}</el-checkbox>
</el-checkbox-group>
<el-checkbox
v-else-if="scope.row.type===1"
:indeterminate="scope.row.isIndeterminate"
v-model="scope.row.checkAll"
@change="handleCheckAllChange(scope.$index, scope.row,scope.row[column.option])"
>{{scope.row[column.value]}}</el-checkbox>
<span v-else>{{scope.row[column.value]}}</span>
<el-checkbox
v-if="scope.row[column.act]"
:indeterminate="scope.row.isIndeterminate"
v-model="scope.row.checkAll"
@change="handleCheckAllChange1(scope.$index, scope.row,column.option)"
>{{scope.row[column.act]}}</el-checkbox>
</template>
</el-table-column>
<slot></slot>
</el-table>
<footer>
<el-button @click="getAuth">確定</el-button>
</footer>
</div>
</template>
<script>
/**
Auth: Lei.j1ang
Created: 2018/1/19-13:59
*/
import treeToArray from "./eval";
export default {
name: "treeTable",
props: {
data: {
type: [Array, Object],
required: true
},
columns: {
type: Array,
default: () => []
},
evalFunc: Function,
evalArgs: Array,
expandAll: {
type: Boolean,
// 默認(rèn)展開
default: true
}
},
computed: {
// 格式化數(shù)據(jù)源
formatData: function() {
let tmp;
if (!Array.isArray(this.data)) {
tmp = [this.data];
} else {
tmp = this.data;
}
const func = this.evalFunc || treeToArray;
const args = this.evalArgs
? Array.concat([tmp, this.expandAll], this.evalArgs)
: [tmp, this.expandAll];
return func.apply(null, args);
}
},
created() {
this.defaultSelcet();
},
updated() {
// 需要在vue的updated周期函數(shù)中調(diào)用methods里的方法 否則methods里面獲取不到頁面元素
this.expandAllClick();
},
methods: {
expandAllClick() {
// 獲取到頁面元素 模擬點擊可實現(xiàn)讓樹形表格展開
var els = document.getElementsByClassName("el-icon-minus"); // 獲取點擊的箭頭元素
console.log(els);
for (let i = 0; i < els.length; i++) {
els[i].click();
}
},
showRow: function(row) {
const show = row.row.parent
? row.row.parent._expanded && row.row.parent._show
: true;
row.row._show = show;
return show
? "animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;"
: "display:none;";
},
// 切換下級是否展開
toggleExpanded: function(trIndex) {
const record = this.formatData[trIndex];
record._expanded = !record._expanded;
},
// 圖標(biāo)顯示
iconShow(index, record) {
return index === 0 && record.children && record.children.length > 0;
},
handleCheckAllChange(index, row, opt) {
this.cc();
if (row.selectchecked.length && row.selectchecked.length !== opt.length) {
let arr = [];
opt.forEach(element => {
arr.push(element.id);
});
row.selectchecked = arr;
row.checkAll = true;
row.isIndeterminate = false;
} else if (!row.selectchecked.length) {
let arr = [];
opt.forEach(element => {
arr.push(element.id);
});
row.selectchecked = arr;
row.checkAll = true;
row.isIndeterminate = false;
} else {
row.selectchecked = [];
row.checkAll = false;
row.isIndeterminate = false;
}
},
handleCheckedCitiesChange(index, row, opt) {
row.checkAll = row.selectchecked.length === opt.length;
row.isIndeterminate =
row.selectchecked.length > 0 && row.selectchecked.length < opt.length;
this.cc();
},
handleCheckAllChange1(index, row, opt) {
if (row.children) {
row.children.forEach(val => {
let arr = [];
if (row.checkAll) {
val[opt].forEach(element => {
arr.push(element.id);
});
val.selectchecked = arr;
val.checkAll = true;
val.isIndeterminate = false;
} else {
val.selectchecked = [];
val.checkAll = false;
val.isIndeterminate = false;
}
});
}
this.cc();
},
defaultSelcet() {
this.data.forEach(val => {
if (val.children) {
val.children.forEach(el => {
if (
el.selectchecked.length &&
el.selectchecked.length !== el[this.columns[0].option].length
) {
el.isIndeterminate = true;
el.checkAll = false;
} else if (
el.selectchecked.length &&
el.selectchecked.length === el[this.columns[0].option].length
) {
el.isIndeterminate = false;
el.checkAll = true;
} else {
el.isIndeterminate = false;
el.checkAll = false;
}
});
this.cc();
}
});
},
cc() {
this.data.forEach(val => {
let checkAllArr = [];
let isIndeterminateArr = [];
if (val.children) {
val.children.forEach(el => {
checkAllArr.push(el.checkAll);
isIndeterminateArr.push(el.isIndeterminate);
});
}
if (new Set(checkAllArr).size === 1) {
// && new Set(isIndeterminateArr).size !== 1
if (checkAllArr[0] && isIndeterminateArr[0] === false) {
val.isIndeterminate = false;
val.checkAll = true;
} else if (checkAllArr[0] && new Set(isIndeterminateArr).size !== 1) {
val.isIndeterminate = false;
val.checkAll = true;
} else if (
!checkAllArr[0] &&
new Set(isIndeterminateArr).size !== 1
) {
val.isIndeterminate = true;
val.checkAll = false;
} else if (
!checkAllArr[0] &&
new Set(isIndeterminateArr).size === 1
) {
if (!isIndeterminateArr[0]) {
val.isIndeterminate = false;
val.checkAll = false;
} else {
val.isIndeterminate = true;
val.checkAll = false;
}
} else {
val.isIndeterminate = false;
val.checkAll = false;
}
} else {
val.isIndeterminate = true;
val.checkAll = false;
}
});
},
getAuth() {
this.$emit("getAuth", this.data);
}
}
};
</script>
<style rel="stylesheet/css">
@keyframes treeTableShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-webkit-keyframes treeTableShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.el-table__body {
text-align: left;
}
</style>
<style lang="scss" rel="stylesheet/scss" scoped>
footer {
display: flex;
justify-content: flex-end;
margin-top: 15px;
}
$color-blue: #2196f3;
$space-width: 18px;
.ms-tree-space {
position: relative;
top: 1px;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1;
width: $space-width;
height: 14px;
&::before {
content: "";
}
}
.processContainer {
width: 100%;
height: 100%;
}
table td {
line-height: 26px;
}
.tree-ctrl {
position: relative;
cursor: pointer;
color: $color-blue;
margin-left: -$space-width;
}
</style>
- 使用組件TableTree.vue
<template>
<div class="app-container">
<tree-table :data="data" :columns="columns" border @getAuth="getAuth"></tree-table>
</div>
</template>
<script>
/**
Explain:根據(jù)花褲衩的表格改的,
isIndeterminate屬性是控制多選半選中狀態(tài),
checkAll是控制全選中狀態(tài)
selectchecked是放置sonData1選中項
*/
import treeTable from '@/components/treeTable'
export default {
name: 'treeTableDemo',
components: { treeTable },
data () {
return {
columns: [
{
text: '菜單列表',
value: 'description',
width: 200,
option: 'sonData1'
},
{
text: '功能權(quán)限',
value: 'sonData1',
option: 'sonData1',
act: 'act'
}
],
data: [
{
type: 0,
'checked': false,
'id': '1',
'description': '用戶管理',
isIndeterminate: false,
checkAll: false,
act: '全選',
children: [
{
type: 1,
id: 6,
'description': '用戶列表',
'parentId': '-1',
'checked': false,
selectchecked: ['7'],
checkAll: false,
isIndeterminate: false,
'sonData1': [
{
type: 2,
'description': '用戶新增',
'parentId': '6',
'checked': false,
'id': '7'
},
{
type: 2,
'description': '用戶修改',
'parentId': '6',
'checked': false,
'id': '8'
},
{
type: 2,
'description': '用戶刪除',
'parentId': '6',
'checked': false,
'id': '9'
}
]
},
{
type: 1,
id: 13,
'description': '角色列表',
'parentId': '-1',
'checked': false,
selectchecked: ['10', '11', '12'],
checkAll: false,
isIndeterminate: false,
'sonData1': [
{
type: 2,
'description': '角色授權(quán)',
'parentId': '6',
'checked': false,
'id': '10'
},
{
type: 2,
'description': '角色修改',
'parentId': '6',
'checked': false,
'id': '11'
},
{
type: 2,
'description': '角色刪除',
'parentId': '6',
'checked': false,
'id': '12'
}
]
}
]
},
{
type: 0,
'checked': false,
'id': '2',
'description': '設(shè)備管理',
isIndeterminate: false,
checkAll: false,
act: '全選',
children: [
{
type: 1,
id: 6,
'description': '設(shè)備列表',
'parentId': '-1',
'checked': false,
selectchecked: [],
checkAll: false,
isIndeterminate: false,
'sonData1': [
{
type: 2,
'description': '設(shè)備新增',
'parentId': '6',
'checked': false,
'id': '17'
},
{
type: 2,
'description': '設(shè)備修改',
'parentId': '6',
'checked': false,
'id': '18'
},
{
type: 2,
'description': '設(shè)備刪除',
'parentId': '6',
'checked': false,
'id': '19'
}
]
}
]
}
]
}
},
created () {
},
methods: {
getAuth (data) {
let opt = []
data.forEach(val => {
opt.push(val.id)
if (val.children) {
val.children.forEach(el => {
console.log(val.id)
if (el.selectchecked.length) {
opt.push(el.id)
opt.push(el.selectchecked)
}
})
}
})
console.log(data)
opt = opt.join().split(',').filter(n => { return n })
console.log(opt)
}
}
}
</script>
-
測試效果
最終效果
三、使用說明
- columns
列屬性,要求是一個數(shù)組
text: 顯示在表頭的文字
value: 對應(yīng)data的key。treeTable將顯示相應(yīng)的value
width: 每列的寬度,為一個數(shù)字(可選) - expandAll
是否默認(rèn)全部展開,boolean值,默認(rèn)為false - evalFunc
解析函數(shù),function,非必須
如果不提供,將使用默認(rèn)的evalFunc - evalArgs
解析函數(shù)的參數(shù),是一個數(shù)組
請注意,自定義的解析函數(shù)參數(shù)第一個為this.data,第二個參數(shù)為, this.expandAll,你不需要在evalArgs填寫。一定記住,這兩個參數(shù)是強(qiáng)制性的,并且位置不可顛倒 this.data為需要解析的數(shù)據(jù),this.expandAll為是否默認(rèn)展開。
如你的解析函數(shù)需要的參數(shù)為(this.data, this.expandAll,1,2,3,4),那么你只需要將[1,2,3,4]賦值給evalArgs就可以了
如果你的解析函數(shù)參數(shù)只有(this.data, this.expandAll),那么就可以不用填寫evalArgs了。 - slot
這是一個自定義列的插槽。
默認(rèn)情況下,treeTable只有一行行展示數(shù)據(jù)的功能。但是一般情況下,我們會要給行加上一個操作按鈕或者根據(jù)當(dāng)行數(shù)據(jù)展示不同的樣式,這時我們就需要自定義列了。
slot和columns屬性可同時存在,columns里面的數(shù)據(jù)列會在slot自定義列的左邊展示
三、樹形數(shù)據(jù)(2.11.1)
https://element.eleme.io/#/zh-CN/component/table
<template>
<div>
<el-table
:data="tableData"
style="width: 100%;margin-bottom: 20px;"
row-key="id"
border
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
<el-table-column prop="name" label="名稱" sortable width="180"></el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [
{
children: [
{
name: "菜單管理",
id: "060ac069ea5b1b1b90e200d94f7941a2"
},
{
name: "字典管理",
id: "68aa1ca036ef8e6d606651f258321f88"
},
{
children: [
{
name: "刪除",
id: "0e64c997bcec402b7a840fc18e7e4e82"
},
{
name: "查詢",
id: "52ae959c875c6fc521ba4ea546ee88ed"
},
{
name: "添加",
id: "59c86152b4e0b345636eb3a48d9ad263"
},
{
name: "導(dǎo)出",
id: "66d8a79be81d61d4e75feccfe2aee94e"
},
{
name: "更新",
id: "acc80082612d67847940985dfe53268b"
},
{
name: "導(dǎo)入",
id: "e5668f14d71db22cb2ad063c87f0b6d3"
}
],
name: "角色管理",
id: "951581fdede1c585a5b7f2de16eddfe2"
},
{
name: "組織機(jī)構(gòu)",
id: "3504711eedb26cab2b9eb2f8c3433aa7"
},
{
name: "用戶管理",
id: "6221da7dac0e80af56d18f9355003438"
}
],
name: "系統(tǒng)設(shè)置",
id: "0b423292e0e27485cdd5257be6701a9f"
}
]
};
},
};
</script>

結(jié)果展現(xiàn)
說明:
支持樹類型的數(shù)據(jù)的顯示。當(dāng) row 中包含 children 字段時,被視為樹形數(shù)據(jù)。渲染樹形數(shù)據(jù)時,必須要指定 row-key。支持子節(jié)點數(shù)據(jù)異步加載。設(shè)置 Table 的 lazy 屬性為 true 與加載函數(shù) load 。通過指定 row 中的 hasChildren 字段來指定哪些行是包含子節(jié)點。children 與 hasChildren 都可以通過 tree-props 配置。
新版功能更簡單,更好使用。
注意:
實際測試,hasChildren 屬性不添加也可,注意id不要重復(fù),default-expand-all 屬性無需設(shè)置值,添加該屬性全部展開,不添加折疊。
