1、使用el-menu子组件递归
<template>
<div class="main-sidebar" class="showSlide expandSide">
<el-menu class="el-menu-style"
@select=""
@open="handleOpen"
@close="handleClose">
<template v-for="item in fileList">
<sub-menu :param="item" ></sub-menu>
</template>
</el-menu>
</div>
</template>
<script>
import subMenu from "./subMenu.vue"
import {fileList} from '../http/api'
export default {
data() {
return {
fileList:{},
}
},
computed: {
onRoutes(){
return this.$route.path;
},
onRouteKeys(){
return getParentArray(this.$route.path, this.fileList);
}
},
created() {
this.loadFile();
},
methods: {
handleOpen(key, keyPath) {
},
handleClose(key, keyPath) {
},
loadFile(){
let projectId = this.$route.params.id
fileList(projectId).then(res => {
this.fileList = res.data
}).catch(err => {
alert("当前用例为空")
})
}
}
}
</script>
<style scoped>
.showSlide {
animation-duration: .2s;
animation-name: slideInLeft;
}
.hideSlide {
animation-duration: .2s;
animation-name: slideOutLeft;
}
.main-sidebar {
background-color: #ffffff;
position: fixed;
top: 50px;
left: 0;
bottom: 0;
height: calc(100vh - 50px);
width: 44px;
z-index: 810;
-webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out;
-moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out;
-o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out;
transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
}
.expandSide {
width: 280px;
}
.el-menu-style,
.el-menu-style .el-menu{
background-color: #ffffff;
}
.el-menu-style .el-menu-item:hover,
.el-menu-style .el-submenu__title:hover{
background-color: #eeeeee !important;
}
.el-menu-style .el-submenu .el-menu-item {
height: 45px;
line-height: 45px;
}
.el-menu-style .el-menu-item,
.el-menu-style .el-submenu__title {
height: 45px;
line-height: 45px;
}
.main-sidebar .el-menu--collapse {
width: 44px;
}
.main-sidebar .el-menu--collapse>.el-menu-item,
.main-sidebar .el-menu--collapse>.el-submenu>.el-submenu__title {
padding-left: 13px !important;
}
.vue-scrollbar{
background-color: #ffffff !important;
height: calc(100vh - 50px)
}
.main-sidebar .el-scrollbar__bar.is-vertical{
display: none;
}
.sidebar{
min-height: 450px;
}
</style>
<template>
<div class="contents">
<div class="menu-list">
<el-submenu :index="item.href" v-if="item.children && item.children.length>0" class="el-menu-sub" >
<template slot="title">
<div v-if="item.isShow == 2" v-contextmenu:contextmenu-file>
<i class="fa fa-file"></i>
<el-link :underline="false" @click="intoItem(item.id)">
<span>{{item.name}}</span>
</el-link>
</div>
<div v-else v-contextmenu:contextmenu-folder>
<i class="fa fa-folder"></i>
<span>{{item.name}}</span>
</div>
</template>
<template v-for="child in item.children">
<sub-menu v-if="child.children && child.children.length>0" :param="child"></sub-menu>
<el-menu-item :index="child.href" v-else >
<div v-if="child.isShow == 1" v-contextmenu:contextmenu-folder>
<i class="fa fa-folder"></i>
<span>{{item.name}}</span>
</div>
<div v-else v-contextmenu:contextmenu-file>
<i class="fa fa-file"></i>
<el-link :underline="false" @click="intoItem(child.id)">
<span>{{child.name}}</span>
</el-link>
</div>
</el-menu-item>
</template>
</el-submenu>
<el-menu-item :index="item.href" v-else class="el-menu-each">
<div v-if="item.isShow == 2" v-contextmenu:contextmenu-file>
<i class="fa fa-file"></i>
<el-link :underline="false" @click="intoItem(item.id)">
<span>{{item.name}}</span>
</el-link>
</div>
<div v-else v-contextmenu:contextmenu-folder>
<i class="fa fa-folder"></i>
<span>{{item.name}}</span>
</div>
</el-menu-item>
</div>
</div>
</template>
<script>
import subMenu from "./subMenu.vue"
export default {
name: 'subMenu',
props: ['param'],
data() {
return {
item: this.param
}
},
components: {
subMenu,
}
}
</script>
<style scoped>
.el-menu .fa {
margin-right: 10px;
}
.box {
width: 100%;
}
.el-dialog{
display: flex;
flex-direction: column;
margin:0 !important;
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
max-height:calc(100% - 30px);
max-width:calc(100% - 30px);
}
.el-dialog .el-dialog__body{
flex:1;
overflow: auto;
}
</style>
2、使用开源组件v-tree
.ztree * {
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select:none;
-khtml-user-select:none;
-webkit-user-select:none;
-ms-user-select:none;
user-select:none;
padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif}
.ztree {margin:0; padding:5px; color:#333}
.ztree li{padding:0; margin:0; list-style:none; line-height:17px; text-align:left; white-space:nowrap; outline:0}
.ztree li ul{ margin:0; padding:0 0 0 18px}
.ztree li ul.line{ background:url(../img/line_conn.png) 0 0 repeat-y;}
.ztree li a {padding-right:3px; margin:0; cursor:pointer; height:21px; color:#333; background-color: transparent; text-decoration:none; vertical-align:top; display: inline-block}
.ztree li a:hover {text-decoration:underline}
.ztree li a.curSelectedNode {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; opacity:0.8;}
.ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; border:1px #666 solid; opacity:0.8;}
.ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#aaa; color:white; height:21px; border:1px #666 solid;
opacity:0.8; filter:alpha(opacity=80)}
.ztree li a.tmpTargetNode_prev {}
.ztree li a.tmpTargetNode_next {}
.ztree li a input.rename {height:14px; width:80px; padding:0; margin:0;
font-size:12px; border:1px #585956 solid; *border:0px}
.ztree li span {line-height:21px; margin-right:2px}
.ztree li span.button {line-height:0; margin:0; padding: 0; width:21px; height:21px; display: inline-block; vertical-align:middle;
border:0 none; cursor: pointer;outline:none;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
background-image:url("../img/v-tree.png");}
.ztree li span.button.chk {width:13px; height:13px; margin:0 2px; cursor: auto}
.ztree li span.button.chk.checkbox_false_full {background-position: -5px -5px;}
.ztree li span.button.chk.checkbox_false_full_focus {background-position: -5px -26px;}
.ztree li span.button.chk.checkbox_false_part {background-position: -5px -48px;}
.ztree li span.button.chk.checkbox_false_part_focus {background-position: -5px -68px;}
.ztree li span.button.chk.checkbox_false_disable {background-position: -5px -89px;}
.ztree li span.button.chk.checkbox_true_full {background-position: -26px -5px;}
.ztree li span.button.chk.checkbox_true_full_focus {background-position: -26px -26px;}
.ztree li span.button.chk.checkbox_true_part {background-position: -26px -48px;}
.ztree li span.button.chk.checkbox_true_part_focus {background-position: -26px -68px;}
.ztree li span.button.chk.checkbox_true_disable {background-position: -26px -89px;}
.ztree li span.button.chk.radio_false_full {background-position: -47px -5px;}
.ztree li span.button.chk.radio_false_full_focus {background-position: -47px -26px;}
.ztree li span.button.chk.radio_false_part {background-position: -47px -47px;}
.ztree li span.button.chk.radio_false_part_focus {background-position: -47px -68px;}
.ztree li span.button.chk.radio_false_disable {background-position: -47px -89px;}
.ztree li span.button.chk.radio_true_full {background-position: -68px -5px;}
.ztree li span.button.chk.radio_true_full_focus {background-position: -68px -26px;}
.ztree li span.button.chk.radio_true_part {background-position: -68px -47px;}
.ztree li span.button.chk.radio_true_part_focus {background-position: -68px -68px;}
.ztree li span.button.chk.radio_true_disable {background-position: -68px -89px;}
.ztree li span.button.switch {width:21px; height:21px}
.ztree li span.button.root_open{background-position:-105px -63px}
.ztree li span.button.root_close{background-position:-126px -63px}
.ztree li span.button.roots_open{background-position: -105px 0;}
.ztree li span.button.roots_close{background-position: -126px 0;}
.ztree li span.button.center_open{background-position: -105px -21px;}
.ztree li span.button.center_close{background-position: -126px -21px;}
.ztree li span.button.bottom_open{background-position: -105px -42px;}
.ztree li span.button.bottom_close{background-position: -126px -42px;}
.ztree li span.button.noline_open{background-position: -105px -84px;}
.ztree li span.button.noline_close{background-position: -126px -84px;}
.ztree li span.button.root_docu{ background:none;}
.ztree li span.button.roots_docu{background-position: -84px 0;}
.ztree li span.button.center_docu{background-position: -84px -21px;}
.ztree li span.button.bottom_docu{background-position: -84px -42px;}
.ztree li span.button.noline_docu{ background:none;}
.ztree li span.button.ico_open{margin-right:2px; background-position: -147px -21px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.ico_close{margin-right:2px; background-position: -147px 0; vertical-align:top; *vertical-align:middle}
.ztree li span.button.ico_docu{margin-right:2px; background-position: -147px -42px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.edit {margin-left:2px; margin-right: -1px; background-position: -189px -21px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.edit:hover {
background-position: -168px -21px;
}
.ztree li span.button.remove {margin-left:2px; margin-right: -1px; background-position: -189px -42px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.remove:hover {
background-position: -168px -42px;
}
.ztree li span.button.add {margin-left:2px; margin-right: -1px; background-position: -189px 0; vertical-align:top; *vertical-align:middle}
.ztree li span.button.add:hover {
background-position: -168px 0;
}
.ztree li span.button.ico_loading{margin-right:2px; vertical-align:top; *vertical-align:middle}
ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}
span.tmpzTreeMove_arrow {width:16px; height:21px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
background-position:-168px -84px; background-image:url("../img/v-tree.png"); }
ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)}
.ztreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}
<template>
<div>
<ul class="ztree">
<vTree v-for="(node,index) in treeData"
:key="index"
:checkBoxType="checkBoxType"
:allOpen="allOpen"
:beforeClick="beforeClick"
:checkBox="checkBox"
:nodeTrigger="nodeTrigger"
:index="index"
:tree="node"
:first="index===0"
:last="treeData.length-1===index"
:currentArray="treeData"
:parentTree="node.parentTree"
:rootData="treeData"
:checkBoxCallInit="checkBoxCallInit"
:checkBoxCall="checkBoxCall"
:clickNode="clickNode"
:hiddenLine="hiddenLine"
:async="async"
:asyncCall="asyncCall"
/>
</ul>
</div>
</template>
<script>
import vTree from './tree-core'
export default {
components: {vTree},
name: "tree",
props: {
treeNode: {
type: Array,
default: function () {
return [];
},
required: true,
},
allOpen: {
type: Boolean,
default: null,
required: false,
},
nodeTrigger: {
type: Boolean,
default: false,
required: false,
},
checkBox: {
type: Boolean,
default: false,
required: false,
},
checkBoxType: {
type: Boolean,
default: true,
required: false,
},
beforeClick: {
type: Function,
default: null
},
clickNode: {
type: Function,
},
asyncCall: {
type: Function,
},
hiddenLine: {
type: Boolean,
default: false,
required: false,
},
async: {
type: Boolean,
default: false,
required: false,
},
},
data() {
return {
treeData: [],
line: '',
first: true,
allOpens: this.allOpen,
checkedBoxCallArr: [],
}
},
methods: {
init() {
let tempList = JSON.parse(JSON.stringify(this.treeNode));
let initTree = (tree, parent) => {
for (let index = 0; index < tree.length; index++) {
let m = tree[index];
if (!m.hasOwnProperty("id")) {
m.id = m.hasOwnProperty("id") ? m.id : null;
}
if (!m.hasOwnProperty("open")) {
m.open = m.hasOwnProperty("open") ? m.open : false;
}
if (!m.hasOwnProperty("checked")) {
m.checked = m.hasOwnProperty("checked") ? m.checked : false;
}
if (!m.hasOwnProperty("checkBox")) {
m.checkBox = m.hasOwnProperty("checkBox") ? m.checkBox : false;
}
if (!m.hasOwnProperty("nodeTrigger")) {
m.nodeTrigger = m.hasOwnProperty("nodeTrigger") ? m.nodeTrigger : false;
}
if (!m.hasOwnProperty("checkBoxType")) {
m.checkBoxType = this.checkBoxType
}
if (!m.hasOwnProperty("last")) {
m.last = m.hasOwnProperty("last") ? m.last : false;
}
if (!m.hasOwnProperty("first")) {
m.first = m.hasOwnProperty("first") ? m.first : false;
}
if (!m.hasOwnProperty("active")) {
m.active = m.hasOwnProperty("active") ? m.active : false;
}
if (!m.hasOwnProperty("async")) {
m.async = this.async;
}
if (!m.hasOwnProperty("hiddenLine")) {
m.hiddenLine = this.hiddenLine;
}
if (!m.hasOwnProperty("parentTree")) {
m.parentTree = parent ? parent : null;
}
m.children = m.children || [];
if (m.children.length > 0)
initTree(m.children, m);
}
};
initTree(tempList, null);
this.treeData = tempList;
this.line = 'line';
if (this.first)
this.$emit('call', this.treeData);
},
changeStatus() {
let changeOpen = (data) => {
data.forEach(d => {
d.open = this.allOpen;
if (d.children) changeOpen(d.children);
});
};
changeOpen(this.treeData);
},
checkBoxCallInit(arr) {
arr.forEach(a => {
this.checkedBoxCallArr.push(a);
});
},
checkBoxCall(arr, isAdd) {
if (isAdd)
arr.forEach(a => {
this.checkedBoxCallArr.push(a);
});
else {
arr.forEach(a => {
if (this.checkBoxCall.length === 0)
return;
let key = (a.id ? a.id : null) + a.index + a.name;
this.checkedBoxCallArr.forEach((ss, index) => {
if (((ss.id ? ss.id : null) + ss.index + ss.name) === key) {
this.checkedBoxCallArr.splice(index, 1);
}
});
});
}
this.$emit('checkBoxCall', this.checkedBoxCallArr);
},
},
created() {
this.init();
},
update() {
},
mounted() {
this.$emit('checkBoxCall', this.checkedBoxCallArr);
},
watch: {
allOpen() {
this.changeStatus()
}
}
}
</script>
<style scoped>
@import "../../../../static/css/v-tree.css";
</style>
<template>
<li>
<span v-if="vif()" title=""
@click="vShow(tree)"
class="button switch"
:class="vTreeFirstSpan()"></span>
<span v-else title="" class="button switch "
:class="classes()"></span>
<span v-if="checkBox" @click="selectCheckBox(tree)" class="button chk"
:class="tree.checked?'checkbox_true_full':'checkbox_false_full'"></span>
<a target="_blank"
style=""
@click="selectNode"
:class="aClass()"
:title="tree.name">
<span :title="tree.name" class="button" :class="iconCss()" :style="load()"></span>
<span class="node_name">{{tree.name}}</span>
</a>
<ul :class="line">
<treeCore
v-for="(tr,i) in tree.children"
v-show="tree.open"
:key="i"
:checkBoxType="checkBoxType"
:allOpen="tr.allOpen"
:first="false"
:checkBox="checkBox"
:nodeTrigger="nodeTrigger"
:index="i"
:tree="tr"
:currentArray="tree.children"
:parentTree="tr.parentTree"
:rootData="rootData"
:clickNode="clickNode"
@checkedBoxV="checkedBoxV"
:beforeClick="beforeClick"
:checkBoxCallInit="checkBoxCallInit"
:checkBoxCall="checkBoxCall"
:last="tree.children.length-1===i"
:hiddenLine="tr.hiddenLine"
:async="async"
:asyncCall="asyncCall"
/>
</ul>
</li>
</template>
<script>
export default {
name: "treeCore",
props: {
tree: {
type: Object,
required: true,
},
allOpen: {
type: Boolean,
default: null,
required: false,
},
nodeTrigger: {
type: Boolean,
default: false,
required: false,
},
checkBox: {
type: Boolean,
default: false,
required: false,
},
checkBoxType: {
type: Boolean,
default: true,
required: false,
},
beforeClick: {
type: Function,
default: null
},
last: {
type: Boolean,
default: null,
required: false,
},
first: {
type: Boolean,
default: true,
required: false,
},
currentArray: {
type: Array,
required: true,
},
index: {
type: Number,
required: true,
},
rootData: {
type: Array,
required: true,
},
parentTree: {
type: Object,
required: false,
},
checkBoxCallInit: {
type: Function,
},
checkBoxCall: {
type: Function,
},
clickNode: {
type: Function,
},
asyncCall: {
type: Function,
},
hiddenLine: {
type: Boolean,
default: false,
required: false,
},
async: {
type: Boolean,
default: false,
required: false,
},
},
data() {
return {
line: '',
currentTree: this.tree,
asyncLoading: false,
}
},
methods: {
coreInit() {
this.line = this.hiddenLine ? '' : this.currentArray.length - 1 === this.index ? '' : 'line';
if (this.tree.parentTree && this.tree.checked) {
let checkedBoxArr = [];
checkedBoxArr.push(this.tree);
let selectParent = (data) => {
if (!data.checked) {
data.checked = this.tree.checked;
checkedBoxArr.push(data);
}
if (data.parentTree) {
selectParent(data.parentTree);
} else {
this.checkBoxCallInit(checkedBoxArr);
}
};
selectParent(this.currentTree);
}
},
checkedBoxV() {
this.tree.checked = true;
this.$emit('checkedBoxV')
},
pNode(tree) {
return tree.open;
},
vShow(tree) {
if (this.async && !tree.open && tree.children.length === 0) {
this.asyncCall.call(this, tree, this.asyncBack);
this.asyncLoading = true;
}
tree.open = !tree.open;
},
asyncBack(requestDataArr) {
if (typeof requestDataArr)
this.addNode(requestDataArr);
this.asyncLoading = false;
},
selectNode() {
let isClick = this.beforeClick.call(this, this.tree);
if (isClick) {
let pro = null;
let clearStyle = (data) => {
for (let i = 0; i < data.length; i++) {
let d = data[i];
if (d.active) {
pro = d;
d.active = !d.active;
} else if (d.children) {
clearStyle(d.children);
}
}
};
clearStyle(this.rootData);
this.tree.active = true;
if (this.nodeTrigger) {
this.tree.open = !this.tree.open;
}
this.clickNode.call(this, this.tree, pro);
}
},
addNode(arr) {
try {
arr.forEach(a => {
this.tree.children.push({
id: a.id ? a.id : null,
name: a.name,
open: a.open ? a.open : false,
checked: a.checked ? a.checked : false,
checkBox: a.checkBox ? a.checkBox : false,
nodeTrigger: a.nodeTrigger ? a.nodeTrigger : this.nodeTrigger,
checkBoxType: this.checkBoxType,
last: false,
first: false,
active: false,
async: this.async,
hiddenLine: this.hiddenLine,
parentTree: this.tree,
children: [],
});
});
} catch (e) {
console.error('The asynchronous callback parameter must be an array,异步回调参数必须是数组');
}
},
selectCheckBox(tree) {
tree.checked = !tree.checked;
let checkedBoxArr = [];
checkedBoxArr.push(tree);
if (this.checkBoxType) {
let changeChecked = (data) => {
data.forEach(d => {
checkedBoxArr.push(d);
d.checked = tree.checked;
if (d.children) changeChecked(d.children);
});
};
changeChecked(tree.children);
let checkChildren = (data) => {
for (let i = 0; i < data.length; i++) {
if (data[i].checked)
return true;
}
};
let selectParent = (data) => {
if (data.children.length > 1) {
const childExistsThis = [];
data.children.forEach((m, index) => {
if (index !== this.index)
childExistsThis.push(m);
});
if (childExistsThis && data.checked && checkChildren(childExistsThis))
return;
}
checkedBoxArr.push(data);
data.checked = tree.checked;
if (data.parentTree) selectParent(data.parentTree);
};
if (this.parentTree)
selectParent(this.parentTree);
}
this.checkBoxCall(checkedBoxArr, tree.checked);
}
},
created() {
this.coreInit();
},
computed: {
vif() {
return function () {
return this.async ? true : this.tree.children.length > 0;
}
},
vTreeFirstSpan() {
return function () {
return this.hiddenLine ? this.tree.open ? 'noline_open treenode_switch' : 'noline_close treenode_switch' :
this.currentArray.length - 1 === this.index ? this.tree.open ? 'bottom_open' : 'bottom_close'
:
this.tree.open ? 'roots_open' : 'roots_close';
}
},
classes() {
return function () {
return this.hiddenLine ?
'noline_docu'
:
this.first ?
'roots_docu'
:
this.currentArray.length - 1 === this.index ?
'bottom_docu'
:
'center_docu'
}
},
iconCss() {
return function () {
return this.tree.children.length > 0 ? this.tree.open ? 'ico_open' : 'ico_close' : 'ico_docu';
}
},
load() {
return function () {
return this.asyncLoading ?
'background:url(../../static/img/loading.gif) 0 0 no-repeat' : ''
}
},
aClass() {
return function () {
return this.tree.active ? 'curSelectedNode' : '';
}
}
},
watch: {
tree: {
handler(oldV, newV) {
if (newV.checked)
this.tree.checked = newV.checked;
},
deep: true
},
}
}
</script>
<style scoped>
@import "../../../../static/css/v-tree.css";
</style>
<template>
<div style="height: auto">
<div class="main">
<ul>
<li>
<Tree
:treeNode="treeNode"
:clickNode="clickNode"
:beforeClick="beforeClick"
:hiddenLine="hiddenLine"
:async="async"
@call='callAsync'
:asyncCall="asyncCall"
:nodeTrigger="nodeTrigger"
/>
</li>
</ul>
</div>
</div>
</template>
<script>
import Tree from "./tree";
export default {
components: {Tree},
data() {
return {
treeNode: [],
nodeTrigger: false,
trees: [],
hiddenLine: false,
async: true,
}
},
methods: {
beforeClick(node) {
return true;
},
call(data) {
console.log('data', data);
},
callAsync(data) {
},
clickNode(data, oldData) {
},
asyncCall(data, call) {
setTimeout(function () {
let hm = '' + (new Date()).getTime();
let addNode = [
{id: hm, name: "children node" + hm.substr(hm.length - 4, hm.length)},
];
console.log(data.id)
call([])
}, 800);
}
},
mounted() {
},
created() {
this.treeNode = [
{
id: "1",
name: "父节点22 - 折叠",
open: true,
children: [
{id: "1-1", name: "叶子节点221",open: true},
{id: "1-2", name: "叶子节点222",open: true},
{id: "1-3", name: "叶子节点223",open: true},
{id: "1-4", name: "叶子节点224",open: true}
]
}, {
id: "2",
name: "父节点23 - 折叠",
children: [
{id: "2-1", name: "叶子节点231"},
{id: "2-2", name: "叶子节点232"},
{id: "2-3", name: "叶子节点233"},
{id: "2-4", name: "叶子节点234"}
]
}, {
open: true,
id: "3",
name: "父节点24 - 折叠",
children: [
{
id: "2-1-1", name: "叶子节点232", open: true,
children: [
{id: "2-1-3", name: "叶子节点2313"},
{id: "2-2-3", name: "叶子节点2323"},
{id: "2-3-3", name: "叶子节点2333"},
{id: "2-4-3", name: "叶子节点2343"}
]
},
]
},
];
}
}
</script>
<style scoped>
.main ul li {
list-style: none;
float: left;
width: 25%;
}
</style>
3、使用el-tree动态加载