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

news2024/9/23 15:24:53

目录

  • 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>

效果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用户需求转化为产品需求的5大注意事项

用户需求不等于产品需求&#xff0c;如果将用户需求和产品需求混为一谈&#xff0c;往往容易产生需求不完整、需求错误以及后期需求变更频繁等问题&#xff0c;那么用户需求转化为产品需求有什么注意事项&#xff1f; 1、了解需求转化落地逻辑流程 需要注意&#xff0c;用户需求…

DT水下照明学习

ocean1.timetime*.5; 水草动画 整体样子 叶子数量相关设置 植物运动相关 晃动不明显 增加这里的值 螺旋 弯曲播放&#xff0c;植物不会变直 表达式 kelp1.spiralMinsin(frame*.1)/5; 水泡 particleShape1.spriteScaleXPP particleShape1.spriteScaleYPP rand(0.1,0.5); part…

Nginx 【实战篇】 ---- Nginx 实战(购买服务器、域名、申请证书)

Nginx 【实战篇】 ---- Nginx 实战 1. 购买域名2. 购买服务器3. 服务器的基础配置和ssh连接4. 配置 LNMP 环境和防火墙配置4.1 配置 LNMP 环境4.2 修改防火墙配置 5. 将域名解析到服务器上6. 在线申请证书7. 将刚申请的证书配置到Nginx上 1. 购买域名 2. 购买服务器 域名和服务…

spring boot项目整合spring security权限认证

一、准备一个spring boot项目 1、引入基础依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.sp…

RuoYi-Vue代码常见漏洞修复

Redis未设置密码 解决方法&#xff1a; 此方法永久生效* 找到requirepass关键字&#xff0c;后面就是跟的密码&#xff0c;默认情况下是注释掉的&#xff0c;即默认不需要密码&#xff0c;如下&#xff1a;  打开注释&#xff0c;设置为自己的密码&#xff0c;重启即可 数据…

Scrum敏捷开发管理流程+scrum工具免费

Leangoo领歌它覆盖了敏捷项目研发全流程&#xff0c;包括小型团队Scrum敏捷开发&#xff0c;规模化敏捷SAFe&#xff0c;Scrum of Scrums大规模敏捷。它提供了灵活的敏捷模板和极致的协作体验&#xff0c;可以让团队快速上手&#xff0c;快速落地Scrum敏捷开发管理。 首先建立产…

06 HTTP(下)

06 HTTP&#xff08;下&#xff09; 介绍服务器如何响应请求报文&#xff0c;并将该报文发送给浏览器端。介绍一些基础API&#xff0c;然后结合流程图和代码对服务器响应请求报文进行详解。 基础API部分&#xff0c;介绍stat、mmap、iovec、writev。 流程图部分&#xff0c;描…

ssm员工管理系统

ssm员工管理系统 java员工管理系统 员工管理系统 运行环境&#xff1a; JAVA版本&#xff1a;JDK1.8 IDE类型&#xff1a;IDEA、Eclipse都可运行 数据库类型&#xff1a;MySql&#xff08;8.x版本都可&#xff09; 硬件环境&#xff1a;Windows 功能介绍&#xff1a; 1.用户…

html学习3(表格table、列表list)

1、html表格由<table>标签来定义。 <thead>用来定义表格的标题部分&#xff0c;其内部用 <th > 元素定义列的标题&#xff0c;可以使其在表格中以粗体显示&#xff0c;与普通单元格区分开来。<tbody>用来定义表格的主体部分&#xff0c;其内部用<t…

Java版知识付费免费搭建+spring cloud 前后端分离实现知识付费平台

提供职业教育、企业培训、知识付费系统搭建服务。系统功能包含&#xff1a;录播课、直播课、题库、营销、公司组织架构、员工入职培训等。 提供私有化部署&#xff0c;免费售后&#xff0c;专业技术指导&#xff0c;支持PC、APP、H5、小程序多终端同步&#xff0c;支持二次开发…

CompletableFuture 详解

目录 简单介绍 常见操作 创建 CompletableFuture new 关键字 静态工厂方法 处理异步结算的结果 简单介绍 CompletableFuture 同时实现了 Future 和 CompletionStage 接口。 public class CompletableFuture<T> implements Future<T>, CompletionStage<T…

小白到运维工程师自学之路 第六十集 (docker的概述与安装)

一、概述 1、客户&#xff08;老板&#xff09;-产品-开发-测试-运维项目周期不断延后&#xff0c;项目质量差。 随着云计算和DevOps生态圈的蓬勃发展&#xff0c;产生了大量优秀的系统和软件。软件开发人员可以自由选择各种软件应用环境。但同时带来的问题就是需要维护一个非…

HTTP——HTTP报文内的HTTP信息

HTTP 通信过程包括从客户端发往服务器端的请求及从服务器端返回客户端的响应。本章就让我们来了解一下请求和响应是怎样运作的。 HTTP 一、HTTP报文二、请求报文及响应报文的结构三、编码提升传输速率1、报文主体和实体主题的差异2、压缩传输的内容编码3、分割发送的分块传输编…

【数据分享】1999—2021年地级市地区生产总值及一二三产构成数据(Shp/Excel格式)

在之前的文章中&#xff0c;我们分享过基于2000-2022年《中国城市统计年鉴》整理的1999-2021年地级市的人口相关数据、各类用地面积数据、污染物排放和环境治理相关数据、房地产投资情况和商品房销售面积、社会消费品零售总额和年末金融机构存贷款余额、地方一般公共预算收支状…

ABeam China Global | 探索赞比亚:非洲市场商机、产业发展与劳动挑战(下)

前情回顾 ABeam China Global | 探索赞比亚&#xff1a;非洲市场商机、产业发展与劳动挑战&#xff08;上&#xff09; 在「探索赞比亚」系列的上期&#xff0c;我们介绍了森大集团、非洲市场概述&#xff0c;以及“SADC”这一地区经济组织。本期我们将视角聚焦到赞比亚&…

Android Studio多渠道打包

使用环境&#xff1a; Android studio 多渠道打包 使用方法&#xff1a; 1 APP下build.gradle文件 flavorDimensions "default"productFlavors {huawei {dimension "default"manifestPlaceholders [ channel:"huawei" ]}xiaomi {dimension &…

LLM微调 | Adapter: Parameter-Efficient Transfer Learning for NLP

目的&#xff1a;大模型预训练微调范式&#xff0c;微调成本高。adapter只只微调新增的小部分参数【但adapter增加了模型层数&#xff0c;引入了额外的推理延迟。】 Adapters最初来源于CV领域的《Learning multiple visual domains with residual adapters》一文&#xff0c;其…

数据库信息速递, RAFT 原生系统是未来数据流式系统的未来

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

功能测试之兼容性测试点和注意项

一&#xff1a;兼容性测试的概念&#xff1a;就是验证开发出来的程序在特定的运行环境中与特定的软件、硬件或数据相组合是否能正常运行、有无异常的测试过程。 二&#xff1a;兼容性测试的分类&#xff1a; &#xff08;1&#xff09;浏览器兼容性测试 指的是在浏览器上检查…

第四课:逻辑控制

1.分支语句 &#xff08;1&#xff09;if语句 练习 1.判断一个数字是奇数还是偶数 public static void main(String[] args) {int a 10;if (a % 2 1){System.out.println("a是奇数");}else{System.out.println("a是偶数");}} 2.判断一个年份是否为闰年…