element-plus 树状表格 复选框勾选问题

news/2024/7/10 1:42:43 标签: vue

最近项目需要用到树形表格,咱们公司用的是 elment-plus 这个ui库暂时还不成熟,有好多bug,需要自己处理

在这里插入图片描述
注意的地方

*1,树状表格的节点都需要自己手动设置,调用 elemet-plus库抛出的方法toggleRowSelection;
*2. 表格里数据在显示的时候不能变动,不然就会出现问题,所以在此之间不能去更改每一行数据的值;
*3. 表格没有暴露出来半选状态的方法,需要自己去设置;

思路
*(1)上面说了,表格没有暴露出来半选状态的方法,所以我们要通过设置类名的方法自己设置,咱们需要用到element-plus 库抛出的属性 :row-class-name=“rowClassNameFun” ;

*(2)由于不能修改显示的时候修改表格数据,所以我们需要用map对象将所有的数据初始化,这个map对象包含的key->value;就是所有行的 id->false; 当对象保存的状态为 id-> indeterminate 就是用来设置半选;

(3).核心就是点击复选框时候的逻辑, 也就是这个事件@selectAll;判断当前操作行有没有children列表,有就循环chilldern列表将每个子数据的状态设置成当前操作行的状态;没有的话,就计算parentList 里的所有子数据的勾选情况,设置相应的状态(选中 / 非选中/ 半选);

<template>
   <div class="userGroupList">
       <div class="selectUser">
            <el-checkbox v-model="allCheckedOutUser" :disabled='IsDisabled' @change='handleAllCheckedOutUser'>选择全部外部用户</el-checkbox>
            <div><span>已选用户 </span><span>{{ selectBIAll }}</span></div>
       </div>
       <div class="userList">
           <el-table
                ref="multipleTable"
                :data="userGroupsData"
                v-loading="loading"
                row-key="id"
                :border='false'
                style="width: 100%"
                :select-on-indeterminate="true"
                :row-class-name="rowClassNameFun"
                :header-row-class-name="headerRowClassName"
                :tree-props="{ children: 'userList', hasChildren: 'hasChildren' }"
                @select-all="selectAll"
                @select="handleSelect"
                @selection-change="handleSelectionChange"
            >
                <el-table-column type="selection" width="55"> </el-table-column>
                <el-table-column
                prop="name"
                label="组/姓名"
                width="240"
                >
                <template #default="scope">
                    <span>
                        <IconFont
                        v-if="scope.row.type === 2"
                        colorful
                        customClass="dir-icon"
                        icon="wenjianshuxing_guanlixiaozu"
                        />              
                        <IconFont
                        v-else-if="scope.row.type === 1"
                        colorful
                        customClass="dir-icon"
                        icon="wenjianshuxing_chayuexiaozu1"
                        />
                        <IconFont
                        v-else-if="scope.row.type === 0"
                        colorful
                        customClass="dir-icon"
                        icon="wenjianshuxing_guanliyuanxiaozu"
                        />   
                    <span @click="goDetail(scope.row)" :style="{cursor:(scope.row.tenantType? 'pointer':''),color:(scope.row.tenantType?'#11a870':'')}">{{ scope.row.name }}</span><span class="out-user" v-if="scope.row.tenantType === 2">外部</span></span
                    >
                </template>
                </el-table-column>
                <el-table-column prop="roleId" label="角色" width="120">
                <template #default="scope">
                    {{ getRoleName(scope.row.roleId) }}
                </template>
                </el-table-column>
                <el-table-column prop="address" label="类型">
                <template #default="scope">
                    <span v-if="scope.row.tenantType == 1">企业</span>
                    <span v-else-if="scope.row.tenantType == 2">外部</span>
                    <span v-else>-</span>
                </template>
                </el-table-column>
                <el-table-column label="公司">
                <template #default="scope">
                    <span v-if="scope.row.company">{{scope.row.company}}</span>
                    <span v-else>-</span>
                </template>          
                </el-table-column>
            </el-table>  
        </div>
      </div>
</template>

<script>
import getRoleName from "@/mixin/getRoleName";
import IconFont from "@/components/common/Iconfont/index.vue";
import { ElMessage,ElMessageBox } from 'element-plus'

import {
  queryAllGroupByLibrary,
  cleanLibraryUser,
} from "@/services/userAndGroups/list";

import { reactive, ref, onMounted, nextTick, getCurrentInstance, computed, h } from 'vue';
import { useRoute, useRouter } from 'vue-router'

