动手撸一个基于Vue的类树形全选与反选组件

最近,由于公司后台管理系统重构,需要一个用到如图所示的一个功能:

项目使用的Vue和elementUI

treebox.gif

一共有4级菜单,要实现全选和反选功能,当下级有一个被选中时,上级也要被选中;当下级全部未被选中时,上级也不能被选中。

网上搜了一圈也没有找到合适的,elementUI的tree组件虽然功能能实现,但是改样式又不方便,于是,只好自己撸一个了。。。。不写不知道自己有多菜!这个东西居然耗时一天。。

这个功能被抽离出来,做成了一个单独的组件。

下面记录下主要的代码:
后台返回的菜单数据格式

let menuData = [
    {
        "id": 1,
        "menName": "管理模式",
        "path": "manageMode",
        "businessCode": "manageMode",
        "parentId": 0,
        "isChecked": null,
        "menLevel": 1,
        "subMenuPermissionsVO": [
            {
                "id": 105,
                "menName": "首页",
                "path": "/index",
                "businessCode": "index",
                "parentId": 1,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": []
            },
            {
                "id": 106,
                "menName": "工作报表",
                "path": "/report",
                "businessCode": "report",
                "parentId": 1,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": [
                    {
                        "id": 107,
                        "menName": "数据总揽",
                        "path": "/reportOverView",
                        "businessCode": "reportOverView",
                        "parentId": 106,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    },
                    {
                        "id": 109,
                        "menName": "客服",
                        "path": "/reportService",
                        "businessCode": "客服",
                        "parentId": 106,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": [
                            {
                                "id": 110,
                                "menName": "工作报表",
                                "path": "/report-service-workreport",
                                "businessCode": "工作报表",
                                "parentId": 109,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            },
                            {
                                "id": 111,
                                "menName": "工作质量报表",
                                "path": "/report-service-qualityreport",
                                "businessCode": "工作质量报表",
                                "parentId": 109,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            }
                        ]
                    },
                    {
                        "id": 112,
                        "menName": "留言报表",
                        "path": "/reportLeaveWords",
                        "businessCode": "留言报表",
                        "parentId": 106,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    },
                    {
                        "id": 113,
                        "menName": "访问轨迹",
                        "path": "/reportWebsite",
                        "businessCode": "访问轨迹",
                        "parentId": 106,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    }
                ]
            },
            {
                "id": 114,
                "menName": "历史会话",
                "path": "/history",
                "businessCode": "历史会话",
                "parentId": 1,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": []
            },
            {
                "id": 115,
                "menName": "客服管理",
                "path": "/service",
                "businessCode": "客服管理",
                "parentId": 1,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": [
                    {
                        "id": 116,
                        "menName": "所有客服",
                        "path": "/service-all",
                        "businessCode": "所有客服",
                        "parentId": 115,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    },
                    {
                        "id": 117,
                        "menName": "角色设置",
                        "path": "/service-role-setting",
                        "businessCode": "角色设置",
                        "parentId": 115,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    }
                ]
            },
            {
                "id": 118,
                "menName": "企业账户",
                "path": "/business",
                "businessCode": "企业账户",
                "parentId": 1,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": [
                    {
                        "id": 119,
                        "menName": "团队信息",
                        "path": "/business-team",
                        "businessCode": "团队信息",
                        "parentId": 118,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    },
                    {
                        "id": 120,
                        "menName": "当前服务详情",
                        "path": "/business-service",
                        "businessCode": "当前服务详情",
                        "parentId": 118,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    },
                    {
                        "id": 121,
                        "menName": "团队联系人",
                        "path": "/business-contacts",
                        "businessCode": "团队联系人",
                        "parentId": 118,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": []
                    }
                ]
            },
            {
                "id": 122,
                "menName": "设置",
                "path": "/setting",
                "businessCode": "设置",
                "parentId": 1,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": [
                    {
                        "id": 123,
                        "menName": "系统设置",
                        "path": "/setting-system",
                        "businessCode": "系统设置",
                        "parentId": 122,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": [
                            {
                                "id": 124,
                                "menName": "个人信息",
                                "path": "/setting-userinfo",
                                "businessCode": "个人信息",
                                "parentId": 123,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            },
                            {
                                "id": 125,
                                "menName": "修改密码",
                                "path": "/setting-change-pwd",
                                "businessCode": "修改密码",
                                "parentId": 123,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            },
                            {
                                "id": 126,
                                "menName": "通知中心",
                                "path": "/setting-notice",
                                "businessCode": "通知中心",
                                "parentId": 123,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            }
                        ]
                    },
                    {
                        "id": 127,
                        "menName": "在线服务",
                        "path": "/setting-online",
                        "businessCode": "在线服务",
                        "parentId": 122,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": [
                            {
                                "id": 128,
                                "menName": "基础设置",
                                "path": "/setting-online-base",
                                "businessCode": "基础设置",
                                "parentId": 127,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            }
                        ]
                    },
                    {
                        "id": 129,
                        "menName": "接入设置",
                        "path": "/setting-join",
                        "businessCode": "接入设置",
                        "parentId": 122,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": [
                            {
                                "id": 130,
                                "menName": "网站接入",
                                "path": "/setting-join-website",
                                "businessCode": "网站接入",
                                "parentId": 129,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            }
                        ]
                    },
                    {
                        "id": 131,
                        "menName": "高级设置",
                        "path": "/setting-advanced",
                        "businessCode": "高级设置",
                        "parentId": 122,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": [
                            {
                                "id": 132,
                                "menName": "短信服务",
                                "path": "/setting-advanced-msg",
                                "businessCode": "短信服务",
                                "parentId": 131,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            },
                            {
                                "id": 133,
                                "menName": "二维码名片",
                                "path": "/setting-advanced-qrcode",
                                "businessCode": "二维码名片",
                                "parentId": 131,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            }
                        ]
                    }
                ]
            }
        ]
    },
    {
        "id": 2,
        "menName": "客服模式",
        "path": "customerMode",
        "businessCode": "customer",
        "parentId": 0,
        "isChecked": null,
        "menLevel": 1,
        "subMenuPermissionsVO": [
            {
                "id": 102,
                "menName": "留言",
                "path": "儿",
                "businessCode": "分隔符",
                "parentId": 2,
                "isChecked": null,
                "menLevel": 2,
                "subMenuPermissionsVO": [
                    {
                        "id": 103,
                        "menName": "小计成",
                        "path": "34",
                        "businessCode": "大幅度",
                        "parentId": 102,
                        "isChecked": null,
                        "menLevel": 3,
                        "subMenuPermissionsVO": [
                            {
                                "id": 104,
                                "menName": "76657",
                                "path": "76868",
                                "businessCode": "658568",
                                "parentId": 103,
                                "isChecked": null,
                                "menLevel": 4,
                                "subMenuPermissionsVO": []
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

父组件主要代码


<tree-table :menuData="menuData" :level="1"></tree-table>

// 其中menuData为后台返回的数据,level为菜单层级,在menuData中有返回,这里主要是用来写样式用的。。

子组件组件代码(一个递归组件):

子组件全部代码(说明都在代中有注释):

<template>
  <ul class="tree-container" :class="'level'+level">
    <li class="" v-for="(item, Index) in menuData" :key="item.id" :class="'level'+item.menLevel">
      <div class="tree-node-title" :class="'level'+item.menLevel"><label>
        <input type="checkbox" @click.stop="nodeClick(item,Index)" name="menuSelected" :checked="item.isChecked == 1 ? true : false" :value="item.id">
        {{item.menName}}-level{{item.menLevel}}
      </label></div>
      <div class="tree-childnode-container " :class="'level'+item.menLevel">
        <CheckBox :menuData="item.subMenuPermissionsVO" :level="item.menLevel"></CheckBox>
      </div>
    </li>
  </ul>

</template>

<script>
  export default {
    name: 'CheckBox',
    props: ["menuData", "level"],
    data() {
      return {
        checkedVal:[]
      }
    },
    created() {

    },
    mounted() {

    },
    methods: {
      nodeClick(node, index) {
      // 通过isChecked判断是否为选中状态:1-选中;0-未选中。这个字段是后端接口返回的
        if (node.isChecked == 1) {
          this.$set(node, "isChecked", 0)
        } else {
          this.$set(node, "isChecked", 1)
        }
        // 个人理解:组件每次递归调用会生成一个实例,this指向这个实例,这些实例相互独立的,所以每次的this都不一样,具体可以控制台打印看下。
        this.refreshAllParentNodes(this) // 设置父级状态
        this.refreshAllSonNodes(this.$children[index], node.isChecked);// 根据当前点击层级的状态设置自己所有子级的状态
      },
      refreshAllSonNodes(node, status) {

        if (node && node.$children.length) {
          node.menuData.map((meunItem, j) => {
            this.$set(meunItem, 'isChecked', status);
            this.refreshAllSonNodes(node.$children[j], status);
          })
        }
      },
      refreshAllParentNodes(node) {
        if (node) {
          var status = 0;
          var nullCount = 0;
          var fullCount = 0;

          if (node.menuData && node.$parent.menuData) {
            node.menuData.map((meunItem, j) => {
              if (typeof meunItem.isChecked == 'undefined') {
                nullCount++
              } else if (meunItem.isChecked == 0) {
                nullCount++
              } else if (meunItem.isChecked == 1) {
                fullCount++
              }
            })
            if (nullCount === node.$children.length) {
            // 该状态说明当前点击层级和兄弟层级都没选中,所以父级也是未选中状态
              status = 0;
            } else {
            //该状态说明当前点击层级和兄弟层级只要有一个选中,父级也是选中状态
              status = 1;
            }
            node.$parent.menuData.map(child => {
              node.menuData.map((meunItem, j) => {
                if (child.id == meunItem.parentId) {
                // 根据父级id和子级的parentId是否相等,来确定当前点击的层级的父级是哪一个
                  this.$set(child, 'isChecked', status);
                }
              })

            })
          }
          // 递归计算父级
          this.refreshAllParentNodes(node.$parent);
        }
      },
    }
  }
</script>

<style lang="scss">
  .tree-container {
    $borderColor: #ebeef5;
    width: 100%;
    padding: 0;

    &.level3 {
      display: flex;
      flex-wrap: wrap;

      & > .level4 {
        width: 25%;
        padding: 0 20px 0 40px;
      }
    }

    & > .level2 {
      display: flex;
      align-items: center;
      border: 1px solid $borderColor;
      line-height: 45px;

      .tree-node-title {
        &.level2 {
          width: 160px;
          text-align: center;
        }

      }
    }

    .level3 {
      .level3 + .level3 {
        border-top: 1px solid $borderColor;
      }
    }

    .tree-node-title {
      &.level3 {
        background-color: #f2f2f2;
        width: 100%;
        padding: 0 20px;
      }

      &.level1 {
        padding: 20px 0;
      }

    }

    .level2 + .level2 {
      border-top: none;
    }

    .tree-childnode-container {
      width: 100%;

      &.level2 {
        border-left: 1px solid $borderColor;
      }
    }

    input {
      margin-right: 5px;
    }

    label {
      cursor: pointer;
    }

  }
</style>

注意
在当前组件中使用组件本身(递归),注意图中圈出的地方,上面有两种写法

0700DC7EBB9A450C8504982D46B291E3.png

文章归类于: 码不停蹄

文章标签: #Vue#项目#前端

版权声明: 自由转载-署名-非商用

4条评论

提示:

评论会在审核通过后显示在下方

昵称必填,用于展示在评论中

邮箱必填,不会公开展示,方便及时收到回复

网址选填,方便看到的人去访问,请完整填写,例如(http://www.brandhuang.com)

item.from_uname重庆崽儿Brand
2021-05-11 10:14
item.from_uname端木
2021-05-09 22:50

有git地址吗,想看看完整的例子

😅没有git地址。上面基本就是所有的相关代码了

回复
item.from_uname端木
2021-05-09 22:50

有git地址吗,想看看完整的例子

回复
item.from_uname重庆崽儿Brand
2019-06-14 09:45
item.from_unameshyla
2019-06-14 09:38

good~o( ̄▽ ̄)d

ε٩ (๑> 灬 <)۶з,活捉划水的,哈哈

回复
item.from_unameshyla
2019-06-14 09:38

good~o( ̄▽ ̄)d

回复
  • 1