【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、页面代码
    • 1. 父页面代码
    • 2.js方法
  • 二、子组件代码
    • 1.页面
    • 2.样式代码
    • 3. js代码
    • 关键代码
    • 1.子组件变量
    • 2. 搜索过滤器
    • 3. 返回给父页面的对象数组
    • 4.确定和取消方法
    • 5.全选反选
  • 总结
  • 修改bug
    • 把list改成filterUserList就好了
  • 完整代码如下(白嫖需要删除部分)


前言

vant2的级联选择器,组件链接传送门
【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第1张图片
功能需求,全选和取消全选,只做简单版。
就是两个按钮,一个全选,一个取消全选
并且只选中 || 取消 当前显示的这个children数组


提示:以下是本篇文章正文内容,下面案例可供参考

一、页面代码

由于隐私,这里只能放静态图

【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第2张图片
这段代码,我封装成了一个components组件,只需要丢到popup弹出框

1. 父页面代码


<van-popup v-model="showChose" closeable close-icon="close" position="right"
	:style="{ height: '100%', width: '100%' }">
	
	<ChoseUser v-if="showChose" name="参会" @onCancel="showChose = false" @onConfirm="onConfirmChose">
	ChoseUser>
van-popup>

2.js方法

import ChoseUser from "@/components/ChoseUser/general.vue"
export default {
	name: "vinit",
	components: {
		ChoseUser,
	},
	data() {
		return {
			showChose:false,

获取返回值

methods:{
	// 确定选中人员
	onConfirmChose(activeUser) {
		console.log(activeUser, '选中人员')
		this.showChose = false
		// 回显参会人员
		this.userOut = activeUser.map(item => item.text).join()
	},
}

二、子组件代码

1.页面

代码如下(示例):

<template>
    <div class="showUser">
        <h4 @click="getDeptList">请选择{{ name }}人员h4>
        <van-search v-model="searchName" :placeholder="`请输入搜索的${name}人姓名`" />
        <van-loading size="24px" v-if="list.length == 0">加载中...van-loading>
        <div class="submit">
            <van-button type="danger" plain size="small" @click="onSelectAll">全选van-button>
            <van-button type="warning" plain size="small" @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.样式代码

代码如下(示例):

.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;
        }
    }

}

3. js代码

先看vant组件所支持的数据格式

【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第3张图片

它需要的是,一个对象数组,它的对象数据,就是左侧列表,children数据,就是右侧渲染的子级数据列表。
例如下列格式

testList: [
    {
        id: 1,
        text: '湖南',
        children:[{
            id:101,
            text:'长沙'
        },{
            id:102,
            text:'张家界'
        }]
    }, {
        id: 2,
        text: '广东',
        children:[{
            id:201,
            text:'深圳'
        },{
            id:202,
            text:'佛山'
        }]
    },
]

这里我的写法是,获取部门列表,然后点击部门(左侧),再请求右边人员列表
获取到人员列表后、根据当前index下标,写入到左侧对应部门的children下。代码压缩一下了。

方法大概如下,获取人员数据,按需求修改

getDeptList() {
    // 如果有传入id,则zmlk处理, 没有则通用流程处理
    let id = this.id || this.mainDept.deptId
    deptList(id).then(res => {
        let data = res.data
        data.forEach(item => {
            item.text = item.label
            item.children = []
        })
        // 添加主部门信息,可以查询全部人员
        // if (res.data.length > 1) {
        //     data.unshift({
        //         id: this.mainDept.deptId,
        //         text: this.mainDept.deptName,
        //         children: [],
        //     })
        // }
        this.list = data
    }).then(() => {
        // 获取当前用户信息所在部门,使左侧对应
        const userInfo = Cookies.get("userInfo") && JSON.parse(Cookies.get("userInfo"));
        const userDeptId = userInfo ? userInfo.deptId : null
        // 左侧选中的部门id赋值,有个人信息,赋值,没有则取第一项
        this.deptId = userDeptId || this.list[0].id

        // 设置选中部门下标为自己的部门
        let index = this.list.findIndex(item => item.id === this.deptId)
        this.activeIndex = index == -1 ? 0 : index
        this.getDeptUserList() // 调用获取部门下人员信息
    })
},
// 获取自己部门下人员列表
getDeptUserList() {
    // 如果选中部门,有人员列表,则不请求数据
    if (this.list[this.activeIndex].children.length > 0) {
        return
    }
    deptUserList(this.deptId).then(res => {
        let array = []
        res.data.filter(item => {
            array.push({
                text: item.realName,
                id: item.userName,
                deptId: item.deptId,
                userId: item.userId,
            })
        })
        // 赋值当前选中部门下children的值
        this.list[this.activeIndex].children = [...array]
        // 赋值全部人员,用于返回给父页面(对象数组)
        this.userListAll = [...this.userListAll, ...array]
    })
},