export default {
    name:'UserGroupList',
    props: ["detail"],
    emits:['closeCleanUserDialog'],
    setup(props,{ attrs, slots, emit }){
        let selectedGroups = ref([]);
        let userGroupsData = ref([]);
        let loading = ref(false);
        let ckeckAll = ref(false);
        let allCheckedOutUser = ref(false);
        let isIndeterminateMap = ref({});
        let multipleTable = ref(null);
        let selectTotal = ref(0);
        let { ctx } = getCurrentInstance();

        const $route = useRoute();//获取当前路由信息;
        const $router = useRouter();//获取路由实例;

        const selectBIAll = computed(()=>{
            selectTotal.value = calcFilterTotal(selectedGroups.value,'userList');
            let allUserTotal = calcFilterTotal(userGroupsData.value,'userSize', false);
            return `(${ selectTotal.value } / ${ allUserTotal })`
        });

         //计算外部用户
        const getOutUserData = computed(()=>{
            let arr = [];
            userGroupsData.value.forEach((item) => {
                if(item.userList && item.userList.length > 0){
                    item.userList.forEach(val=>{
                        if(val?.tenantType == 2){
                            arr.push(val)
                        }
                    })
                }
            });
            return arr || [];
        });

        //计算外部用户复选框是否启用
        const IsDisabled = computed(()=>{
            return getOutUserData.value.length ? false : true;
        })

        //计算用户个数;
        const calcFilterTotal = ( list, filterName , isSelect=true)=>{
            if(isSelect){
                let arr = list.filter(item=>{
                    return !!!item[filterName];
                })
                return arr?.length || 0;
            }else{
                let sum = 0;
                list.forEach(item=>{
                    sum +=item.userSize;
                })
                return sum;
            }
        };
        
        // 选中删除
        const userDelete = ()=> {
            if (selectTotal.value == 0) {
                ElMessage.warning("请选择要清理的用户!");
                return;
            } else {
                const delDatas = [];
                selectedGroups.value.forEach((value) => {
                    const table = {
                        groupId: value?.groupId,
                        userList: [value.id],
                        libraryId:props.detail.id
                    };
                    delDatas.push(table);
                });
                let delContent = `确认清理所选${selectTotal.value}个用户?`;
                open(delContent,delDatas);
            }
        };

        const open = (delContent,delDatas)=>{
            ElMessageBox({
                title: '清理用户',
                message: h('p', null,[
                    h('p', null , delContent),
                    h('span', null, '清理后,用户将无法访问该资料室'),
                ]),
                showCancelButton: true,
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                dangerouslyUseHTMLString: true,
                distinguishCancelAndClose: true,
                buttonSize:'small',
                type: "warning",
                customClass: "deleteClass",
                beforeClose:async(action, instance, done) => {
                    if (action === 'confirm') { 
                        const res = await cleanLibraryUser({
                            data: delDatas,
                        });
                        if (res.code === 0) {
                            emit('closeCleanUserDialog')
                            ElMessage.success("删除成功");
                            queryTableData();
                        }
                        done();
                    }else{
                        done();
                    }
                }
            })
        };

        const handleSelectionChange = (selected)=>{
            selectedGroups.value = selected;
            checkOutUserAll(selected);
        }
        
        //全选按钮
        const selectAll = (selection)=> {
            let isAllSelect = checkIsAllSelect();
            userGroupsData.value.forEach((item) => {
                isIndeterminateMap.value[item.id] = isAllSelect;
                toggleSelection(item, !isAllSelect)
                handleSelect(selection, item);
            });
           
        };

        //选择复选框时候
        const handleSelect = (selection, row) => {
            setRowIsSelect(selection,row);
        };

        const setRowIsSelect = (selection,row)=>{
            //当点击父级点复选框时,当前的状态可能为未知状态,所以当前行状态设为false并选中,即可实现子级点全选效果
            let isSelect = isIndeterminateMap.value[row.id];
            if (isSelect == "indeterminate") {
                isIndeterminateMap.value[row.id] = false;
                isSelect = false;
                toggleSelection(row, true)
            }
            isIndeterminateMap.value[row.id] = !isIndeterminateMap.value[row.id];//基础类型
            isSelect = !isSelect;
            //判断操作的是子级点复选框还是父级点复选框,如果是父级点,则控制子级点的全选和不全选
            if (row.userList && row.userList.length > 0) {
                row.userList.forEach((item) => {
                    isIndeterminateMap.value[item.id] = isSelect;
                    toggleSelection(item, isSelect)
                });
            } else {
                //操作的是子节点  1、获取父节点  
                // 2、判断子节点选中个数,如果全部选中则父节点设为选中状态,如果都不选中,则为不选中状态,如果部分选择,则设为不明确状态
                let groupId = row.groupId;
                toggleSelection(row, isSelect)
                userGroupsData.value.forEach((item) => {
                    if (item.id == groupId) {
                        let isALLTrue = selectStatus(item,true);
                        let isALLFalse = selectStatus(item,false);
                        if ( isALLTrue ){
                            isIndeterminateMap.value[item.id] = true;
                            toggleSelection(item, true)
                        } else if ( isALLFalse ) {
                            isIndeterminateMap.value[item.id] = false;
                            toggleSelection(item, false)
                        } else {
                            isIndeterminateMap.value[item.id] = "indeterminate";
                        }
                    }
                });
            }
        };

        //判断外部用户是否全选
        const checkOutUserAll = (selection)=>{
            let isOutUserAll=[];
            getOutUserData.value.forEach(item=>{
                isOutUserAll.push(selection.includes(item));
            })
            // debugger
            let res = isOutUserAll.every(item=>{
                return true == item
            })
            allCheckedOutUser.value = res;
        };
        
        const selectStatus = (item,status=true)=>{
            let isAllSelect = [];
            item.userList.forEach((childrenItem) => {
                isAllSelect.push(isIndeterminateMap.value[childrenItem.id]);
            });
            let isStauts = isAllSelect.every((selectItem) => {
                return status == selectItem;
            })
            return isStauts
        };

        const toggleSelection = async (row, select)=> {
            if (row) {
                await nextTick();
                multipleTable.value && multipleTable.value.toggleRowSelection(row, select);
            }
        };

        const checkIsAllSelect = ()=> {
            let oneProductIsSelect = [];
            userGroupsData.value.forEach((item) => {
                let isSelect = isIndeterminateMap.value[item.id];
                oneProductIsSelect.push(isSelect);
            });
            //判断一级是否是全选.如果一级全为true,则设置为取消全选,否则全选
            let isAllSelect = oneProductIsSelect.every((selectStatusItem) => {
                return true == selectStatusItem;
            });
            return isAllSelect;
        };

        //选择全部外部用户
        const handleAllCheckedOutUser = (val)=>{
            getOutUserData.value.forEach(item=>{
                isIndeterminateMap.value[item.id] = val;
                toggleSelection(item,val)
            })
            userGroupsData.value.forEach((item) => {
                let isALLTrue = selectStatus(item,true);
                let isALLFalse = selectStatus(item,false);
                if ( isALLTrue ){
                    isIndeterminateMap.value[item.id] = true;
                    toggleSelection(item, true)
                } else if ( isALLFalse ) {
                    isIndeterminateMap.value[item.id] = false;
                    toggleSelection(item, false)
                } else {
                    isIndeterminateMap.value[item.id] = "indeterminate";
                }
            });
        };

        const rowClassNameFun = ({ row })=> {
           let isSelect = isIndeterminateMap.value[row.id];
            if (isSelect == "indeterminate") {
                return "indeterminate";
            }
        };
    
        const headerRowClassName = ({ row })=>{
            let oneProductIsSelect = [];
            userGroupsData.value.forEach((item) => {
                oneProductIsSelect.push(isIndeterminateMap.value[item.id])
                // oneProductIsSelect.push(item.isSelect);
            });
            
            if (oneProductIsSelect.includes("indeterminate")) {
                return "indeterminate";
            }
            return "";
        };

        // 查询用户和组列表
        const queryTableData = async ()=> {
            loading.value = true;
            const tableData = {
                libraryId: props.detail?.id || $route.params.id,
                pageSize: 999,
                pageNum: 1,
            };
            const res = await queryAllGroupByLibrary({
                data: tableData,
            });
            if (res.code === 0) {
                const { result } = res;
                userGroupsData.value = result?.groupResList.filter(item=>{
                    return item.name != '管理小组';
                });
                userGroupsData.value.forEach((item)=>{
                    // item.isSelect = false; //默认为不选中
                    isIndeterminateMap.value[item.id] = false;
                    if(item.userList && item.userList.length>0){
                        item.userList.forEach(user=>{
                            isIndeterminateMap.value[user.id] = false;
                        })
                    }
                })
                loading.value = false;
                nextTick(() => {
                    multipleTable.value && multipleTable.value.clearSelection();
                });
            }
        };

        const goDetail = (event) => {
            if(event.tenantType){
                $router.push(`/userDetail/${props.detail.id}/${event.id}`);
            }else {
                return
            }
        };

        onMounted(() => {
           queryTableData();
        });

        return {
            loading,
            ckeckAll,
            allCheckedOutUser,
            selectedGroups,
            userGroupsData,
            multipleTable,
            userDelete,
            goDetail,
            selectBIAll,
            IsDisabled,

            handleSelectionChange,
            handleSelect,
            selectAll,
            queryTableData,
            rowClassNameFun,
            headerRowClassName,
            handleAllCheckedOutUser
        }
    },
    mixins:[getRoleName],
    components:{
        IconFont
    }
}
</script>

