编写递归组件tree.vue
<template>
<div>
<div :style="{ marginLeft: depth +30+ 'px' }" v-for="(item,index) of list" :key="index">
<div flex="main:justify cross:center">
<div class="title">
<div @click="show(!item.show,item)" style="cursor: pointer">
<Icon v-if="item.children && item.children.length>0" type="ios-arrow-forward"
:class="item.show?'down':'right'" style="vertical-align:middle"/>
<span v-else style="display: inline-block;width: 14px"></span>
<img src="../../../assets/2.png" alt="" style="width: 14px;vertical-align:middle">
</div>
<div style="cursor: pointer" @click="checkedOpen(!item.checked,item)" :class="item.checked?'checked':''">
<span>{{item.title}}</span>
</div>
</div>
<div v-if="checkbox" class="selected" style="margin-right: 20px"
@click="select(item.selected,item.halfSelected,item)"
:class="item.halfSelected?'halfSelected':''">
<p :class="item.selected?'allSelected':''"></p>
</div>
</div>
<div v-show="item.show">
<detail-list :checkbox="checkbox" v-if="item.children && item.children.length>0" :fatherIndex="index"
:list="item.children"
:depth="depth+1" @selectFatherNode="selectFatherNode"
@checkedFatherNode="checkedFatherNode"></detail-list>
</div>
</div>
</div>
</template>
<script>
import {Icon} from 'view-design'
export default {
name: 'DetailList',//递归组件是指组件自身调用自身(被递归组件名)
props: {
list: {
type: Array,
default() {
return []
}
},
depth: {
type: Number,
default: 0
},
fatherIndex: {
type: Number,
default: 0
},
checkbox: {
type: Boolean,
default: false
}
},
computed: {
// indent() {
// return { '': `translate(${this.depth +30}px)` }
// }
},
mounted() {
console.log("this.fatherIndex", this.fatherIndex, this.list)
},
data() {
return {}
},
methods: {
//扩展
show(show, node) {
node.show = show
},
//选择
select(selected, halfSelected, node) {
node.selected = !selected
if (node.selected) {
node.halfSelected = false
}
if (node.children && node.children.length > 0) {
this.selectedChildren(node.children, node.selected, false)
}
this.$emit('selectFatherNode', this.fatherIndex, this.list.every((nodes) => {
return nodes.selected === true
}), this.list.some((nodes) => {
return nodes.selected === true
}) || this.list.some((nodes) => {
return nodes.halfSelected === true
}))
//当前选中节点、当前父节点索引、全选与否、半选与否
// console.log("this.list.every((node) => { return node.selected === node.selected })",this.list.every((node) => { return node.selected === node.selected }))
},
selectedChildren(node, selected, halfSelected) {
node.forEach(item => {
item.selected = selected
item.halfSelected = halfSelected
if (item.children && item.children.length > 0) {
this.selectedChildren(item.children, selected, halfSelected)
}
})
},
// 作为父级节点检查是否需要修改选中状态(仅用于子节点调用)
selectFatherNode(index, childrenState, halfSelected) {
if (childrenState) {//父节点全选
this.list[index].selected = true
this.list[index].halfSelected = false
this.$emit('selectFatherNode', this.fatherIndex, this.list.every((nodes) => {
return nodes.selected === true
}), this.list.some((nodes) => {
return nodes.selected === true
}) || this.list.some((nodes) => {
return nodes.halfSelected === true
}))
} else if (!childrenState && halfSelected) {//父节点半选
this.list[index].selected = false
this.list[index].halfSelected = true
this.$emit('selectFatherNode', this.fatherIndex, this.list.every((nodes) => {
return nodes.selected === true
}), this.list.some((nodes) => {
return nodes.selected === true
}) || this.list.some((nodes) => {
return nodes.halfSelected === true
}))
} else if (!childrenState && !halfSelected) {//全不选
this.list[index].selected = false
this.list[index].halfSelected = false
this.$emit('selectFatherNode', this.fatherIndex, this.list.every((nodes) => {
return nodes.selected === true
}), this.list.some((nodes) => {
return nodes.selected === true
}) || this.list.some((nodes) => {
return nodes.halfSelected === true
}))
}
},
//选中样式
checkedOpen(checked, item) {
this.checkedList(this.list)
this.$emit('checkedFatherNode')
item.checked = checked
},
checkedList(list) {
list.forEach(item => {
item.checked = false
if (item.children && item.children.length) {
this.checkedList(item.children)
}
})
},
checkedFatherNode() {
this.checkedList(this.list)
this.$emit('checkedFatherNode')
}
},
components: {
Icon
}
}
</script>
<style scoped>
.title > div {
display: inline-block;
}
.down {
transform: rotate(90deg);
transition: all 0.4s ease;
}
.right {
transition: all 0.4s ease;
}
.selected {
cursor: pointer;
}
.checked {
color: #2d8cf0;
background: #CBDCF7;
}
p {
display: inline-block;
width: 16px;
height: 16px;
position: relative;
top: 0;
left: 0;
border: 1px solid #dcdee2;
border-radius: 2px;
background-color: #fff;
transition: border-color .2s ease-in-out, background-color .2s ease-in-out, box-shadow .2s ease-in-out;
}
p.allSelected {
border-color: #2d8cf0;
background-color: #2d8cf0;
}
p.allSelected:after {
content: "";
display: table;
width: 4px;
height: 8px;
position: absolute;
top: 2px;
left: 5px;
border: 2px solid #fff;
border-top: 0;
border-left: 0;
-webkit-transform: rotate(45deg) scale(1);
transform: rotate(45deg) scale(1);
transition: all .2s ease-in-out;
}
.halfSelected > p {
background-color: #2d8cf0;
border-color: #2d8cf0;
}
.halfSelected > p:after {
content: "";
width: 10px;
height: 1px;
position: absolute;
border: 2px solid #fff;
border-top: 0;
border-left: 0;
-webkit-transform: scale(1);
transform: scale(1);
position: absolute;
left: 2px;
top: 6px;
}
</style>
引用tree.vue
<template>
<div>
<div>
<Input v-if="searchOpen" :maxlength="5" v-model="searchValue" size="large"/>
<tree :list="list3" :depth="0" :checkbox="checkbox"></tree>
</div>
</div>
</template>
<script>
import tree from './tree'
import {
Input
} from 'view-design'
export default {
name: "treeIndex",
props:{
// listData:{
// type:Array,
// default(){
// return []
// }
// },
// checkbox:{
// type:Boolean,
// default:false
// }
},
components: {
tree,Input
},
data() {
return {
checkbox:true,//是否开启复选
searchOpen:true,//是否开启搜索
searchValue: '',
list3: [],
list2: [],
list1: [{//原始数据
title: "根级菜单",
show: true,
children: [{
title: "1级菜单",
show: true,
children: [
{
title: "1级菜单-1",
// checked:true
children: [
{
title: "1级菜单-1-1",
// checked:true
},
{
title: "1级菜单-1-2",
selected: true,
}
]
},
{
title: "1级菜单-2",
children: [
{
title: "1级菜单-2-1",
// checked:true
},
{
title: "1级菜单-2-2",
selected: true,
}
]
// selected: true,
}
]
},
{
title: "3级菜单2级菜单",
children: [
{
title: "2级菜单-1",
},
{
title: "2级菜单-2",
// selected: true,
}]
},
{
title: "3级菜单",
children: [
{
title: "3级菜单-1",
// selected: true,
},
{
title: "3级菜单-2",
}]
},
{
title: "4级菜单",
}
]
}]
}
},
watch: {
searchValue() {
this.debouncedGetTableByName()
}
},
mounted() {
// console.log(this.findJieDian(this.list1, data => data.title === '3级菜单-1'))
this.debouncedGetTableByName = this.$Lodash.debounce(this.search, 500, false)
this.diguiList(this.list2,this.list1)
if(this.checkbox){
this.diGuihalfSelect(this.list2)
}
this.list3=JSON.parse(JSON.stringify(this.list2))
},
methods: {
findJieDian(list, fun, path = []) {
for (let node of list) {
path.push(node.title)
if (fun(node)) {
return path
}
if (node.children) {
let fatherArray = this.findJieDian(node.children, fun, path)
if (fatherArray.length > 0) {
return fatherArray
}
}
path.pop()
}
return []
},
//原始数据递归树
diguiList(list2, list1) {
list1.forEach((item, index) => {
list2[index] = Object.assign({}, {
show: true,
selected: false,
halfSelected: false,
checked:false,
}, item)
if (item.children && item.children.length) {
this.diguiList(list2[index].children, item.children)
}
})
},
//半选递归
diGuihalfSelect(list2){
list2.forEach(item=>{
if(item.children && item.children.length){
this.diGuihalfSelect(item.children)
item.halfSelected=item.selected?false:(item.children.some((node)=>{return node.selected==true}) || item.children.some((node)=>{return node.halfSelected==true}))
}
})
},
//选中递归
//搜索过滤树节点
search() {
this.list3=JSON.parse(JSON.stringify(this.list2))
this.productFilter(this.list3, this.searchValue)
},
productFilter(list, name) {
list.forEach(item=>{
if (item.children && item.children.length) {
this.productFilter(item.children, name);
item.children = item.children.filter(item => {
/**自己符合条件或者子级符合条件 */
if (item.title.indexOf(name) != -1 || item.has) {
return item;
}
});
/**发现子级有满足条件的,给父级添加标识,再次循环时保留父级 has:为标识字段*/
item.children.length && (item.has = true);
}
})
// const listLength = list.length;
// for (let index = 0; index < listLength; index++) {
// /**父级满足条件,子级全保留 */
// // if (list[index].title .indexOf(name)!=-1){
// // continue;
// // }
// if (list[index].children && list[index].children.length) {
// this.productFilter(list[index].children, name);
// list[index].children = list[index].children.filter(item => {
// if (item.title.indexOf(name) != -1 || item.has) {
// return item;
// }
// })
//list[index].has = true;
// }
//
// }
}
}
}
</script>
<style scoped>
</style>