父组件引入tree文件调用
<myTree
:dataTree="dataTree"
ref="dataTreeRef"
></myTree>
<script>
import myTree from './tree.vue'
components:{myTree}
data() {
return {
//数据结构
dataTree:[{
"id": 1,
"parentId": null,
"children": [
{
"id": 2,
"parentId": 1,
"children": [],
"name": "戴尔电脑",
"memo": "",
"leaf": "2"
},
{
"id": 6,
"parentId": 1,
"children": [],
"name": "联想",
"memo": "",
"leaf": "2"
}
],
"name": "电脑资产",
"memo": "",
"leaf": "1"
}]
}
}
</script>
这个是tree.vue文件
<template>
<div>
<div
v-for="(item,index) in tree"
:key="index"
class="tree_container"
>
<!-- 一级菜单 -->
<div
:style="'margin-left:'+ treeListIndex*20+'px'"
class="tree-item"
>
<div class="block_">
<div
class="tree-item-name"
@click="select(item, index)"
>
<span class="tree-item-title">{{item.name}}</span>
</div>
<template>
<img
v-if="item.checked === 1"
@click="select(item, index)"
src="@/assets/images/choice.png"
class="check-box"
/>
<img
v-if="item.checked === 0"
@click="select(item, index)"
src="@/assets/images/unchoice.png"
class="check-box"
/>
<img
v-if="item.checked === -1"
@click="select(item, index)"
src="@/assets/images/unfullChoice.png"
class="check-box"
/>
</template>
</div>
<div
class="tree-item-onOff"
v-if="item.children && item.children.length > 0"
@click="isOpen(item,index)"
>
<van-icon
name="arrow-down"
:class="item.open ? 'expand' : 'collapse'"
/>
</div>
<div
class="tree-item-onOff"
v-else
>
</div>
</div>
<!-- 二级菜单 -->
<categoryTree
v-if="item.children && item.children.length > 0 && item.open"
:dataTree='item.children'
:treeListIndex="treeListIndex + 1"
@backTopComponent="backTopComponent"
/>
</div>
</div>
</template>
<script>
export default {
name: 'categoryTree',
props: {
dataTree: {
type: Array,
default: () => []
},
treeListIndex: { // 当期树形列表的索引
type: Number,
default: 0
},
isOpenAll: { // 是否展开全部节点
type: Boolean,
default: false
}
},
watch: {
dataTree: {
handler: function (v) {
this.tree = JSON.parse(JSON.stringify(v))
},
deep: true
}
},
created() {
// 处理数据
this.tree = JSON.parse(JSON.stringify(this.packageData(this.dataTree)))
},
data() {
return {
tree: [],
allChoiceIdList: [] // 所有选中的id数组
}
},
methods: {
// 包装数据
packageData(tree, idx = null) {
if (this.treeListIndex != 0) return tree
return tree.map((its, index) => {
its.open = false
its.checked = 0
its.ancestors = idx == null ? String(index) : idx + '-' + index // 路径
its.children = this.packageData(its.children, its.ancestors)
return its
})
},
/**
* 下级开关按钮
* @param {*} item
* 数据对象
*/
isOpen(item) {
// 路径
let ancestors = item.ancestors.split('-')
if (this.treeListIndex != 0) {
this.$emit('backTopComponent', ancestors, 'isOpen')
} else {
item.open = !item.open
}
},
/**
* 修改状态
* @param {*} item
* 数据对象
*/
select(item) {
// 路径
let ancestors = item.ancestors.split('-')
if (this.treeListIndex != 0) {
this.$emit('backTopComponent', ancestors, 'select')
} else {
if (item.checked === 0 || item.checked === -1) item.checked = 1
else if (item.checked === 1) item.checked = 0
// 子数据修改状态
for (let i = 0; i < item.children.length; i++) {
this.handleChildsData(item.children[i], item.checked)
}
// 子数据调用
this.handleParentData(ancestors)
}
this.allChoiceIdList = this.getAllChoiceId(this.tree)
},
// 处理子数据
handleChildsData(item, checked) {
item.checked = checked
for (let i = 0; i < item.children.length; i++) {
this.handleChildsData(item.children[i], checked)
}
},
/**
* 处理父数据
* 子数据状态修改后 父数据状态需要联动
* @param {*} ancestors
*/
handleParentData(ancestors) {
let treeCopy = this.tree
for (let i = 0; i < (ancestors.length - 1); i++) {
// 获取操作的对象
if (i == 0) treeCopy = treeCopy[ancestors[i]]
else treeCopy = treeCopy.children[ancestors[i]]
/**
* 判断是否已选
* check: 1 -> -1
*/
if (treeCopy.checked == 1) treeCopy.checked = -1
}
// 检索所有上级 更新状态
this.retrievalAllParent(ancestors, ancestors.length - 1)
},
/**
* 检索所有上级 更新状态
* @param {*} ancestors
* 数据路径 ['0', '0', '0'] -> tree[0][0][0]
* @param {*} leng
* 只检索最后一个, 每次调用 leng - 1, 去掉之前检索过的
*/
retrievalAllParent(ancestors, leng) {
let lock = false,
treeCopy = this.tree
for (let i = 0; i < leng; i++) {
// 获取操作的对象
if (i == 0) treeCopy = treeCopy[ancestors[i]]
else treeCopy = treeCopy.children[ancestors[i]]
// 判断子数据是否全部未选
let isNotCheck = !treeCopy.children.some(its => its.checked == 1)
if (isNotCheck) treeCopy.checked = 0
else {
treeCopy.checked = -1
lock = true
}
// 判断子数据是否全部已选
let isAllCheck = treeCopy.children.filter(its => its.checked == 1 && its)
if (isAllCheck.length == treeCopy.children.length) {
treeCopy.checked = 1
lock = true
}
}
if (lock) this.retrievalAllParent(ancestors, leng - 1)
},
/**
* 回到顶级组件
* @param {*} ancestors
* 数据路径
* @param {*} methods
* select: 修改状态
* isOpen: 下级开关
*/
backTopComponent(ancestors, methods) {
if (this.treeListIndex != 0) {
this.$emit('backTopComponent', ancestors, methods)
} else {
// 将路径拼接 获取需要操作的对象
let treeCopy = this.tree
for (let i = 0; i < ancestors.length; i++) {
// 获取操作的对象
if (i == 0) treeCopy = treeCopy[ancestors[i]]
else treeCopy = treeCopy.children[ancestors[i]]
}
this[methods](treeCopy)
}
},
// 获取所有选中的节点id
getAllChoiceId(nodes, res = []) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].checked === 1) {
var obj = {}
obj.id = nodes[i].id
obj.name = nodes[i].name
res.push(obj)
}
if (nodes[i].children && nodes[i].children.length > 0) this.getAllChoiceId(nodes[i].children, res)
}
return res
}
}
}
</script>
<style scoped>
.tree_container {
width: auto;
box-sizing: border-box;
background: #fff;
position: relative;
}
.tree-item {
width: auto;
box-sizing: border-box;
padding: 9px 0;
display: flex;
justify-content: flex-start;
align-items: center;
border-bottom: 1px solid #cdcdcd;
}
.block_ {
width: 100% !important;
display: contents;
margin-right: 30px;
}
.tree-item-name {
display: flex;
justify-content: flex-start;
align-items: center;
flex: 8;
width: 100%;
}
.tree-item-title {
margin-left: 12px;
color: #1c2438;
font-size: 16px;
word-break: break-all;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 200px;
}
.tree-item-onOff {
width: 20px;
height: 20px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 30px;
}
.collapse {
width: 16px;
height: 10px;
transform: rotate(-90deg);
}
.expand {
width: 16px;
height: 10px;
}
.check-box {
height: 16px !important;
width: 16px !important;
margin-right: 30px !important;
}
.tree-item-name-select {
color: #0079fe;
}
</style>