vue2实现一个树型控件(支持展开树与checkbox勾选)

目录

  • vue2实现一个树型控件(支持展开树与checkbox勾选)
    • TreeItem.vue
    • Tree.vue
    • 效果

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>

效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/54524.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VMware vCenter Server Appliance VCSA 备份还原

vCenter是VMware管理员的常备工具&#xff0c;要保护它的安全&#xff0c;我们可以借助vCenter备份还原方式来达成目的。 怎么备份vCenter 7.0&#xff1f; vCenter备份包括vCenter Server核心配置、资源清册和历史数据&#xff0c;如统计信息、事件和任务。接下来&#xff0…

力扣SQL之路:窗口函数应用

文章目录 1.引言2.力扣SQL题目3. 解题策略4.代码实现5.总结 1.引言 窗口函数是 SQL 中一种强大的分析函数&#xff0c;它可以在结果集中创建一个窗口&#xff0c;并对窗口内的数据进行计算和分析。在力扣&#xff08;LeetCode&#xff09;的 SQL 题目中&#xff0c;窗口函数经…

5分钟快手入门laravel邮件通知

第一步&#xff1a; 生成一个邮件发送对象 php artisan make:mail TestMail 第二步&#xff1a; 编辑.env 添加/修改&#xff08;没有的key则添加&#xff09; MAIL_DRIVERsmtp MAIL_HOSTsmtp.163.com &#xff08;这里用163邮箱&#xff09; MAIL_PORT25 &#xff08;163邮箱…

【LeetCode】160.相交链表

题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结…

Istio 安全 授权管理AuthorizationPolicy

这个和cka考试里面的网络策略是类似的。它是可以实现更加细颗粒度限制的。 本质其实就是设置谁可以访问&#xff0c;谁不可以访问。默认命名空间是没有AuthorizationPolicy---允许所有的客户端访问。 这里是没有指定应用到谁上面去&#xff0c;有没有指定使用哪些客户端&#…

细讲TCP三次握手四次挥手(二)

