树形结构的组件:
<template>
<span class="tree-expand">
<span class="tree-label" v-show="DATA.isEdit">
<el-input class="edit" size="mini" autofocus
v-model="DATA.name"
:ref="'treeInput'+DATA.id"
@click.stop.native="nodeEditFocus"
@blur.stop="nodeEditPass(STORE,DATA,NODE)"
@keyup.enter.stop.native="nodeEditPass(STORE,DATA,NODE)"></el-input>
</span>
<span v-show="!DATA.isEdit"
:class="[DATA.id > maxexpandId ? 'tree-new tree-label' : 'tree-label']">
<span>{{DATA.name}}</span>
</span>
<span class="tree-btn" v-show="!DATA.isEdit">
<i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
<i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
<i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
</span>
</span>
</template>
<script>
export default{
name: 'nx-tree-render',
props: ['NODE', 'DATA', 'STORE', 'maxexpandId'],
methods: {
nodeAdd(s, d, n) { // 新增
this.$emit('nodeAdd', s, d, n)
},
nodeEdit(s, d, n) { // 编辑
d.isEdit = true
this.$nextTick(() => {
this.$refs['treeInput' + d.id].$refs.input.focus()
})
this.$emit('nodeEdit', s, d, n)
},
nodeDel(s, d, n) { // 删除
this.$emit('nodeDel', s, d, n)
},
// 编辑完成
nodeEditPass(s, d, n) { // 编辑完成
console.log('编辑完成', d)
this.$emit('finishEdit', s, d, n)
d.isEdit = false
},
nodeEditFocus() {
// 阻止点击节点的事件冒泡
}
}
}
</script>
<style>
.tree-expand{
overflow:hidden;
}
.tree-expand .tree-label.tree-new{
font-weight:600;
}
.tree-expand .tree-label{
font-size:0.9em;
}
.tree-expand .tree-label .edit{
width:80%;
}
.tree-expand .tree-btn{
display:none;
float:right;
margin-right:20px;
}
.tree-expand .tree-btn i{
color:#8492a6;
font-size:0.9em;
margin-right:3px;
}
</style>
组件的应用:
<template>
<div class="expand">
<p class="warn-content">
<a href="https://xiaoniezi.github.io/2017-10-16/vue-ElementUI-tree-expand/" target="_blank">Tree组件参考 tree
</a>
</p>
<div>
<el-button @click="handleAddTop">添加顶级节点</el-button>
<el-tree ref="expandMenuList" class="expand-tree"
v-if="isLoadingTree"
:data="setTree"
node-key="id"
highlight-current
:props="defaultProps"
:expand-on-click-node="false"
:render-content="renderContent"
@node-click="handleNodeClick"></el-tree>
</div>
</div>
</template>
<script>
import TreeRender from '@/components/nx-tree'
import api from '@/api/treeApi'
export default{
name: 'tree',
data() {
return {
maxexpandId: api.maxexpandId, // 新增节点开始id
non_maxexpandId: api.maxexpandId, // 新增节点开始id(不更改)
isLoadingTree: false, // 是否加载节点树
setTree: api.treelist, // 节点树数据
defaultProps: {
children: 'children',
label: 'name'
},
defaultExpandKeys: [] // 默认展开节点列表
}
},
mounted() {
console.log(api)
this.initExpand()
},
methods: {
initExpand() {
this.setTree.map((a) => {
this.defaultExpandKeys.push(a.id)
})
this.isLoadingTree = true
},
handleNodeClick(d, n, s) { // 点击节点
// console.log(d,n)
d.isEdit = false// 放弃编辑状态
},
renderContent(h, { node, data, store }) { // 加载节点
const that = this
return h(TreeRender, {
props: {
DATA: data,
NODE: node,
STORE: store,
maxexpandId: that.non_maxexpandId
},
on: {
nodeAdd: (s, d, n) => that.handleAdd(s, d, n),
nodeEdit: (s, d, n) => that.handleEdit(s, d, n),
nodeDel: (s, d, n) => that.handleDelete(s, d, n)
finishEdit: (s, d, n) => that.handleFinishEdit(s, d, n) // 编辑完成状态
}
})
},
handleAddTop() {
this.setTree.push({
id: ++this.maxexpandId,
name: '新增节点',
pid: '',
isEdit: false,
children: []
})
},
handleAdd(s, d, n) { // 增加节点
console.log(s, d, n)
if (n.level >= 6) {
this.$message.error('最多只支持五级!')
return false
}
// 添加数据
d.children.push({
id: ++this.maxexpandId,
name: '新增节点',
pid: d.id,
isEdit: false,
children: []
})
// 展开节点
if (!n.expanded) {
n.expanded = true
}
},
handleEdit(s, d, n) { // 编辑节点
console.log(s, d, n)
},
// 此处编辑完成(点击页面任意处)
handleFinishEdit() {
console.log('触发了编辑完成')
},
handleDelete(s, d, n) { // 删除节点
console.log(s, d, n)
const that = this
// 有子级不删除
if (d.children && d.children.length !== 0) {
this.$message.error('此节点有子级,不可删除!')
return false
} else {
// 新增节点直接删除,否则要询问是否删除
const delNode = () => {
const list = n.parent.data.children || n.parent.data // 节点同级数据
let _index = 99999// 要删除的index
/* if(!n.parent.data.children){//删除顶级节点,无children
list = n.parent.data
}*/
list.map((c, i) => {
if (d.id === c.id) {
_index = i
}
})
const k = list.splice(_index, 1)
console.log(_index, k)
this.$message.success('删除成功!')
}
const isDel = () => {
that.$confirm('是否删除此节点?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delNode()
}).catch(() => {
return false
})
}
// 判断是否新增
d.id > this.non_maxexpandId ? delNode() : isDel()
}
}
}
}
</script>
<style>
.expand{
width:100%;
height:100%;
overflow:hidden;
}
.expand>div{
height:85%;
padding-top:20px;
width:50%;
margin:20px auto;
max-width:400px;
overflow-y:auto;
}
.expand>div::-webkit-scrollbar-track{
box-shadow: inset 0 0 6px rgba(0,0,0,.3);
border-radius:5px;
}
.expand>div::-webkit-scrollbar-thumb{
background-color:rgba(50, 65, 87, 0.5);
outline:1px solid slategrey;
border-radius:5px;
}
.expand>div::-webkit-scrollbar{
width:10px;
}
.expand-tree{
border:none;
margin-top:10px;
}
.expand-tree .el-tree-node.is-current,
.expand-tree .el-tree-node:hover{
overflow:hidden;
}
.expand-tree .is-current>.el-tree-node__content .tree-btn,
.expand-tree .el-tree-node__content:hover .tree-btn{
display:inline-block;
}
.expand-tree .is-current>.el-tree-node__content .tree-label{
font-weight:600;
white-space:normal;
}
</style>
注意:树形节点部分的样式一定不能加scoped,否则增删改的图标就会无法显示
API部分的数据:
export default {
maxexpandId: 95,
treelist: [{
id: 1,
name: '北京市',
ProSort: 1,
remark: '直辖市',
pid: '',
isEdit: false,
children: [{
id: 35,
name: '朝阳区',
pid: 1,
remark: '',
isEdit: false,
children: []
}, {
id: 36,
name: '海淀区',
pid: 1,
remark: '',
isEdit: false,
children: []
}, {
id: 37,
name: '房山区',
pid: 1,
remark: '',
isEdit: false,
children: []
}, {
id: 38,
name: '丰台区',
pid: 1,
remark: '',
isEdit: false,
children: []
}, {
id: 39,
name: '昌平区',
pid: 1,
remark: '',
isEdit: false,
children: []
}, {
id: 40,
name: '大兴区',
pid: 1,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 2,
name: '天津市',
ProSort: 2,
remark: '直辖市',
pid: '',
isEdit: false,
children: [{
id: 41,
name: '北辰区',
pid: 2,
remark: '',
isEdit: false,
children: []
}, {
id: 42,
name: '河北区',
pid: 2,
remark: '',
isEdit: false,
children: []
}, {
id: 43,
name: '西青区',
pid: 2,
remark: '',
isEdit: false,
children: []
}, {
id: 44,
name: '东丽区',
pid: 2,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 3,
name: '河北省',
ProSort: 5,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 45,
name: '衡水市',
pid: 3,
remark: '',
isEdit: false,
children: []
}, {
id: 46,
name: '张家口市',
pid: 3,
remark: '',
isEdit: false,
children: []
}, {
id: 47,
name: '秦皇岛市',
pid: 3,
remark: '',
isEdit: false,
children: []
}, {
id: 48,
name: '承德市',
pid: 3,
remark: '',
isEdit: false,
children: []
}, {
id: 49,
name: '邯郸市',
pid: 3,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 4,
name: '山西省',
ProSort: 6,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 50,
name: '大同市',
pid: 4,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 5,
name: '内蒙古自治区',
ProSort: 32,
remark: '自治区',
pid: '',
isEdit: false,
children: [{
id: 51,
name: '包头市',
pid: 5,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 6,
name: '辽宁省',
ProSort: 8,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 52,
name: '大连市',
pid: 6,
remark: '',
isEdit: false,
children: []
}, {
id: 53,
name: '太原市',
pid: 6,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 20,
name: '海南省',
ProSort: 24,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 68,
name: '海口市',
pid: 20,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 21,
name: '广西壮族自治区',
ProSort: 28,
remark: '自治区',
pid: '',
isEdit: false,
children: [{
id: 69,
name: '玉林市',
pid: 21,
remark: '',
isEdit: false,
children: []
}, {
id: 86,
name: '贺州市',
pid: 21,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 22,
name: '甘肃省',
ProSort: 21,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 70,
name: '兰州市',
pid: 22,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 23,
name: '陕西省',
ProSort: 27,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 71,
name: '咸阳市',
pid: 23,
remark: '',
isEdit: false,
children: []
}, {
id: 87,
name: '安康市',
pid: 23,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 24,
name: '新疆维吾尔自治区',
ProSort: 31,
remark: '自治区',
pid: '',
isEdit: false,
children: [{
id: 72,
name: '乌鲁木齐市',
pid: 24,
remark: '',
isEdit: false,
children: []
}, {
id: 88,
name: '和田地区',
pid: 24,
remark: '',
isEdit: false,
children: []
}, {
id: 89,
name: '阿勒泰州',
pid: 24,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 25,
name: '青海省',
ProSort: 26,
remark: '省份',
pid: '',
isEdit: false,
children: [{
id: 73,
name: '黄南藏族自治州',
pid: 25,
remark: '',
isEdit: false,
children: []
}]
}, {
id: 26,
name: '宁夏回族自治区',
ProSort: 30,
remark: '自治区',
pid: '',
isEdit: false,
children: [{
id: 74,
name: '银川市',
pid: 26,
remark: '',
isEdit: false,
children: []
}]
}]
}