1.实现效果

实现效果

2.数据和布局准备

准备一个 el-table 组件以及数据源,这里的数据源可以是你的接口提供的。

<template>
    <div style="width: 78%;">
        <el-table ref="menuTableRef" :data="menuList" style="width: 100%;margin-bottom: 20px;"
            :row-class-name="rowClassNameFun" row-key="id" border
            :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @select="handleSelect"
            @select-all="handleSelectAll">
            <el-table-column type="selection" width="55"> </el-table-column>
            <el-table-column align="center" prop="title" label="标题">
            </el-table-column>
            <el-table-column align="center" prop="describe" label="描述">
            </el-table-column>
            <el-table-column align="center" prop="name" label="name" />
        </el-table>
    </div>
</template>

<script>
export default {
    name: 'MenuTree',
    data() {
        return {
            menuList: [
                {
                    id: 1,
                    title: '系统管理',
                    describe: '系统管理描述',
                    name: 'system',
                    parentId: 0,
                    children: [
                        {
                            id: 11,
                            title: '用户管理',
                            describe: '系统管理描述',
                            name: 'user',
                            parentId: 1,
                            children: []
                        },
                        {
                            id: 12,
                            title: '角色管理',
                            describe: '角色管理描述',
                            name: 'role',
                            parentId: 1,
                            children: []
                        }
                    ]
                },
                {
                    id: 2,
                    title: '数据管理',
                    describe: '数据管理描述',
                    name: 'data',
                    parentId: 0,
                    children: [
                        {
                            id: 21,
                            title: '数据导入',
                            describe: '数据导入描述',
                            name: 'import',
                            parentId: 2,
                            children: []
                        }
                    ]
                },
                {
                    id: 3,
                    title: '权限管理',
                    describe: '权限管理描述',
                    name: 'permission',
                    parentId: 0,
                    children: [
                        {
                            id: 31,
                            title: '角色管理',
                            describe: '角色管理描述',
                            name: 'role',
                            parentId: 3,
                            children: []
                        }
                    ]
                },
                {
                    id: 4,
                    title: '日志管理',
                    describe: '日志管理描述',
                    name: 'log',
                    parentId: 0,
                    children: [
                        {
                            id: 41,
                            title: '操作日志',
                            describe: '操作日志描述',
                            name: 'operation',
                            parentId: 4,
                            children: []
                        }
                    ]
                },
                {
                    id: 5,
                    title: '设置管理',
                    describe: '设置管理描述',
                    name: 'setting',
                    parentId: 0,
                    children: [
                        {
                            id: 51,
                            title: '参数设置',
                            describe: '参数设置描述',
                            name: 'parameter',
                            parentId: 5,
                            children: []
                        }
                    ]
                }
            ]
        }
    },

    created() {
        this.initData(this.menuList)
    },

    methods: {
        initData(data) {
            for (let item of data) {
                item.isSelect = false // 默认为不选中
                if (item.children && item.children.length > 0) {
                    this.initData(item.children)
                }
            }
        }
     }

3.代码实现

在每个方法中都写好了注释,细心看代码,一步一步跟着代码逻辑来就可以实现最终效果。
代码逻辑

表格数据是否选中标志
isSelect状态: true为选中状态; false为未选中状态; 空字符串为半选中状态
<script>
methods: {

        initData(data) {
            for (let item of data) {
                item.isSelect = false // 默认为不选中
                if (item.children && item.children.length > 0) {
                    this.initData(item.children)
                }
            }
        },

        rowClassNameFun({ row }) {
            if (row.isSelect === "") {
                return "indeterminate";
            }
        },

        /**
         * 处理选中事件
         * @param selection 所有选中的row
         * @param row 当前选中的row
         */
        handleSelect(selection, row) {
            //当点击父级点复选框时,当前的状态可能为未知状态,所以当前行状态设为false并选中,即可实现子级点全选效果
            if (row.isSelect === "") {
                row.isSelect = false;
                this.$refs['menuTableRef'].toggleRowSelection(row, true);
            }

            row.isSelect = !row.isSelect

            let level = this.getSelectedRowLevel(row)

            if (level == 1) {
                this.changeAllChildrenStatus(row.children, row.isSelect)
            } else if (level == 2) {
                this.changeAllChildrenStatus(row.children, row.isSelect)
                this.changeAllParentsStatus(row)
            } else if (level == 3) {
                this.changeAllParentsStatus(row)
            }
        },

        /**
         * 处理全选事件
         * @param selection 全选选中的行
         */
        handleSelectAll(selection) {
            let isAllSelect = this.checkIsAllSelect()
            this.menuList.forEach((item) => {
                item.isSelect = isAllSelect
                this.$refs['menuTableRef'].toggleRowSelection(item, !isAllSelect)
                this.handleSelect(selection, item)
            })
        },

        /**
         * 获取选中行的等级
         * @param row
         * @returns 1 父级 2 既是父级也是子级 3 叶子节点
         */
        getSelectedRowLevel(row) {
            if (row.parentId == 0) {
                return 1
            } else {
                if (row.children || row.children.length) {
                    return 2
                } else {
                    return 3
                }
            }
        },
        /**
         * 改变所有子节点选中状态
         * @param data
         */
        changeAllChildrenStatus(data, isSelect) {
            data.forEach((item) => {
                item.isSelect = isSelect;
                this.$refs['menuTableRef'].toggleRowSelection(item, isSelect);
                if (item.children && item.children.length) {
                    this.changeAllChildrenStatus(item.children, isSelect);
                }
            })
        },

        /**
         * 改变所有父级选中状态
         * 操作的是子节点:
         * 1、获取父节点  
         * 2、判断子节点选中个数,如果全部选中则父节点设为选中状态,如果都不选中,则为不选中状态,如果部分选择,则设为不明确状态
         * 3、判断父节点是否还有父级
         * @param row
         */
        changeAllParentsStatus(row) {
            let parentRow = null
            if (row.parentId == 0) {
                parentRow = row
            } else {
                parentRow = this.getParentRow(this.menuList, row.parentId)
            }
  
            let selectStatusList = []
            this.getRowSelectStatus(parentRow.children, selectStatusList)

            let isAllSelected = selectStatusList.every(e => e == true)
            let isAllNotSelected = selectStatusList.every(e => e == false)

            if (isAllSelected) {
                parentRow.isSelect = true;
                this.$refs['menuTableRef'].toggleRowSelection(parentRow, true);
            } else if (isAllNotSelected) {
                parentRow.isSelect = false;
                this.$refs['menuTableRef'].toggleRowSelection(parentRow, false);
            } else {
                parentRow.isSelect = "";
            }
            
            // 还有父级
            if (parentRow.parentId != 0) {
                this.changeAllParentsStatus(parentRow)
            }
        },

        /**
         * 获取选中节点的根节点对象
         * @param data
         * @param parentId
         */
        getParentRow(data, parentId) {
            for (let item of data) {
                if (item.id == parentId) {
                    return item;
                }

                if (item.children && item.children.length) {
                    let result = this.getParentRow(item.children, parentId);
                    if (result) {
                        return result;
                    }
                }
            }
            return null;
        },

        /**
         * 获取row选中状态
         * @param data 行数据
         * @param list 选中状态容器
         */
        getRowSelectStatus(data, list) {
            data.forEach((item) => {
                list.push(item.isSelect)
                if (item.children && item.children.length) {
                    this.getRowSelectStatus(item.children, list)
                }
            })

        },

        /**
         * 判断一级row是否全选
         * @returns
         */
        checkIsAllSelect() {
            let rootRowIsSelectList = []
            this.menuList.forEach((item) => {
                rootRowIsSelectList.push(item.isSelect);
            });

            //判断一级row是否是全选.如果一级row全为true,则设置为取消全选,否则全选
            let isAllSelect = rootRowIsSelectList.every((item) => {
                return true == item;
            });
            return isAllSelect;
        },

        /**
         * 获取选中行的id列表
         * @param data menuList
         * @param list 选中row和被选中row父级id的列表
         */
        getSelectedRowId(data, list) {
            data.forEach((item) => {
                if (item.isSelect || item.isSelect === "") {
                    list.push(item.id)
                }
                if (item.children && item.children.length) {
                    this.getSelectedRowId(item.children, list)
                }
            })
        },
    }
</script>

半选中样式

<style scoped lang="scss">
::v-deep .indeterminate .el-checkbox__input .el-checkbox__inner {
    background-color: #409eff !important;
    border-color: #409eff !important;
    color: #fff !important;
}

::v-deep .indeterminate .el-checkbox__input.is-checked .el-checkbox__inner::after {
    transform: scale(0.5);
}

::v-deep .indeterminate .el-checkbox__input .el-checkbox__inner {
    background-color: #f2f6fc;
    border-color: #dcdfe6;
}

::v-deep .indeterminate .el-checkbox__input .el-checkbox__inner::after {
    border-color: #c0c4cc !important;
    background-color: #c0c4cc;
}

::v-deep .product-show th .el-checkbox__inner {
    display: none !important;
}

::v-deep .indeterminate .el-checkbox__input .el-checkbox__inner::after {
    content: "";
    position: absolute;
    display: block;
    background-color: #fff;
    height: 2px;
    transform: scale(0.5);
    left: 0;
    right: 0;
    top: 5px;
    width: auto !important;
}

::v-deep .product-show .el-checkbox__inner {
    display: block !important;
}

::v-deep .product-show .el-checkbox {
    display: block !important;
}
</style>

4.参考


Judei
1 声望0 粉丝

你应该听说过长城炮