element树形结构新建编辑节点组件
el-tree 只要数据结构对就能直接用的组件 
功能:
1.可新建最外层一级节点
2.可编辑删除每一级节点
3.可在每一级节点下新增子节点(添加下级)
4.可拖动排序,但只能平级拖动(为了防止拖动乱了的情况)
5.可搜索
6.初始化折叠状态,逐个打开编辑后,保持当前节点打开状态
效果如图:

(附赠数据格式)
代码如下:
父组件引用:
<template>
<div class="view-tree">
<!-- 树组件 -->
<leftTree
:treeData="treeData"
@handleTreeClick="handleTreeClick"
></leftTree>
</div>
</template>
<script>
import { treeListApi } from '@/http/thematicLibrary';
import leftTree from './components/leftTree';
export default {
components: {
leftTree,
},
data() {
return {
treeData: [], // 左侧菜单树信息
orgId: '', //组织机构id
departTitle: '' //左侧状态选择的值
};
},
created() {
this.getlistList(); //获取目录
},
methods: {
//获取目录
getlistList() {
treeListApi().then(res => {
if (res.data.code === 200) {
this.treeData = res.data.data;
let route = this.$route.query.columnId;
if (route) {
this.orgId = route;
} else {
if (this.treeData.length > 0) {
this.orgId = this.treeData[0].id;
}
}
}
});
},
// 树点击事件
handleTreeClick(data) {
this.orgId = data.id;
this.departTitle = data.name;
}
}
};
</script>
树子组件:
<template>
<div class="tree-box">
<el-input
v-model.trim="searchValue"
class="input-search"
placeholder="搜索"
clearable
/>
<div class="group-wrap">
<el-button
type="info"
size="small"
@click="addEditFn('新建一级节点', null)"
>
<i class="iconfont icon_a-zu66381"></i>
新建一级节点</el-button
>
</div>
<el-tree
ref="tree"
class="filter-tree"
:data="filterList"
:props="defaultProps"
:check-strictly="true"
:default-expand-all="expand"
:filter-node-method="filterNode"
node-key="id"
:check-on-click-node="true"
:current-node-key="currentValue"
:expand-on-click-node="false"
:highlight-current="true"
@node-click="handleNodeClick"
:default-expanded-keys="keys"
draggable
@node-drop="handleDrop"
:allow-drop="allowDrop"
>
<span class="ltree-content-item" slot-scope="{ data }">
<span class="ltree-content-name">{{ data.name }}</span>
<el-popover
placement="bottom"
popper-class="thematic_popper"
trigger="click"
@show="handleStatusWarn(data.id)"
v-has="'labelManagement/labelEdit'"
>
<ul class="popover-wrap" v-if="popId == data.id">
<li @click="addEditFn('编辑', data)">编辑</li>
<li @click="deleteFn(data.id)" v-if="data.pid !== '-1'">删除</li>
<li @click="addEditFn('添加下级', data)">添加下级</li>
</ul>
<div slot="reference">
<i
slot="reference"
class="icon_a-lujing4703 iconfont ltree-content-icon"
></i>
</div>
</el-popover>
</span>
</el-tree>
<!-- 弹窗 -->
<el-dialog
:title="title"
:visible.sync="dialogVisible"
width="520px"
@open="openTagFn"
@close="closeTagFn"
>
<el-form
:model="ruleForm"
:rules="rules"
ref="ruleForm"
label-width="100px"
class="search-form"
>
<el-form-item
v-if="typeTitle == '添加下级' || ruleForm.pid != ''"
label="所属上级"
prop="pName"
class="work-item-label"
>
<el-input v-model="ruleForm.pName" disabled></el-input>
</el-form-item>
<el-form-item label="节点名称" prop="name" class="work-item-label">
<el-input
v-model="ruleForm.name"
maxlength="20"
placeholder="请输入节点名称"
></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="mini" type="default" @click="dialogVisible = false"
>取 消</el-button
>
<el-button size="mini" type="info" @click.prevent="submit('ruleForm')"
>确 定</el-button
>
</span>
</el-dialog>
</div>
</template>
<script>
import {
treeListApi,
addApi,
editApi,
delApi,
detailApi,
updateSortApi
} from '@/http/thematicLibrary';
import CircularJSON from 'circular-json';
export default {
props: {
treeData: Array
},
data() {
return {
keys: [],
defaultProps: {
children: 'children',
label: 'name',
value: 'id'
},
popId: '',
expand: false, //默认展开
searchValue: '', //搜索的
currentValue: '', //高亮的
filterList: [],
title: '', //弹窗标题
dialogVisible: false,
pidList: [], //新建一级节点列表
ruleForm: {
name: '',
id: '',
level: '',
pName: '', //父名称
pid: ''
},
typeTitle: '', //弹窗标题区别
typeFlag: 'add', //编辑还是删除区别
rules: {
name: [{ required: true, message: '请输入节点名称', trigger: 'blur' }]
}
};
},
watch: {
treeData(val) {
this.$nextTick(() => {
this.filterList = val;
if (this.filterList.length > 0) {
console.log(this.$route.query.columnId, 'columnId');
console.log(this.$route.query.departName, 'departName');
this.currentValue = this.$route.query.columnId
? this.$route.query.columnId
: this.filterList[0].id;
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.currentValue); //一定要加这个选中了否则样式没有出来
});
}
}, 500);
},
searchValue(val) {
this.$refs.tree.filter(val);
this.getFilterData();
}
},
mounted() {
this.$nextTick(() => {
setTimeout(() => {
if (this.treeData.length > 0) {
this.currentValue = this.$route.query.columnId
? this.$route.query.columnId
: this.treeData[0].id;
this.$refs.tree.setCurrentKey(this.currentValue);
}
}, 500);
});
},
methods: {
//拖动后的回调函数
handleDrop(draggingNode, dropNode, dropType, ev) {
let params = [];
if (draggingNode.data.pid == '-1') {
params = this.filterList;
} else {
this.filterList.map((v) => {
abc(v);
});
}
//循环
function abc(item) {
if (item.id == draggingNode.data.pid) {
params = item.children;
} else {
item.children.map((val) => {
abc(val);
});
}
}
updateSortApi({ thematicLibraryUpdateDTOList: params }).then((res) => {
if (res.data.code == 200) {
this.$message({
type: 'success',
message: '排序成功!'
});
this.getList();
this.expand = false;
}
});
},
//哪些不能拖动
allowDrop(draggingNode, dropNode, type) {
if (draggingNode.data.pid == dropNode.data.pid && type != 'inner') {
return true;
} else {
return false;
}
},
//获取列表树
getList() {
treeListApi().then((res) => {
if (res.data.code === 200) {
this.filterList = res.data.data;
setTimeout(() => {
let route = this.$route.query.columnId;
this.currentValue = route;
this.$refs.tree.setCurrentKey(this.currentValue);
}, 500);
}
});
},
//新增或编辑
addEditFn(type, data) {
console.log(data, 'dataaaa');
this.typeTitle = type;
this.title = type;
if (type == '添加下级') {
this.ruleForm.pName = data.name;
this.ruleForm.pid = data.id;
this.ruleForm.level = Number(data.level) + 1;
this.ruleForm.name = '';
this.ruleForm.id = '';
} else if (type == '新建一级节点') {
this.ruleForm.pName = '';
this.ruleForm.pid = '';
this.ruleForm.level = 0;
this.ruleForm.name = '';
this.ruleForm.id = '';
} else {
if (data.level == 0) {
this.ruleForm.pName = '';
this.ruleForm.pid = '';
this.ruleForm.level = 1;
this.ruleForm.name = data.name;
this.ruleForm.id = data.id;
} else {
detailApi(data.id).then((res) => {
if (res.data.code == 200) {
this.ruleForm.pName = res.data.data.pidName;
this.ruleForm.pid = res.data.data.pid;
this.ruleForm.level = Number(data.level) + 1;
this.ruleForm.name = data.name;
this.ruleForm.id = data.id;
}
});
}
}
this.dialogVisible = true;
},
// 提交
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let data = {};
if (this.typeTitle == '添加下级') {
data = {
pid: this.ruleForm.pid,
name: this.ruleForm.name,
level: this.ruleForm.level
};
addApi(data).then((res) => {
if (res.data.code == 200) {
this.$message({
type: 'success',
message: '添加成功!'
});
this.getList();
this.keys = res.data.data
this.dialogVisible = false;
}
});
} else if (this.typeTitle == '新建一级节点') {
data = {
level: '0',
pid: '-1',
name: this.ruleForm.name
};
addApi(data).then((res) => {
if (res.data.code == 200) {
this.$message({
type: 'success',
message: '添加成功!'
});
this.getList();
this.dialogVisible = false;
}
});
} else {
data = {
id: this.ruleForm.id,
name: this.ruleForm.name
};
editApi(data).then((res) => {
if (res.data.code == 200) {
this.$message({
type: 'success',
message: '修改成功!'
});
this.getList();
this.dialogVisible = false;
}
});
}
} else {
return false;
}
});
},
//打开弹窗
openTagFn() {},
//关闭
closeTagFn() {
this.$refs['ruleForm'].resetFields();
},
//关闭其他popover
handleStatusWarn(id) {
this.popId = id;
},
//点击节点
handleNodeClick(data) {
this.popId = data.id;
if (data.id === this.$route.query.columnId) return;
this.$emit('handleTreeClick', data);
this.$router.push({
query: {
columnId: data.id,
departName: data.name
}
});
},
//删除
deleteFn(id) {
this.$confirm('是否删除此条数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
delApi(id).then((res) => {
if (res.data.code === 200) {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
}
});
});
},
//过滤后的数据
getFilterData() {
if (this.searchValue) {
const rootData = this.$refs.tree.root;
if (rootData.visible) {
const childNodesStr = CircularJSON.stringify(rootData.childNodes);
const childNodes = CircularJSON.parse(childNodesStr);
this.filterData = this.recursionNodes(childNodes);
this.handleNodeClick(this.filterData[0]);
this.currentValue = this.filterData[0].id;
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.currentValue); //一定要加这个选中了否则样式没有出来
});
} else {
console.log('暂无数据');
}
}
},
//递归遍历数据
recursionNodes(childNodes) {
const nodes = childNodes;
const result = [];
for (const item of nodes) {
if (item.visible) {
result.push(item.data);
}
if (item.childNodes && item.childNodes.length) {
const tempResult = this.recursionNodes(item.childNodes);
item.data.children = tempResult;
}
}
return result;
},
//搜索
filterNode(value, data, node) {
if (!value) return true;
let parentNode = node.parent,
labels = [node.label],
level = 1;
while (level < node.level) {
labels = [...labels, parentNode.label];
parentNode = parentNode.parent;
level++;
}
return labels.some((label) => label.indexOf(value) !== -1);
}
}
};
</script>
<style lang="scss" scoped>
.input-search {
width: 280px;
margin: 5px 20px 15px 20px;
/deep/ .el-input__suffix {
margin-top: -4px;
}
}
/deep/.el-input__inner {
background: #f4f6fc;
border-radius: 6px;
color: #6b7177;
border: none;
}
.tree-box {
width: 100%;
height: calc(100% - 50px);
// 添加按钮
.group-wrap {
display: block;
padding: 0 20px !important;
margin-bottom: 10px;
box-sizing: border-box;
/deep/ .el-button {
height: 30px;
display: inline-block;
padding: 0px 11px !important;
font-size: 12px !important;
.iconfont {
font-size: 12px;
margin: 0 !important;
}
}
/deep/ .el-button:hover {
opacity: 0.8;
}
}
.el-tree {
background: #fff;
min-width: 100%;
display: inline-block !important;
box-sizing: border-box;
padding: 0 10px;
}
// 添加下级按钮
.ltree-content-item {
// width: 150px;
height: 40px;
line-height: 40px;
padding: 0 28px 0 0px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
cursor: pointer;
.ltree-content-name {
font-size: 13px;
font-family: Microsoft YaHei;
font-weight: 400;
color: #6b7177;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ltree-content-icon {
width: 20px;
color: #6b7177;
font-size: 10px;
cursor: pointer;
display: none;
}
}
.ltree-content-item:hover {
.ltree-content-name {
color: #0069ba;
}
.ltree-content-icon {
display: block;
}
}
}
::v-deep .el-tree-node__content {
height: 40px;
&:hover {
background: #f4f6fc;
.el-tree-node__label {
color: #0069ba;
}
.ltree-content-icon {
display: block;
}
}
.el-tree-node__label {
color: #6b7177;
// max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
::v-deep
.el-tree--highlight-current
.el-tree-node.is-current
> .el-tree-node__content {
background: #f4f6fc;
.el-tree-node__label {
color: #0069ba;
}
.ltree-content-name {
color: #0069ba;
}
.ltree-content-icon {
display: block;
}
}
/deep/ .el-dialog .el-form-item__content {
margin-left: 10px !important;
.el-select {
width: 100%;
}
}
.popover-wrap {
width: 70px;
border-radius: 6px;
li {
font-size: 12px;
font-family: Microsoft YaHei;
font-weight: 400;
line-height: 35px;
color: #6b7177;
background: #fff;
cursor: pointer;
padding-left: 20px;
}
li:first-child {
border-radius: 6px 6px 0 0;
}
li:last-child {
border-radius: 0 0 6px 6px;
}
li:hover {
color: #0069ba;
}
}
</style>
<style>
.thematic_popper {
width: 85px !important;
border: none;
}
.thematic_popper.el-popper {
margin-top: -2px !important;
}
.el-popper.thematic_popper {
border: 0px;
min-width: 70px;
background: #fff;
padding: 0;
border-radius: 6px !important;
transform: translateX(20px);
}
.thematic_popper .popper__arrow {
top: -7px !important;
background: transparent !important;
border: none !important;
}
.thematic_popper .popper__arrow::after {
background: transparent !important;
border: none !important;
}
</style>

浙公网安备 33010602011771号