<style lang='scss'>
.userList {
    .indeterminate {
        .el-checkbox__input{ 
            .el-checkbox__inner {
                background-color: $color-primary !important;
                border-color: $color-primary !important;
                color: #fff !important;
                &::after {
                    border-color: #C0C4CC !important; 
                    background-color: #C0C4CC;
                    content: "";
                    position: absolute;
                    display: block;
                    background-color: #fff;
                    height: 2px;
                    transform: scale(0.5);
                    left: 0;
                    right: 0;
                    top: 5px;
                    width: auto !important;
                }
            }
        }
    }
}

</style>
<style lang='scss' scoped>

IconFont组件需要用的也可以用

<template>
  <svg v-if="colorful" :class="['iconfont-svg', customClass]" aria-hidden="true">
    <use :xlink:href="`#icon-${icon}`"></use>
  </svg>
  <i v-else :class="['iconfont', `icon-${icon}`, customClass]"></i>
</template>
<script>
export default {
  name: 'iconFont',
  props: {
    icon: String,
    colorful: {
      type: Boolean,
      default: () => false
    },
    customClass: {
      type: String,
      default: () => ''
    }
  }
}
</script>

http://www.niftyadmin.cn/n/565020.html

相关文章

mysql 如何避免间隙锁_mysql 间隙锁

