vue2实现一个树型控件(支持展开树与checkbox勾选)
TreeItem.vue
<template>
<div class="tree-item">
<span @click="toggleExpanded" class="icon" v-show="treeNode && treeNode.children && treeNode.children.length">
<span
class="triangle"
:class="[ expanded ? 'triangle_down' : 'triangle_up']"
></span>
</span>
<span class="icon-font icon-kaiwenjianjia-shense icon-wenjianjia"></span>
<span @click="toggleExpanded">{{ treeNode.deptName }}</span>
<input class="check-item check-style" type="checkbox" v-model="treeNode.checked" @change="handleChange(treeNode)">
<div class="children" v-show="expanded">
<TreeItem v-for="childNode in treeNode.children" :key="childNode.id" :tree-node="childNode" @checkItem="handleChange"></TreeItem>
</div>
</div>
</template>
<script>
export default {
name: 'TreeItem',
props: {
// 每一项的节点数据
treeNode: {
type: Object,
required: true
}
},
data() {
return {
// 是否展开
expanded: false,
};
},
methods: {
toggleExpanded() {
this.expanded = !this.expanded;
},
handleChange(item) {
console.log('handleChange',item, "treeNode",this.treeNode);
this.setChecked(item,item.checked);
this.$emit('checkItem',item)
},
// 递归 当父集选中或者取消 联动子集
setChecked(node, checked) {
node.checked = checked;
if (node.children && node.children.length > 0) {
for (let child of node.children) {
this.setChecked(child, checked);
}
}
}
}
};
</script>
<style lang="less" scoped>
.tree-item {
position: relative;
font-size: 14px;
.check-item {
position: absolute;
top: 10px;
right: 4px;
z-index: 111;
cursor: pointer;
}
}
.icon {
width: 16px;
display: inline-block;
margin-right: 4px;
line-height: 20px;
cursor: pointer;
}
.icon-wenjianjia {
color: #ccc;
margin-right: 6px;
}
.children {
margin-left: 20px;
}
input[type="checkbox"] {
/* 未选中时的样式 */
appearance: none;
border: 1px solid transparent;
width: 14px;
height: 14px;
display: inline-block;
position: relative;
vertical-align: middle;
cursor: pointer;
background-color: #eee;
}
/* 选中时的样式 */
input[type="checkbox"]:checked {
background-color: #1bc5bd;
}
/* ✔图标 */
input[type="checkbox"]:checked:after {
content: "✔";
position: absolute;
left: 1px;
top: -11px;
font-size: 12px;
color: #fff;
}
.triangle {
position: relative;
top: -4px;
transition: 0.5s;
}
.triangle_up {
display: inline-block;
margin: 0px;
width: 0px;
height: 0px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #ccc;
}
.triangle_down {
display: inline-block;
margin: 0px;
width: 0px;
height: 0px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #ccc;
}
</style>
Tree.vue
<template>
<div class="select-tree-com">
<TreeItem
class="tree-item" v-for="treeNode in treeData" :key="treeNode.id" :tree-node="treeNode"
@checkItem="checkItem"
></TreeItem>
</div>
</template>
<script>
import TreeItem from "./TreeItem"
export default {
name:'SelectTreeCom',
components:{
TreeItem
},
props: {
// 结构数据
lists: {
type: Array,
default () {
return []
}
},
// 是否开启checkbox
checkbox: {
type: Boolean,
default: false
},
},
data() {
return {
treeData: [
{
id: 1,
name: 'Node 1',
deptCode:1,
deptName:'Node-1',
checked: false,
children: [
{
id: 11,
deptCode:11,
deptName:'Node-11',
parentId: 1,
name: 'Node 11',
checked: false,
children: [
{
id: 111,
deptName:'Node-111',
deptCode:111,
parentId: 11,
name: 'Node 111',
checked: false,
children: [
{
id: 1111,
deptName:'Node-1111',
deptCode:1111,
parentId: 111,
name: 'Node 1111',
checked: false,
children: []
},
{
id: 1112,
deptName:'Node-1112',
deptCode:1112,
parentId: 111,
name: 'Node 1112',
checked: false,
children: []
}
]
},
{
id: 112,
deptName:'Node-112',
deptCode:112,
parentId: 11,
name: 'Node 112',
checked: false,
children: []
}
]
},
{
id: 12,
deptName:'Node-12',
deptCode:12,
parentId: 1,
name: 'Node 12',
checked: false,
children: []
},
{
id: 13,
deptName:'Node-13',
deptCode:13,
parentId: 1,
name: 'Node 13',
checked: false,
children: [
{
id: 131,
deptName:'Node-131',
deptCode:131,
parentId: 13,
name: 'Node 131',
checked: false,
children: [
{
id: 1311,
deptName:'Node-1311',
deptCode:1311,
parentId: 131,
name: 'Node 1311',
checked: false,
children: []
},
{
id: 1312,
deptName:'Node-1312',
deptCode:1312,
parentId: 131,
name: 'Node 1312',
checked: false,
children: []
}
]
},
{
id: 132,
deptName:'Node-132',
deptCode:132,
parentId: 13,
name: 'Node 132',
checked: false,
children: []
}
]
},
]
},
{
id:2,
deptName:'Node-2',
deptCode:2,
name: 'Node 2',
checked: false,
children: []
}
],
// treeData: [],
// 选中的所有项 check为true
checkList:[],
};
},
watch:{
lists:{
handler(newV){
console.log('selectTreeeCom组件lists',newV);
// this.treeData = [...newV]
},
// immediate: true
}
},
created() {
},
methods: {
// 拿到当前选中的所有item数据
checkItem(item) {
// console.log('selectcom-checkItem',item);
let newArr = []
newArr = this.flattenNodes(item)
console.log('newArr',newArr);
// 存储选中的!
newArr && newArr.length && newArr.forEach(item => {
if ( item.checked ) {
this.checkList.push(item)
}
});
console.log('存储选中的-this.checkList',this.checkList);
// 处理再一次选中时 包含之前的选项,覆盖之前的选项 check
this.checkList && this.checkList.length && this.checkList.forEach(itemB =>{
newArr.some(itemA => {
if ( itemA.id === itemB.id ) {
itemB.checked = itemA.checked
}
})
})
console.log('处理this.checkList',this.checkList);
// 过滤掉 check为false 得到实际选中的数据
this.checkList = this.checkList.filter(item=>{
if(item.checked) {
return item;
}
})
// console.log('res-this.checkList',this.checkList);
// 去重
let uniqueArr = []
uniqueArr = Array.from(new Set(this.checkList.map(item => item.id))).map(id => this.checkList.find(item => item.id === id));
console.log('uniqueArr',uniqueArr);
this.$emit('getCheckList', uniqueArr)
},
// 把树对象 扁平化为父+子的数据
flattenNodes(data) {
let nodes = [];
// 添加当前节点到结果数组中
nodes.push({
id: data.id,
name: data.name,
checked: data.checked,
deptCode: data.deptCode,
deptName: data.deptName
});
// 遍历子级节点并递归处理
if (data.children && data.children.length > 0) {
for (let child of data.children) {
nodes = nodes.concat(this.flattenNodes(child));
}
}
return nodes;
},
// 全选
setCheckAll(params){
// console.log('setCheckAll',params);
const allTreeData = this.treeToOneArr(this.treeData)
if ( params ) {
this.checkList = [...allTreeData]
return this.checkList
} else {
this.checkList = []
return this.checkList
}
},
// 取消全选
cancelCheckAll(){
this.checkList = []
},
// tree数据 扁平化
treeToOneArr(arr) {
const data = JSON.parse(JSON.stringify(arr))
const newData = []
const hasChildren = item => {
(item.children || (item.children = [])).map(v => {
hasChildren(v)
})
delete item.children
newData.push(item)
}
data.map(v => hasChildren(v))
return newData
},
oneArrToTree(data) {
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data))
// filter嵌套filter相当于for循环嵌套for循环
const result = cloneData.filter(parent => {
// 返回每一项的子级数组
const branchArr = cloneData.filter(child => parent.parentCode === child.parentCode)
// 若子级存在,则给子级排序;且,赋值给父级
if (branchArr.length > 0) {
branchArr.sort(this.compare('order'))
parent.children = branchArr
}
// 返回最高的父级,即,parent_id为0,
return parent.parentCode === '00'
})
// 给最高级的父级排序
result.sort(this.compare('order'))
return result
},
// 对象数组排序
compare(property) {
return function(a, b) {
const value1 = a[property]
const value2 = b[property]
return value1 - value2// 升序,降序为value2 - value1
}
},
}
};
</script>
<style lang="less" scoped>
.select-tree-com {
padding: 10px 0;
}
.tree-item {
line-height: 34px;
}
</style>
效果
文章来源地址https://www.toymoban.com/news/detail-622243.html
文章来源:https://www.toymoban.com/news/detail-622243.html
到了这里,关于vue2实现一个树型控件(支持展开树与checkbox勾选)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!