因为是一开始没有用户人员,所以我要监听器,左侧列表切换,就需要请求右边人员列表

watch: {
    // 要监听的数据,可以是数组、对象、或者普通的变量
    activeIndex: {
        // immediate: true, // 立即执行,创建页面时就执行一次
        // deep: true, // 深度监听,会监听到对象里面的属性改变
        handler(newIndex) {
            this.deptId = this.list[newIndex].id
            this.getDeptUserList()
        },
    },
},

上面代码是,获取左侧列表和右侧人员列表的方法,因为是分开获取的。
如果你的是一次性获取来的数组,那就不需要这么繁琐,虽然请求返回值变大了,可能会缓慢、

关键代码

1.子组件变量

props: {
    name: {
        type: String,
        default: "处理", // 处理||传阅, 领导||部门
    },
    // 是否多选,默认多选,
    isArray: {
        type: Boolean,
        default: true
    },
},
data() {
    return {
        activeIndex: 0, // 左侧《下标
        activeId: [], //右侧》列表选中项 ids数组
        searchName: "", // 搜索过滤
        deptId: "", // 部门id,用于查询部门人员
        list: [], // ----------------待选列表, 部门+人员子级 children嵌套
        flowList: [], // 正在处理人员,用于禁选

        userListAll: [], // 所有子级人员
        mainDept: null,// 当前用户部门信息
    }
},
created() {
    this.init()
},
mounted() {
},
methods: {
    init(){
        this.isArray ? this.activeId = [] : this.activeId = ""
        this.getDeptList() // 获取部门列表
    }
},

2. 搜索过滤器

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
        }
    }
},

3. 返回给父页面的对象数组

computed: {
    activeList() {
        let selectedData = []
        if (Array.isArray(this.activeId)) {
            console.log("计算")
            selectedData = this.activeId.map(id => {
                // 通过 id 查找对应的数据
                return this.userListAll.find(data => data.id == id);
            });
        }
        return selectedData
    },
},

4.确定和取消方法

子页面代码

// 取消
onCancel() {
    this.$emit("onCancel")
},
// 确定
onConfirm() {
    this.$emit("onConfirm", this.activeList)
},

取消方法是通过父页面去关闭
【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第4张图片

5.全选反选

全选反选只要变量一致,可以直接拿走使用。

// 全选
onSelectAll() {
    console.log('全选')
    const currentChildren = this.list[this.activeIndex]?.children || [];
    console.log(currentChildren)
    const selectedIdsSet = new Set(this.activeId);

    currentChildren.forEach(item => {
        selectedIdsSet.add(item.id);
    });

    this.activeId = Array.from(selectedIdsSet);
    console.log(this.activeId)
},
// 清空当前页全选
onClearAll() {
    const currentChildren = this.list[this.activeIndex]?.children || [];
    const selectedIdsSet = new Set(this.activeId);

    currentChildren.forEach(item => {
        selectedIdsSet.delete(item.id);
    });

    this.activeId = Array.from(selectedIdsSet);
},

总结

来看案例图
【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第5张图片

1、在部分人员列表点击全选,然后在所有人员列表页可以看到勾选中了所选人员
2、在全部人员列表全选,然后在部分人员列表取消全选。切换回全部人员,可以看到也已经取消了部分人员的选中状态。
3、过滤功能
【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第6张图片

修改bug