mysql控制间隙锁的参数是:&#xff1a;innodb_locks_unsafe_for_binlog&#xff0c;这个参数默认值是OFF&#xff0c; 也就是启用间隙锁&#xff0c; 他是一个bool值&#xff0c; 当值为true时表示disable间隙锁间隙锁的出现主要集中在同一个事务中先delete后 insert的情况下&a…

python获取对象的大小_获取python对象占用空间的大小

获取python对象占用空间的大小import sys函数原型&#xff1a;sys.getsizeof(object[, default]):Return the size of an object in bytes. The object can be any type of object. All built-in objects will return correct results, but this does not have to hold true fo…

文件上传组件 vue/elemnt-ul

<template><ElDialog:model-value"visible"title"文件上传"closed"cancelAction"custom-class"data-lib-upload-modal custom-dialog":width"468"><div class"title-part flexbox justify-content-betw…

华夫饼为什么不松软_华夫饼这做法真简单,不揉面不整形,5分钟就好,酥脆松软超好吃...

话说没有哪个小孩子不喜欢吃零食&#xff0c;超市里五花八门、充满诱惑力的零食被孩子们视为最爱的宝贝&#xff0c;就连大人有些时候也是禁不住零食的诱惑要买点回来解解馋。其实好多小零食并不健康&#xff0c;也缺少营养&#xff0c;这些弊端大家都知道&#xff0c;但就是戒…

jsencrypt加解密

import JSEncrypt from jsencrypt/bin/jsencrypt.min// 密钥对生成 http://web.chacuo.net/netrsakeypairconst publicKey MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n nzkXSOVOZbFu/TJhZ7rFANeaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQconst private…

java map 覆盖_Java Map put 方法别用于覆盖操作

map .put 方法 put(“1″,”111”); 再put一次&#xff1a; put(“1″,”222”);get(“1”) >>结果是222一个Map中不能包含相同的key&#xff0c;每个key只能映射一个value 自动帮你覆盖了呗所以最好是如果有KEY 就用另一个自己写的方法来修改public void fix(Map map, S…

小程序 订阅消息 一次授权多次接收

1、微信公众平台需要先使用模板&#xff0c;不符合要求的就重新申请模板&#xff08;按照要求修改即可&#xff09; 2、前端需手动调起用户授权&#xff08;考虑到用户体验的问题&#xff0c;用户没有授权时会调用起来用户授权弹框&#xff0c;当用户授权完成后则无需再弹框提示…

java stack方法_Java Stack类

Stack类是Vector类的一个子类&#xff0c;它实现了标准的后进先出堆栈。Stack类仅定义默认构造函数&#xff0c;该构造函数创建一个空堆栈。 Stack包含Vector定义的所有方法&#xff0c;并添加了几个自己的方法。除了从父类Vector继承的方法之外&#xff0c;Stack还定义了以下方…