首先上效果图:
image.png
关键代码如下:
1、首先封装成一个tree组件:
<template>
<div class="left-tree">
<el-tree
id="my-tree"
ref="tree"
class="tree-view structure-tree"
:data="treeData"
highlight-current
:default-expand-all="treeExpandAll"
:props="defaultProps"
check-strictly
:node-key="treeNodeKey"
:auto-expand-parent="false"
:expand-on-click-node="false"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<i class="el-icon-s-home" v-if="node.level == 1" style="color: #3D98F9;font-size: 16px;"></i>
<i class="el-icon-folder" v-else style="color: #3D98F9;font-size: 16px;"></i>
<span class="tooltip">
<span class="add-f-s-14" :class="{ 'level1': node.level == 1 }">{{ data.name }}</span>
</span>
<!-- v-if="node.isCurrent == true" -->
<div v-show="isHoveredNode(node)" class="hover-operations operation-view">
<el-tooltip content="增加子级节点" placement="top" effect="dark">
<i
class="small-operation-btn el-icon-plus"
@click.stop="handleAdd(data, node)"
/>
</el-tooltip>
<el-tooltip content="编辑当前节点" placement="top" effect="dark">
<i
v-show="showCurrentNode(node)"
class="small-operation-btn el-icon-edit "
@click.stop="handleEdit(data, node)"
/>
</el-tooltip>
<el-tooltip content="删除当前节点" placement="top" effect="dark">
<i
v-show="showCurrentNode(node)"
class="small-operation-btn el-icon-delete"
@click.stop="handleDelete(data, node)"
/>
</el-tooltip>
</div>
</span>
</el-tree>
</div>
</template>
<script>
export default {
props: {
// 源数据
treeData: {
type: Array,
default: function() {
return []
}
},
// 树节点是否默认展开
treeExpandAll: {
type: Boolean,
default: true
},
// 树节点唯一标识
treeNodeKey: {
type: String,
default: ''
},
},
data() {
return {
defaultProps: {
children: 'children',
label: 'name'
},
selectItem: {},
filterText: ""
}
},
watch: {
},
methods: {
isHoveredNode(node) {
console.log(node, 'node')
return true;
},
// 收藏夹不运行编辑和删除
showCurrentNode(node) {
if(node.level == 1 && node.label === '我的收藏夹') return false
else return true;
},
// 添加新增按钮
handleAdd(data) {
console.log(data, 'dataaaa')
this.$emit('addItem', data)
},
// 点击删除按钮
handleDelete(data, node) {
console.log(data, node, 'data and node')
this.$emit('deleteItem', data)
},
// 点击编辑按钮
handleEdit(data, node) {
this.selectItem = data
this.$emit('editItem', JSON.parse(JSON.stringify(data)))
},
// 添加新记录,树形列表回显
treeAddItem(data) {
this.$refs.tree.append(data, data.pid)
},
// 删除节点
treeDeleteItem(val) {
console.log(val, 'val')
this.$refs.tree.remove(val)
},
// 修改记录,树形列表回显
treeEditItem(val) {
console.log(val, 'val'); // Object.assign修改后变成val的值 val为最新值。
Object.assign(this.selectItem, val)
this.selectItem = {}
},
// 查询
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
// 点击树形节点
handleNodeClick(data,node) {
console.log(data, node, 'node and data')
}
}
}
</script>
<style lang="scss" scoped>
.structure-tree {
.el-scrollbar .el-scrollbar__wrap {
overflow-x: hidden;
}
#my-tree .el-tree > .el-tree-node {
min-width: 100%;
display: inline-block;
}
.el-tree-node__content {
margin-bottom: 10px;
position: relative;
&:hover .hover-operations { // 当此元素被悬停时,显示 .hover-operations
display: inline-block;
}
}
.tooltip {
margin-right: 5px;
font-size: 13px;
border-radius: 4px;
box-sizing: border-box;
white-space: nowrap;
padding: 4px;
}
.operation-view, .hover-operations { // 将 .hover-operations 样式与 .operation-view 合并或单独定义
display: none; // 默认隐藏
padding: 0px 5px;
margin-left: 5px;
color: #777777;
}
.small-operation-btn {
margin: 0px 3px;
color: #3D98F9;
}
}
.left-tree {
width: calc(20vw - 10px);
max-width: 700px;
padding:0;
height: 100%;
background: #fff;
border:1px solid #d2dce5;
.add-f-s-14 {
font-size: 14px;
}
.level1 {
font-weight: bold;
}
::v-deep {
background: none;
line-height: 32px;
padding: 0 5px;
font-size: 14px;
height: calc(100% - 40px);
overflow: auto;
.el-tree-node__expand-icon.expanded {
transform: rotate(0deg);
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
}
.el-tree-node__expand-icon:not(.is-leaf) {
background: #fafbff;
position: relative;
z-index: 99;
}
.el-tree-node__expand-icon.is-leaf {
&:before {
content: "";
width: 20px;
display: inline-block;
}
// &:after {
// content: "\e785";
// color: #ccc;
// }
}
.el-tree-node__content {
height: 28px;
-webkit-box-align: start;
-ms-flex-align: start;
align-items: flex-start;
position: relative;
padding-left: 0 !important;
span.el-tree-node__expand-icon {
white-space: nowrap;
color: #000;
font-size: 16px;
}
}
.el-tree-node {
position: relative;
padding-left: 16px;
&:before {
content: "";
height: 100%;
width: 1px;
position: absolute;
border-width: 1px;
border-left: 1px dashed #c1c1c1;
left: 0px;
top: 0;
}
&:after {
content: "";
width: 34px;
height: 20px;
position: absolute;
border-width: 1px;
border-top: 1px dashed #c1c1c1;
top: 13px;
left: 0;
}
}
.el-icon-caret-right {
padding-right: 0;
&:before {
content: "\e723";
margin-right: 5px;
color: #3D98F9;
}
}
.expanded.el-icon-caret-right {
&:before {
content: "\e722";
margin-right: 5px;
color: #3D98F9;
}
}
}
}
</style>
2、封装dialog,用做是curd
<template>
<div>
<el-dialog
:title="title"
:visible.sync="dialogVisible"
width="50%"
>
<el-form ref="form" :model="form" :rules="rules" label-width="100px" class="demo-form">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="描述" prop="desc">
<el-input v-model="form.desc" resize="none" type="textarea" />
</el-form-item>
<el-form-item>
<el-button class="pull-right" type="primary" @click="submitForm()">确定</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialogVisible: false,
title: '',
form: {
name: '',
desc: ''
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
desc: [
{ required: false, message: '请输入描述', trigger: 'blur' }
]
},
isEdit: false,
pid: '',
data: {}
}
},
methods: {
/**
* @description 打开弹窗
* @param { Boolean } isEdit 是否是修改状态 true 修改 / false 新增
* @param { String } pid 父级id,新增时默认选中父级时使用
* @param { Object } data 表单数据,编辑时使用
*/
openDialog(isEdit, pid, data) {
console.log(isEdit, pid, data, 'isEdit, pid, data')
this.isEdit = isEdit
this.pid = pid
this.data = data
this.title = isEdit ? '编辑' : '新增'
this.initFormData()
this.dialogVisible = true
if (this.isEdit) {
this.$nextTick(() => {
this.form = data; //编辑时赋值
})
}
},
// 初始化表单数据
initFormData() {
this.form = {
name: '',
desc: ''
}
},
// 提交表单
submitForm() {
this.$refs.form.validate((valid) => {
if (valid) {
console.log(this.form, 'this.form')
const formData = JSON.parse(JSON.stringify(this.form))
console.log(formData, 'formData')
if (this.isEdit) {
this.$emit('editData', formData)
} else {
// 设置新创建节点的父级编号
formData.pid = this.pid ? this.pid : ''
// 随机生成id(仅为前端模拟使用,正常场景应为后台生成)
formData.id = Math.random()
this.$emit('addData', formData)
}
this.closeDialog()
}
})
},
// 关闭当前弹窗
closeDialog() {
this.$refs.form.resetFields()
this.dialogVisible = false
}
}
}
</script>
<style scoped>
.pull-right {
float: right
}
</style>
3、然后做引用:
<template>
<div>
<Tree ref="customTree"
:tree-data="treeData"
:tree-expand-all="treeExpandAll"
:tree-node-key="treeNodeKey"
@addItem="addTreeItem"
@deleteItem="deleteTreeItem"
@editItem="editTreeItem"
/>
<PlaceDialog
ref="placeDialog"
@addData="addData"
@editData="editData"
/>
</div>
</template>
<script>
import Tree from '@/apps/xzbg/views/gwsc/tree.vue'
import PlaceDialog from '@/apps/xzbg/views/gwsc/placeDialog.vue'
export default {
components: {
Tree,
PlaceDialog,
},
data() {
return {
treeData: [],
treeExpandAll: true,
treeNodeKey: 'id'
};
},
created() {
this.initTreeData()
},
methods: {
// 初始化列表
initTreeData() {
// this.treeData = [
// {
// children: [
// {
// children: [],
// name: '1楼',
// desc: '这是教学楼1楼',
// parentId: '1',
// id: '2'
// },
// {
// children: [],
// name: '2楼',
// desc: '这是教学楼1楼',
// parentId: '1',
// id: '3'
// },
// {
// children: [],
// name: '3楼',
// desc: '这是教学楼3楼',
// parentId: '1',
// id: '4'
// }
// ],
// name: '教学楼',
// parentId: '',
// id: '1'
// },
// {
// children: [
// {
// children: [],
// name: '办公1楼',
// desc: '这是办公楼',
// parentId: '5',
// id: '6'
// }
// ],
// name: '办公楼',
// parentId: '',
// id: '5'
// }
// ]
this.treeData = [
{
"children": [
{
"children": [
{
"children": [],
"gwCount": "0",
"id": "88f62562122d4efab64273313090773d",
"userId": "2b9689b34dbd405b88debcbb00664327",
"pid": "82ead47871ad4fb1bc313402fc29e52d",
"name": "制度方法"
}
],
"gwCount": "1",
"id": "82ead47871ad4fb1bc313402fc29e52d",
"userId": "2b9689b34dbd405b88debcbb00664327",
"pid": "0",
"name": "数据管理"
},
{
"children": [],
"gwCount": "0",
"id": "d956dba0871441f49004ff12a768cb99",
"userId": "2b9689b34dbd405b88debcbb00664327",
"pid": "0",
"name": "总局来文"
}
],
"name": "我的收藏夹",
"pid": "",
"id": "0"
}
]
},
// 添加新记录
addNewRecord() {
this.$refs.placeDialog.openDialog(false)
},
// 新增表单数据
addData(data) {
// 新增树节点
this.$refs.customTree.treeAddItem(data)
},
// 修改表单数据
editData(data) {
// 修改树节点
this.$refs.customTree.treeEditItem(data)
},
// 增加树节点
addTreeItem(data) {
console.log(data, 'data')
// 打开地点弹窗,设置默认父级节点
this.$refs.placeDialog.openDialog(false, data.id)
},
// 删除树节点
deleteTreeItem(data) {
this.$confirm('确定删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 删除树节点
this.$refs.customTree.treeDeleteItem(data)
// 提示
this.$message({
type: 'success',
message: '删除成功!'
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
},
// 修改树节点
editTreeItem(data) {
console.log(data, 'editTreeItem')
// 打开地点编辑弹窗
this.$refs.placeDialog.openDialog(true, data.pid, data)
}
},
};
</script>
<style scoped>
</style>
所用到的数据格式如下:
this.treeData = [
{
"children": [
{
"children": [
{
"children": [],
"gwCount": "0",
"id": "88f62562122d4efab64273313090773d",
"userId": "2b9689b34dbd405b88debcbb00664327",
"pid": "82ead47871ad4fb1bc313402fc29e52d",
"name": "制度方法"
}
],
"gwCount": "1",
"id": "82ead47871ad4fb1bc313402fc29e52d",
"userId": "2b9689b34dbd405b88debcbb00664327",
"pid": "0",
"name": "数据管理"
},
{
"children": [],
"gwCount": "0",
"id": "d956dba0871441f49004ff12a768cb99",
"userId": "2b9689b34dbd405b88debcbb00664327",
"pid": "0",
"name": "总局来文"
}
],
"name": "我的收藏夹",
"pid": "",
"id": "0"
}
]