我的全选是获取当前选中项的所有children数据,但是
当我使用了search搜索过滤时,就会误伤。我在所有人员列表,搜索出来3个人,点击全选,结果选中了10个人。

现在修改

在methods下添加过滤方法

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;
    }
},

在computed中添加计算属性变量

filterUserList() {
    return this.filterNewUserList(this.list, this.searchName);
},

修改上面的全选方法
【Vue】vue2移动端 ,vant2使用van-tree-select分类选择实现【全选】和【取消全选】、【搜索过滤当前children】,只影响当前显示children,并且去重_第7张图片

把list改成filterUserList就好了

清空方法不动,因为真的要清空所有,当前,清空搜索出来的人,也可以,一致修改为filterUserList

完整代码如下(白嫖需要删除部分)

<template>
    <div class="showUser">
        <h4 @click="getDeptList">请选择{{ name }}人员h4>
        <van-search v-model="searchName" :placeholder="`请输入搜索的${name}人姓名`" />
        <van-loading size="24px" v-if="list.length == 0">加载中...van-loading>
        <div class="submit">
            <van-button type="danger" plain size="small" @click="onSelectAll">全选van-button>
            <van-button type="warning" plain size="small" @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 {
    deptList, // 查询部门列表 
    deptUserList, // 查询部门下的用户列表 id
} from "@/api/flow/common.js"

import {
    shouwenFormNot, // 收文未办
} from "@/api/flow/shouwen/task.js"
import {
    jobFormNot, // 任务
} from "@/api/flow/job/task.js"
import {
    sealFormNot, // 公章
} from "@/api/flow/seal/task.js"
import {
    carFormNot, // 公车
} from "@/api/flow/car/task.js"
import {
    leaveFormNot, // 请假
} from "@/api/flow/leave/task.js"

