【Vue】vant2使用van-tree-select实现【全选、反选、搜索】,自定义组件,拿去即用。2.0版本保姆级教程

news2024/11/25 19:34:16

系列文章目录

这是原篇教程,本篇为升级版,旧版已废弃。对你们不友好。
【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重


文章目录

  • 系列文章目录
  • 前言
  • 一、写在前面
  • 二、使用步骤
    • 1.html代码
    • 2.data变量
    • 3. filters过滤器
    • 4.compted计算属性
    • 5. methods方法
    • **`全选、反选、过滤、取消和确定,都不要动它。`**
    • 6.父页面用法引入
  • 解答疑惑
  • 总结


前言

由于项目又多了新功能,我发现之前写的van-tree-select全选反选组件,不友好。得改进一下。
效果如下。

van-tree-select实现全选反选组件效果实战案例


全部代码在总结,需要适当修改一点东西。就是获取列表那。不一定你的字段跟我的一样

一、写在前面

我直接把代码全部贴出来。你们其实只需要改接口方法就好了。

后面我会一步步讲解

<!--
 * @Author: guo-bomin 2974463764@qq.com
 * @Date: 2023-11-07 16:38:51
 * @LastEditors: guo-bomin 2974463764@qq.com
 * @LastEditTime: 2023-11-07 17:51:03
 * @FilePath: \wuzhihua-app\src\components\ChoseDept\index.vue
 * @Description: 郭博民16670506200
 * 有问题微信同号询问,急事call我
 * Copyright (c) 2023 by 湖南習羽科技网络有限公司, All Rights Reserved. 
-->

<template>
  <div class="showUser">
    <h4 @click="getDeptList">请选择部门</h4>
    <van-search v-model="searchName" :placeholder="`请输入`" />
    <van-loading size="24px" v-if="list.length == 0">加载中...</van-loading>
    <div class="submit">
      <van-button type="info" plain size="small" @click="onSelectAll"
        >全选</van-button
      >
      <van-button
        type="warning"
        plain
        size="small"
        color="#999"
        @click="onClearAll"
        >取消全选</van-button
      >
    </div>
    <van-tree-select
      :items="list | newUserList(searchName)"
      :active-id.sync="activeId"
      :main-active-index.sync="activeIndex"
    />
    <div class="submit">
      <van-button type="default" @click="onCancel">取消</van-button>
      <van-button type="info" @click="onConfirm">确定</van-button>
    </div>
  </div>
</template>

<script>
import { mainDepts } from "@/api/flow/common.js";

export default {
  name: "vinit",
  components: {},
  props: {},
  data() {
    return {
      activeIndex: 0, // 左侧《下标
      activeId: [], //右侧》列表选中项 ids数组
      searchName: "", // 搜索过滤
      list: [], // ----------------待选列表, 部门+人员子级 children嵌套
      flowList: [], // 正在处理人员,用于禁选

      userListAll: [], // 所有子级项数组,用来筛选
      mainDept: null, // 当前用户部门信息
    };
  },
  computed: {
    // 用来返回到父页面
    activeList() {
      let selectedData = [];
      if (Array.isArray(this.activeId)) {
        selectedData = this.activeId.map((id) => {
          // 通过 id 查找对应的数据
          return this.userListAll.find((data) => data.id == id);
        });
      }
      return selectedData;
    },
    // 过滤后的右侧人员列表
    filterUserList() {
      return this.filterNewUserList(this.list, this.searchName);
    },
  },
  filters: {
    // 过滤选择人员
    newUserList(list, searchName) {
      let arr = [];
      if (searchName != "") {
        list.forEach((item1, index1) => {
          arr.push({
            text: item1.text,
            children: [],
          });
          item1.children.forEach((item2) => {
            if (item2.text.toLowerCase().includes(searchName.toLowerCase())) {
              arr[index1].children.push({
                id: item2.id,
                disabled: item2.disabled,
                text: item2.text,
              });
            }
          });
        });
        return arr;
      } else {
        return list;
      }
    },
  },
  watch: {},
  created() {},
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.getDeptList(); // 获取部门列表
    },
    // 全选
    onSelectAll() {
      const currentChildren =
        this.filterUserList[this.activeIndex]?.children || [];
      const selectedIdsSet = new Set(this.activeId);

      currentChildren.forEach((item) => {
        if (!item.disabled) {
          selectedIdsSet.add(item.id);
        }
      });
      this.activeId = Array.from(selectedIdsSet);
    },
    // 清空当前页全选
    onClearAll() {
      const currentChildren =
        this.filterUserList[this.activeIndex]?.children || [];
      const selectedIdsSet = new Set(this.activeId);
      currentChildren.forEach((item) => {
        selectedIdsSet.delete(item.id);
      });
      this.activeId = Array.from(selectedIdsSet);
    },
    // 取消
    onCancel() {
      this.$emit("onCancel");
    },
    // 确定
    onConfirm() {
      this.$emit("input", this.activeList);
      this.$emit("onConfirm", this.activeList);
    },
    // 获取部门列表
    getDeptList() {
      mainDepts().then((res) => {
        console.log(`res -->`, logText(res));
        let allData = {
          id: "-1",
          text: "全部",
          children: [],
        };
        let data = res.data;
        // 将label赋值给text
        data.forEach((item) => {
          item.text = item.label;
          if (item.children) {
            item.children.forEach((child) => {
              child.text = child.label;
              allData.children.push(child);
              this.userListAll.push(child);
            });
          }
        });
        data.unshift(allData);
        this.list = data;
      });
    },
    // 搭配过滤使用
    filterNewUserList(list, searchName) {
      let arr = [];
      if (searchName !== "") {
        list.forEach((item1, index1) => {
          arr.push({
            text: item1.text,
            children: [],
          });
          item1.children.forEach((item2) => {
            if (item2.text.toLowerCase().includes(searchName.toLowerCase())) {
              arr[index1].children.push({
                id: item2.id,
                disabled: item2.disabled,
                text: item2.text,
              });
            }
          });
        });
        return arr;
      } else {
        return list;
      }
    },
  },
};
</script>

