系列文章目录
这是原篇教程,本篇为升级版,旧版已废弃。对你们不友好。
【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-buttontype="warning"plainsize="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赋值给textdata.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-buttontype="warning"plainsize="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赋值给textdata.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-popupv-model="showChose"closeableclose-icon="close"position="right":style="{ height: '100%', width: '100%' }"><ChoseDeptv-model="activeList"@onCancel="showChose = false"@onConfirm="onConfirmChose"></ChoseDept></van-popup>
activeList就是绑定的对象数组,具体看下面疑惑解释
解答疑惑
-
为什么getDeptList方法里面要写一个forEach循环?
因为我的res返回值,与vant所需要的对应不上,我的返回值label,是对应了element的tree组件,所以用text重新赋值。 -
computed中的filterUserList可以不封装吗?
它本质就是把一个方法的return数组,封装为变量,在全选和反选里面有用到,看起来更简洁点。 -
userListAll的作用
由于activeId是选中项id,我的父页面,需要的是对象数组。当我选中后,呈现的页面效果如下。
-
取消和确定的方法里的 $emit 是啥,没见过。
左边是方法名,右边是传递的参数。
这个是给父页面调用的,看父页面用法参数
这个v-model是什么原理?
当我的子页面调用this.$emit("input", this.activeList);
这段代码就是把值赋给input, 然后父页面只管v-model接收就好了
但是要记住,这个子页面的this.$emit("input", this.activeList);
得你自己触发调用。我的确定按钮是在子页面中的,所以我是点击了确定手动触发。
- 为啥我有了v-model还需要一个onConfirm方法呢?
你需要在父页面,关闭van-popup弹窗 - 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组件二次封装,有全选和反选。
我这里没有做主页面删除、然后子组件也跟着取消勾选。我觉得没必要、