import {
    getToken
} from "@/utils/auth";
import Cookies from "js-cookie"
export default {
    name: "vinit",
    components: {

    },
    props: {
        name: {
            type: String,
            default: "处理", // 处理||传阅, 领导||部门
        },
        // 是否多选,默认多选,
        isArray: {
            type: Boolean,
            default: true
        },
        // 公文id, 有id查出正在办并禁用,没id查全部人员
        formId: {
            type: Number
        },
        // 传入部门id,6185||326, zmlk专用,没传根据自己id查当前能查询到的
        id: {
            type: String
        },
        // 选择人员类型,shouwen收文,job任务,seal公章,car公车,leave请假
        type: {
            type: String,
            default: "shouwen",
        }
    },
    data() {
        return {
            timer: null,//定时器
            activeIndex: 0, // 左侧《下标
            activeId: [], //右侧》列表选中项 ids数组
            searchName: "", // 搜索过滤
            deptId: "", // 部门id,用于查询部门人员
            list: [], // ----------------待选列表, 部门+人员子级 children嵌套
            flowList: [], // 正在处理人员,用于禁选

            userListAll: [], // 所有子级人员
            mainDept: null,// 当前用户部门信息
        }
    },
    computed: {
        activeList() {
            let selectedData = []
            if (Array.isArray(this.activeId)) {
                console.log("计算")
                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: {
        // 要监听的数据,可以是数组、对象、或者普通的变量
        activeIndex: {
            // immediate: true, // 立即执行,创建页面时就执行一次
            // deep: true, // 深度监听,会监听到对象里面的属性改变
            handler(newIndex) {
                this.deptId = this.list[newIndex].id
                this.getDeptUserList()
            },
        },
    },
    created() {
        this.mainDept = JSON.parse(Cookies.get("mainDept"))
        // 发起定时器轮询异步请求
        this.timer = setInterval(() => {
            // 执行异步请求,获取 token 的新值
            // 如果获取到了新值,则更新组件的状态
            if (typeof getToken() !== 'undefined') {
                this.init()
                // 清除定时器
                clearInterval(this.timer);
            }
        }, 30);
    },
    mounted() {
    },
    methods: {
        init() {
            this.isArray ? this.activeId = [] : this.activeId = ""
            // 公文id, 有id查出正在办并禁用,没id查全部人员
            if (this.formId) {
                switch (this.type) {
                    case "shouwen":
                        this.getShouwenList()
                        break;
                    case "job":
                        this.getJobList()
                        break;
                    case "seal":
                        this.getSealList()
                        break;
                    case "car":
                        this.getCarList()
                        break;
                    case "leave":
                        this.getLeaveList()
                        break;
                }
            } else {
                this.getDeptList() // 获取部门列表
            }
        },
        // 全选
        onSelectAll() {
            const currentChildren = this.filterUserList[this.activeIndex]?.children || [];
            console.log(currentChildren)
            const selectedIdsSet = new Set(this.activeId);

            currentChildren.forEach(item => {
                selectedIdsSet.add(item.id);
            });

            this.activeId = Array.from(selectedIdsSet);
            console.log(this.activeId)
        },
        // 清空当前页全选
        onClearAll() {
            const currentChildren = this.list[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("onConfirm", this.activeList)
        },


        // 获取部门列表
        getDeptList() {
            // 如果有传入id,则zmlk处理, 没有则通用流程处理
            let id = this.id || this.mainDept.deptId
            deptList(id).then(res => {
                let data = res.data
                // 如果传参为部门,过滤领导id
                if (this.name == "部门") {
                    data = res.data.filter(item => {
                        return item.id != 6185
                    })
                }
                data.forEach(item => {
                    item.text = item.label
                    item.children = []
                })
                // 添加主部门信息,可以查询全部人员
                if (res.data.length > 1) {
                    data.unshift({
                        id: this.mainDept.deptId,
                        text: this.mainDept.deptName,
                        children: [],
                    })
                }
                this.list = data
            }).then(() => {
                const userInfo = Cookies.get("userInfo") && JSON.parse(Cookies.get("userInfo"));
                const userDeptId = userInfo ? userInfo.deptId : null
                this.deptId = userDeptId || this.list[0].id
                console.log('当前部门id', this.deptId)
                // zmlk\通用的请求部门人员判断
                if (this.id) {
                    this.deptId = this.list[0].id // 文秘查询专用
                } else {
                    this.deptId = this.deptId // 自己的部门id
                }
                // 设置选中部门下标为自己的部门
                let index = this.list.findIndex(item => item.id === this.deptId)
                this.activeIndex = index == -1 ? 0 : index
                this.getDeptUserList()
            })
        },
        // 获取自己部门下人员列表
        getDeptUserList() {
            // 如果选中部门,有人员列表,则不请求数据
            if (this.list[this.activeIndex].children.length > 0) {
                return
            }

            deptUserList(this.deptId).then(res => {
                let array = []
                res.data.filter(item => {
                    let bool = false
                    for (const element of this.flowList) {
                        (element.userName == item.userName) && (bool = true)
                    }
                    array.push({
                        text: item.realName,
                        id: item.userName,
                        disabled: bool,
                        userName: item.userName,
                        realName: item.realName,
                        deptId: item.deptId,
                        userId: item.userId,
                    })
                })
                // 赋值当前选中部门下children的值
                this.list[this.activeIndex].children = [...array]
                // 赋值全部人员
                this.userListAll = [...this.userListAll, ...array]
            })
        },
        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;
            }
        },

        // 收文待办
        getShouwenList() {
            shouwenFormNot(this.formId).then(res => {
                this.flowList = [...res.data]
                this.getDeptList()
            })
        },
        // 任务
        getJobList() {
            jobFormNot(this.formId).then(res => {
                this.flowList = [...res.data]
                this.getDeptList()
            })
        },
        // 公章
        getSealList() {
            sealFormNot(this.formId).then(res => {
                this.flowList = [...res.data]
                this.getDeptList()
            })
        },
        // 公车
        getCarList() {
            carFormNot(this.formId).then(res => {
                this.flowList = [...res.data]
                this.getDeptList()
            })
        },
        // 请假
        getLeaveList() {
            leaveFormNot(this.formId).then(res => {
                this.flowList = [...res.data]
                this.getDeptList()
            })
        },
    },
}
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>

你可能感兴趣的:(Vue,vue.js,前端,javascript)