<style scoped lang="less">
.showUser {
  height: 100vh;
  box-sizing: border-box;
  padding: 30px 0;

  h4 {
    margin-bottom: 30px;
    padding-left: 30px;
    font-size: 30px;
  }

  // 选择器
  .van-tree-select {
    margin-top: 20px;
    height: 70% !important;

    .van-sidebar-item--select::before {
      background-color: #418af1;
    }

    .van-tree-select__item--active {
      color: #418af1;
    }
  }

  .submit {
    width: 100%;
    display: flex;
    justify-content: space-around;
    margin: 20px 0;

    .van-button {
      width: 150px;
    }
  }
}
</style>

注意,我的接口方法是对原来的返回数组进行了修改

在这里插入图片描述
在这里插入图片描述

van-tree-select是需要text、id、children

二、使用步骤

1.html代码

代码如下(示例):

<template>
  <div class="showUser">
    <h4 @click="getDeptList">请选择部门</h4>
    <van-search v-model="searchName" :placeholder="`请输入`" />
    <van-loading size="24px" v-if="list.length == 0">加载中...</van-loading>
    <div class="submit">
      <van-button type="info" plain size="small" @click="onSelectAll"
        >全选</van-button
      >
      <van-button
        type="warning"
        plain
        size="small"
        color="#999"
        @click="onClearAll"
        >取消全选</van-button
      >
    </div>
    <van-tree-select
      :items="list | newUserList(searchName)"
      :active-id.sync="activeId"
      :main-active-index.sync="activeIndex"
    />
    <div class="submit">
      <van-button type="default" @click="onCancel">取消</van-button>
      <van-button type="info" @click="onConfirm">确定</van-button>
    </div>
  </div>
</template>

2.data变量

代码如下(示例):