TCP/IP 协议族 应用层 应用层( application-layer &#xff09;的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程&#xff08;进程&#xff1a;主机中正在运行的程序&#xff09;间的通信和交互的规则。 对于不同的网络应用需要不同的应用层协议…

Keepalived 在CentOS 7安装并配置监听MySQL双主

keepalived安装 MySQL双主配置请看这里&#xff1a;https://tongyao.blog.csdn.net/article/details/132016200?spm1001.2014.3001.5502 128、129两台服务器安装步骤相同&#xff0c;配置文件不同&#xff0c;下面有介绍。 1.安装相关依赖包&#xff0c;并下载keepalived安…

pycharm bash: 第 0 行: cd: xxxxxxx: 没有那个文件或目录

设置里面的python接口&#xff0c;path mappings 是空的&#xff0c;设置好本地机器和远程机器所对应的目录就好了。如下图:

【C++】模板进阶(模板的特化,非类型模板参数,模板的分离编译)

文章目录 一、模板使用时一定要加typename的情况二、 非类型模板参数三、模板的特化1.函数模板特化2.类模板特化1.全特化&#xff1a;2. 偏特化&#xff1a;1. 部分特化2.参数更一步限制 四、模板的分离编译1.Stack.h2.Stack.cpp(定义)3.test.cpp 一、模板使用时一定要加typena…

【虚拟数字人】SadTalker简易部署教程

视频教程在这里&#xff1a; sadtalker数字人创建简易教程 项目基于SadTalkers实现视频唇形合成的Wav2lip。通过以视频文件方式进行语音驱动生成唇形&#xff0c;设置面部区域可配置的增强方式进行合成唇形&#xff08;人脸&#xff09;区域画面增强&#xff0c;提高生成唇形的…

2023年【零声教育】13代C/C++Linux服务器开发高级架构师课程体系分析

对于零声教育的C/CLinux服务器高级架构师的课程到2022目前已经迭代到13代了&#xff0c;像之前小编也总结过&#xff0c;但是课程每期都有做一定的更新&#xff0c;也是为了更好的完善课程跟上目前互联网大厂的岗位技术需求&#xff0c;之前课程里面也包含了一些小的分支&#…

工作记录------单元测试(持续更新)

工作记录------单元测试 之前的工作中从来没有写过单元测试&#xff0c;新入职公司要求写单元测试&#xff0c; 个人觉得&#xff0c;作为程序员单元测试还是必须会写的 于此记录一下首次编写单元测试的过程。 首先引入单元测试相关的依赖 <dependency><groupId>…

【Python目标识别】Labelimg标记深度学习(YOLO)样本

人工智能、ai、深度学习已经火了很长一段时间了&#xff0c;但是还有很多小伙伴没有接触到这个行业&#xff0c;但大家应该多多少少听过&#xff0c;网上有些兼职就是拿电脑拉拉框、数据标注啥的&#xff0c;其实这就是在标记样本&#xff0c;供计算机去学习。所以今天跟大家分…

金融翻译难吗,如何做好金融翻译?

我们知道&#xff0c;金融翻译涉及企业经济这块的&#xff0c;是影响各公司发展很重要的一方面&#xff0c;翻译做得好&#xff0c;可以促进公司内外的交流&#xff0c;及时掌握各种信息&#xff0c;做好应对。那么&#xff0c;金融翻译难吗&#xff0c;如何做好金融翻译&#…

Vue2面试题

1. Vue 的基本原理 当 一 个 Vue 实 例 创 建 时 &#xff0c; Vue 会 遍 历 data 中 的 属 性 &#xff0c; 用 Object.defineProperty &#xff08; vue3.0 使 用 proxy&#xff09; 将 它 们 转 为 getter/setter&#xff0c;并且在内部追踪相关依赖&#xff0c;在属性被访…

Chrome浏览器中的vue插件devtools的下载方式(使用Chrome应用商店/科学上网情况下)

目录 devtools对前端来说的好处——开发预览、远程调试、性能调优、Bug跟踪、断点调试等 下载步骤&#xff1a; 测试阶段&#xff1a; 最近做项目要使用devtools这个vue插件。 devtools对前端来说的好处——开发预览、远程调试、性能调优、Bug跟踪、断点调试等 下载步骤…

opencv+ffmpeg环境(ubuntu)搭建全面详解

一.先讲讲opencv和ffmpeg之间的关系 1.1它们之间的联系 我们知道opencv主要是用来做图像处理的&#xff0c;但也包含视频解码的功能&#xff0c;而在视频解码部分的功能opencv是使用了ffmpeg。所以它们都是可以处理图像和视频的编解码&#xff0c;我个人感觉两个的侧重点不一…

第六篇:什么是Prometheus Operator

Prometheus Operator简介 Prometheus Operator 是 CoreOS 开发的基于 Prometheus 的 Kubernetes 监控方案&#xff0c;也是目前功能最全面的开源方案。 Prometheus是一个开源的系统监控和报警系统&#xff0c;现在已经加入到CNCF基金会&#xff0c;成为继k8s之后第二个在CNCF…

React组件进阶之children属性,props校验与默认值以及静态属性static

React组件进阶之children属性,props校验与默认值以及静态属性static 一、children属性二、props校验2.1 props说明2.2 prop-types的安装2.3 props校验规则2.4 props默认值 三、静态属性static 一、children属性 children 属性&#xff1a;表示该组件的子节点&#xff0c;只要组…

Your local changes to the following files would be overwritten by checkout

Git 之 Your local changes to the following files would be overwritten by checkout 今天在切换分支时遇到了这样一个问题&#xff1a; 首先翻译下&#xff1a; Your local changes to the following files would be overwritten by checkout 大致意思就是&#xff1a; 当…