export default {
  name: "vinit",
  components: {},
  props: {},
  data() {
    return {
      activeIndex: 0, // 左侧《下标
      activeId: [], //右侧》列表选中项 ids数组
      searchName: "", // 搜索过滤
      list: [], // ----------------待选列表, 部门+人员子级 children嵌套
      userListAll: [], // 所有子级项数组,用来筛选
    };
  },

3. filters过滤器

由于,我有一个搜索输入框,我需要过滤的。
在这里插入图片描述
并且页面中渲染,也不能直接用list,不然起不到过滤的作用。
过滤器时传了一个参数,我不知道为啥,在filters中不能用this.searchName,反正我就写到过滤器里面了。其实拿出来,丢到methods里面效果一样。

避免methods复杂,我这里就归类到filters中了

filters: {
  // 过滤选择人员
  newUserList(list, searchName) {
    let arr = [];
    if (searchName != "") {
      list.forEach((item1, index1) => {
        arr.push({
          text: item1.text,
          children: [],
        });
        item1.children.forEach((item2) => {
          if (item2.text.toLowerCase().includes(searchName.toLowerCase())) {
            arr[index1].children.push({
              id: item2.id,
              disabled: item2.disabled,
              text: item2.text,
            });
          }
        });
      });
      return arr;
    } else {
      return list;
    }
  },
},

4.compted计算属性

  • 我需要计算,选中的人员列表,当然你会说,不是有选中项数组吗?
  • 可那个是id数组,我需要的是对象数组,我除了id,我还需要它的text值。用作回显。
  computed: {
    // 用来返回到父页面
    activeList() {
      let selectedData = [];
      if (Array.isArray(this.activeId)) {
        selectedData = this.activeId.map((id) => {
          // 通过 id 查找对应的数据
          return this.userListAll.find((data) => data.id == id);
        });
      }
      return selectedData;
    },
    // 过滤后的右侧人员列表
    filterUserList() {
      return this.filterNewUserList(this.list, this.searchName);
    },
  },

filterUserList 这个是列表中右侧的人员列表
在这里插入图片描述
它搭配了一个filterNewUserList使用,它的作用是啥呢?

过滤右侧、通过搜索。

假设右侧有10个数据,我搜索输入之后,只有5个,当我点击全选,我需要选中的是这5个,不是10个,所以它就是这个作用,但是它是个方法,为了方便,把它丢到computed里面。作为一个变量,有利于后面的全选反选方法。

5. methods方法

methods: {
    init() {
      this.getDeptList(); // 获取部门列表
    },
    // 全选
    onSelectAll() {
      const currentChildren =
        this.filterUserList[this.activeIndex]?.children || [];
      const selectedIdsSet = new Set(this.activeId);

      currentChildren.forEach((item) => {
        if (!item.disabled) {
          selectedIdsSet.add(item.id);
        }
      });
      this.activeId = Array.from(selectedIdsSet);
    },
    // 清空当前页全选
    onClearAll() {
      const currentChildren =
        this.filterUserList[this.activeIndex]?.children || [];
      const selectedIdsSet = new Set(this.activeId);
      currentChildren.forEach((item) => {
        selectedIdsSet.delete(item.id);
      });
      this.activeId = Array.from(selectedIdsSet);
    },
    // 取消
    onCancel() {
      this.$emit("onCancel");
    },
    // 确定
    onConfirm() {
      this.$emit("input", this.activeList);
      this.$emit("onConfirm", this.activeList);
    },
    // 获取部门列表
    getDeptList() {
      mainDepts().then((res) => {
        console.log(`res -->`, logText(res));
        let allData = {
          id: "-1",
          text: "全部",
          children: [],
        };
        let data = res.data;
        // 将label赋值给text
        data.forEach((item) => {
          item.text = item.label;
          if (item.children) {
            item.children.forEach((child) => {
              child.text = child.label;
              allData.children.push(child);
              this.userListAll.push(child);
            });
          }
        });
        data.unshift(allData);
        this.list = data;
      });
    },
    // 搭配过滤使用
    filterNewUserList(list, searchName) {
      let arr = [];
      if (searchName !== "") {
        list.forEach((item1, index1) => {
          arr.push({
            text: item1.text,
            children: [],
          });
          item1.children.forEach((item2) => {
            if (item2.text.toLowerCase().includes(searchName.toLowerCase())) {
              arr[index1].children.push({
                id: item2.id,
                disabled: item2.disabled,
                text: item2.text,
              });
            }
          });
        });
        return arr;
      } else {
        return list;
      }
    },
  },

全选、反选、过滤、取消和确定,都不要动它。

6.父页面用法引入

    <!-- 选择人员弹出框 -->
    <van-popup
      v-model="showChose"
      closeable
      close-icon="close"
      position="right"
      :style="{ height: '100%', width: '100%' }"
    >
      <ChoseDept
        v-model="activeList"
        @onCancel="showChose = false"
        @onConfirm="onConfirmChose"
      ></ChoseDept>
    </van-popup>

在这里插入图片描述
在这里插入图片描述

activeList就是绑定的对象数组,具体看下面疑惑解释

解答疑惑

  1. 为什么getDeptList方法里面要写一个forEach循环?
    因为我的res返回值,与vant所需要的对应不上,我的返回值label,是对应了element的tree组件,所以用text重新赋值。

  2. computed中的filterUserList可以不封装吗?
    它本质就是把一个方法的return数组,封装为变量,在全选和反选里面有用到,看起来更简洁点。

  3. userListAll的作用
    由于activeId是选中项id,我的父页面,需要的是对象数组。当我选中后,呈现的页面效果如下。
    在这里插入图片描述

  4. 取消和确定的方法里的 $emit 是啥,没见过。
    在这里插入图片描述
    左边是方法名,右边是传递的参数。
    这个是给父页面调用的,看父页面用法参数
    在这里插入图片描述

这个v-model是什么原理?
当我的子页面调用 this.$emit("input", this.activeList);
这段代码就是把值赋给input, 然后父页面只管v-model接收就好了

但是要记住,这个子页面的this.$emit("input", this.activeList); 得你自己触发调用。我的确定按钮是在子页面中的,所以我是点击了确定手动触发。

  1. 为啥我有了v-model还需要一个onConfirm方法呢?
    你需要在父页面,关闭van-popup弹窗
  2. logText方法是什么?为什么它打印出来的数组,没有显示…省略号
console.log("打印选择结果", logText(this.activeList));

由于数组或者对象打印,会显示小数点省略号
我封装了一个全局方法,专门用于打印,把它丢到main.js中就好了,随便你放哪一行

// 在main.js中定义全局方法
window.logText = function (value) {
  return JSON.parse(JSON.stringify(value));
};

总结

提示:这里对文章进行总结:

样式可以自由修改,我这里并没有适配的很完善。高度之类的。但是肯定,如果你也用vant组件,也是vue2项目,你可以尝试下我的这个二级选人组件。van-tree-select组件二次封装,有全选和反选。

我这里没有做主页面删除、然后子组件也跟着取消勾选。我觉得没必要、

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

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

相关文章

clickhouse.22.8.3.13单机版安装

介绍 1、clickhouse是一款优秀的开源MPP数据库。 安装ClickHouse的步骤如下&#xff1a; 2、下载clickhouse https://repo.clickhouse.tech/tgz/ 但是这个下载太慢了&#xff0c;找个国内的镜像 https://mirrors.aliyun.com/clickhouse/ 我们采用阿里云的镜像地址。 cli…

An error occurred while filtering resources

Description Path Resource Location Type An error occurred while filtering resources PMS line 1 Maven Java EE Configuration Problem不知道怎么跑出来了&#xff0c;update project 还是不行 但是不影响运行&#xff0c;奇…

记录两个Excel导出出现的问题

问题一&#xff1a;导出数据时&#xff0c;这行代码返回null&#xff0c;导致导出excel失败&#xff1b; Workbook workbook ExcelExportUtil.exportExcel(params, map);解决&#xff1a;排查出来&#xff0c;是因为版本问题&#xff0c;autopoi版本是1.2.1&#xff1b; 升级…

MCU系统的调试技巧

MCU系统的调试技巧对于确保系统稳定性和性能至关重要。无论是在嵌入式系统开发的初期阶段还是在产品维护和优化的过程中&#xff0c;有效的调试技巧可以帮助开发人员快速发现和解决问题&#xff0c;本文将讨论一些MCU系统调试的技巧。 首先&#xff0c;使用调试工具是非常重要…

小程序day05

使用npm包 Vant Weapp 类似于前端boostrap和element ui那些的样式框架。 安装过程 注意:这里建议直接去看官网的安装过程。 vant-weapp版本最好也不要指定 在项目目录里面先输入npm init -y 初始化一个包管理配置文件: package.json 使用css变量定制vant主题样式&#xff0…

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远程控制软件总结

红队专题 招募六边形战士队员[30]远控班第一期课程与远控总结 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 [30]远控班第一期课程与远控总结 一.Bug修复(1)生成路径(2)显示系统版本号二.内存泄露(1)如何检查内存泄露 #define CRTDBG_…

Modbus通讯模拟仿真环境的搭建

文章目录 一、概要二、所需工具介绍三、搭建虚拟仿真环境1.Modbus RTU虚拟仿真环境搭建1.1.虚拟串口工具&#xff08;VSPD&#xff09;使用1.2.虚拟从站工具&#xff08;ModSim32&#xff09;使用1.3.虚拟主站工具&#xff08;Modscan32&#xff09;使用1.4.更改虚拟从站工具&a…

如何处理数据集内的缺失值?

照片 奥坎耶尼贡 由Pierre Bamin在Unsplash上拍摄 一、说明 也许数据科学或机器学习问题研究中要求最高的阶段是数据预处理阶段&#xff0c;其目的是最终创建有用的数据集。如果说处理很酷的机器学习模型是阿喀琉斯的热门&#xff0c;那么数据预处理就是被诅咒的西西弗斯。…

Git的高效使用 git的基础 高级用法

Git的高效使用 git的基础 高级用法 前言 什么是Git 在日常的软件开发过程中&#xff0c;软件版本的管理都离不开使用Git&#xff0c;Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 也是Linus Torvalds为了帮助管理Linu…

认识EPLAN软件中的各种“点”

原文是网络上的一篇文章&#xff0c;内容有很多错字&#xff0c;我重新编辑了一下发出来&#xff0c;供参考。 在 EPLAN 中&#xff0c;有很多"点"&#xff0c;不同的点的具体含义各有不同&#xff0c;只有弄清楚了不同点的含义&#xff0c;在软件应用中才会得心应手…

【爬虫】Java爬虫爬取某招聘网站招聘信息

目录 前言 一、爬虫程序的基本架构 二、如何获取目标网站的页面内容 三、解析HTML页面&#xff0c;提取所需信息 四、代理IP的使用 五、完整代码 总结 前言 随着互联网的普及&#xff0c;越来越多的人开始关注网络上的招聘信息&#xff0c;而传统的求职方式愈发显得不够…

壹基金防灾减灾宣传进社区 提升家庭安全能力

11月7日&#xff0c;瑞金市赋能济困公益协会、蓝天救援队等联合沙洲坝镇红都新城社区一起走进梦想家园小区&#xff0c;开展家庭安全计划社区活动包挑战赛活动暨壹基金安全家园项目防灾减灾宣传社区行活动。 活动中&#xff0c;志愿者针对从洪涝灾害、风灾、火灾、雪灾、地质灾…

k8s:kubectl 详解

目录 1 kubectl 2 基本信息查看 2.1 查看 master 节点状态 2.2 查看命名空间 2.3 查看default命名空间的所有资源 2.4 创建命名空间app 2.5 删除命名空间app 2.6 在命名空间kube-public 创建副本控制器&#xff08;deployment&#xff09;来启动Pod&#xff08;nginx-wl…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(二)

新增员工功能开发 1. 新增员工1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.1.3 表设计 1.2 代码开发1.2.1 设计DTO类1.2.2 Controller层1.2.3 Service层接口1.2.4 Service层实现类1.2.5 Mapper层 1.3 功能测试1.3.1 接口文档测试 1.4 代码完善1.4.1 问题一1.4.2 问题二1.…

从零开始的C++(十四)

继承&#xff1a; 作用&#xff1a;减少重复代码&#xff0c;简化程序。 用法&#xff1a; class b&#xff1a;public a {//...b中成员 } 在如上代码中&#xff0c;b类以public的方式继承了a类。规定a类是父类、基类&#xff0c;b类是子类、派生类。 关于继承方式&#xf…

Tcl语言:SDC约束命令create_generated_clock详解(下)

相关阅读 Tcl语言https://blog.csdn.net/weixin_45791458/category_12488978.html?spm1001.2014.3001.5482 设定生成时钟特性 前文的末尾提到&#xff0c;当使用-divide by或-multiply_by选项创建生成时钟时&#xff0c;会根据master clock的时钟周期派生出生成时钟的周期&am…

【Java 进阶篇】Java Filter 快速入门

欢迎来到这篇有关 Java Filter 的快速入门指南&#xff01;如果你是一名 Java 开发者或者正在学习 Java Web 开发&#xff0c;Filter 是一个强大的工具&#xff0c;可以帮助你管理和控制 Web 应用程序中的请求和响应。本文将向你解释 Filter 的基本概念&#xff0c;如何创建和配…

安全认证框架Shiro入门学习(shiro概述和shiro入门小案例);后续整合SpringBoot,应用程序安全;

权限概述 什么是权限 什么是权限 权限管理&#xff0c;一般指根据系统设置的安全策略或者安全规则&#xff0c;用户可以访问而且只能访问自己被授权的资源&#xff0c;不多不少。权限管理几乎出现在任何系统里面&#xff0c;只要有用户和密码的系统。 权限管理再系统中一般分…

小米6安装Ubuntu Touch系统也不是很难嘛

序言 这个文章是用来解说,小米6如何安装Ubuntu Touch系统 正文 安装这个系统需要注意的几点 1.手机必须已经解BL锁 2.没了 安装步骤 先双击打开压缩包查看,按照第一步第二步来进行执行,下面是解压图片 第一步 1.打开第一个文件夹 复制刷入rec的命令.txt里面的内容,然后打开红…

pytorch(小土堆)深度学习

第五节课讲项目的创建和对比 第六节&#xff1a;Dataset,Dataloader Dataset提供一种方式区获取数据及其label(如何获取每一个数据及其label&#xff0c;告诉我们总共有多少的数据) Dataloader为后面的网络提供不同的数据形式 第七节&#xff1a;Dataset类代码实战 显示图片 f…