vue多级复杂列表展开/折叠及全选/分组全选实现


Posted in Javascript onNovember 05, 2018

首先,来看下效果图

vue多级复杂列表展开/折叠及全选/分组全选实现

在线体验地址:https://hxkj.vip/demo/multipleList/。温馨提示,打开之后按F12,使用手机模式食用,口味更佳!

可以看出,这个列表有三种展现形式:

1.第一层级中包含直属子项和第二层级,其中第二层级里包含子项
2.第一层级中只包含第二层级,第二层级里包含子项
3.第一层级中只包含直属子项

接下来,再分析列表所实现的功能:

1.点击父级可以展开与折叠该直属子级;
2.点击父级级的勾选图标可以全选或取消该层级下列的所有子项;
3.点击子项达到该父级的全选状态时时,父级的勾选图标自动勾选;反之,没达到全选时,父级的勾选图标自动取消勾选;
4.所有相同层级之间勾选状态的改变互不影响;

分析完了,紧接着就到了撸码时刻了。

1.首先构建我们所需要的数据结构。

data() {
  return {
    headColor: ['#1c71fb', '#f7aa47', '#00c182', '#ff6769', '#917ee6', '#2cb2eb'],//待选颜色
    jobList: [{
      "id": "2511",
      "name": "嫩江第一中学",
      "member": [{
        "pid": "12058",
        "userName": "冷风",
        "job": "安全主任",
        "name": "冷风"
      }, {
        "pid": "12005",
        "userName": "周大龙",
        "job": "安全主任",
        "name": "大龙"
      }],
      "son": [{
        "id": "2513",
        "name": "校领导",
        "member": [{
          "pid": "12056",
          "userName": "凌凌",
          "job": "安全主任",
          "name": "凌凌"
        }, {
          "pid": "12039",
          "userName": "唐老师",
          "job": "安全主任",
          "name": "老师"
        }]
      }]
    }, {
      "id": "2510",
      "name": "黑龙江黑河市嫩江县教育局",
      "son": [{
        "id": "2525",
        "name": "办公室 ",
        "member": [{
          "pid": "12006",
          "userName": "张喵喵",
          "job": "安全主任",
          "name": "喵喵"
        }, {
          "pid": "12024",
          "userName": "张?粤?,
          "job": "老师",
          "name": "?粤?
        }]
      }, {
        "id": "2524",
        "name": "局长部",
        "member": [{
          "pid": "12015",
          "userName": "小组长",
          "job": "安全主任",
          "name": "组长"
        }, {
          "pid": "12025",
          "userName": "TSY",
          "job": "11",
          "name": "SY"
        }]
      }]
    }, {
      "id": "2545",
      "name": "城市之星2总部",
      "member": [{
        "pid": "12076",
        "userName": "文明",
        "job": "前端开发工程师",
        "name": "文明"
      }, {
        "pid": "12077",
        "userName": "不文明",
        "job": "高级架构师",
        "name": "文明"
      }]
    }], //从后台获取的人员列表信息
    selectPeople: [],//存储被选择的人员
    isOpenItem: [],//控制每级面板的打开与折叠
    isSelectAll: [],//控制每级面板的选中状态
  }
}

2.初始化数据

初始化数据的主要目的。

1.根据数据来给头像设置随机颜色
2.根据数据初始化一层级全选按钮状态
3.根据数据初始化折叠面板折叠状态
4.根据数据设置第二层级的选中状态

initData() {//数据初始化
  //设置头像背景颜色
  let len = this.jobList.length;
  for (let i = 0; i < len; i++) {
    this.setHeadColor(this.jobList[i].member, this.headColor);
    //根据数据初始化全选按钮状态
    this.isSelectAll.push([]);
    this.$set(this.isSelectAll[i], 'group', false);
    this.$set(this.isSelectAll[i], 'child', []);
    //根据数据初始化折叠面板折叠状态
    this.isOpenItem.push([]);
    this.$set(this.isOpenItem[i], 'group', false);
    this.$set(this.isOpenItem[i], 'child', []);
    //设置第二层级的状态
    for (let j in this.jobList[i].son) {
      this.isSelectAll[i].child.push(false)
      this.isOpenItem[i].child.push(false)
      this.setHeadColor(this.jobList[i].son[j].member, this.headColor);
    }
  }
}

3.为父级绑定事件

全选框HTML。使用@click="checkAll(index)"绑定全选事件,用于改变全选框的全选状态

<div class="checkGroup" @click="checkAll(index)" @click.stop>
    <i class="iconfont"
       :class="{'icon-gouxuan':isSelectAll[index] && isSelectAll[index].group,'icon-checkboxround0':isSelectAll[index] && !isSelectAll[index].group}"></i>
</div>

全选框JS。通过改变this.isSelectAll[index]中的group属性,来动态修改父级的选中状态。因为子级选项的数据this.selectPeople是由v-model绑定的,所有只需要对其进行增加和删除的操作,就可以改变子级每一项的选中状态

checkAll(index) {//父级的全选操作
  this.$set(this.isSelectAll[index], 'group', !this.isSelectAll[index].group);//改变当前按钮的选中状态
  if (!this.jobList[index].member && !this.jobList[index].son) {
    return
  }
  if (!this.isSelectAll[index].group) {// 从全选 =》 全不选
    for (let key in this.isSelectAll[index].child) { // 移除所有第二层级子项的选中状态
      this.$set(this.isSelectAll[index].child, key, false);
    }
    for (let i = 0, len = this.selectPeople.length; i < len; i++) {
      if (!this.selectPeople[i]) { //删除干净了
        return
      }
      for (let k in this.jobList[index].son) {//循环删除有部门的人员(对应列表第三层级)
        for (let j in this.jobList[index].son[k].member) {
          if (this.selectPeople[i] && this.selectPeople[i].pid == this.jobList[index].son[k].member[j].pid) {
            this.selectPeople.splice(i, 1);
            i--;
            break;
          }
        }
      }
      for (let j in this.jobList[index].member) {//循环删除没有部门的人员(对应列表第二层级)
        if (this.selectPeople[i] && this.selectPeople[i].pid == this.jobList[index].member[j].pid) {
          this.selectPeople.splice(i, 1);
          i--;
          break;
        }
      }
    }
  } else {// 从全不选 =》 全选
    for (let key in this.isSelectAll[index].child) { // 给所有第二层级子项添加选中状态
      this.$set(this.isSelectAll[index].child, key, true);
    }
    for (let i in this.jobList[index].member) {//循环添加没有部门的人员(对应列表第二层级)
      if (this.selectPeople.includes(this.jobList[index].member[i])) { //如果已经存在,就不用再进行添加
        continue;
      }
      this.selectPeople.push(this.jobList[index].member[i]);
    }
    for (let i in this.jobList[index].son) {//循环添加有部门的人员(对应列表第三层级)
      for (let j in this.jobList[index].son[i].member) {
        if (this.selectPeople.includes(this.jobList[index].son[i].member[j])) { //如果已经存在,就不用再进行添加
          continue;
        }
        this.selectPeople.push(this.jobList[index].son[i].member[j]);
      }
    }
  }
}

4.通过子级改变父级的选中状态

子级HTML。需要注意的是,这里面绑定了两次stateChanged事件,只是参数不同。@click="stateChanged(index, j, k)"代表第二层级的子级。@click="stateChanged(index, i)"代表第一层级的子级。

<ul class="item_second" v-show="isOpenItem[index] && isOpenItem[index].group">
  <div class="item_third" v-for="(second,j) in item.son" :key="second.id">
    <div @click="checkSecondItem(index, j)" class="item">
      <div class="checkGroup" @click="checkSecondAll(index, j)" @click.stop>
        <i class="iconfont"
          :class="{'icon-gouxuan':isSelectAll[index] && isSelectAll[index].child[j],'icon-checkboxround0':isSelectAll[index] && !isSelectAll[index].child[j]}"></i>
      </div>
      {{second.name}}
    </div>
    <ul class="item_fourth" v-show="isOpenItem[index] && isOpenItem[index].child[j]">
      <li v-for="(third,k) in second.member" :key="third.pid">
        <input @click="stateChanged(index, j, k)" type="checkbox" name="school"
            :id="'check'+third.pid"
            v-model="selectPeople"
            :value="third">
        <label class="content-wrap" :for="'check'+third.pid">
          <div class="item_img" :style="{ background: third.headColor }">{{ third.name }}</div>
          <div class="content">
            <p class="content_name">
              {{third.userName}}
            </p>
            <p class="vice">{{ third.job }}</p>
          </div>
        </label>
      </li>
    </ul>
  </div>
  <li v-for="(people,i) in item.member" :key="people.pid">
    <input @click="stateChanged(index,i)" type="checkbox" name="school" :id="'check'+people.pid"
        v-model="selectPeople"
        :value="people">
    <label class="content-wrap" :for="'check'+people.pid">
      <div class="item_img" :style="{ background: people.headColor }">{{ people.name }}</div>
      <div class="content">
        <p class="content_name">
          {{people.userName}}
        </p>
        <p class="vice">{{ people.job }}</p>
      </div>
    </label>
  </li>
</ul>

子级JS。其中,stateChanged函数,通过传入的参数不同来判断当前属于哪一层级的数据。setFirstLevelChecked函数,通过判断所有子级选项的选中状态来给第一层级添加选中状态。

stateChanged(index, i, j) {
  if (j !== undefined) { //如果有j值,代表第三层级数据
    if (this.selectPeople.includes(this.jobList[index].son[i].member[j])) {//点击之前为选中状态
      this.$set(this.isSelectAll[index].child, i, false);//改变父级按钮的选中状态为非选中状态
      this.$set(this.isSelectAll[index], 'group', false);//改变顶级按钮的选中状态为非选中状态
    } else {//点击之前为非选中状态
      //给父级添加选中状态
      for (let k = 0; k < this.jobList[index].son[i].member.length; k++) {
        if (!this.selectPeople.includes(this.jobList[index].son[i].member[k]) && this.jobList[index].son[i].member[k] != this.jobList[index].son[i].member[j]) {//只要有其中一个未选中,就跳出循环,不给父级添加选中状态
          return false
        }
      }
      this.$set(this.isSelectAll[index].child, i, true);//改变父级按钮的选中状态为选中状态
      this.setFirstLevelChecked(index, this.jobList[index].son[i].member[j]);//给第一级添加选中状态
    }
  } else {//没有j值,第二层级数据
    if (this.selectPeople.includes(this.jobList[index].member[i])) {//点击之前为选中状态
      this.$set(this.isSelectAll[index], 'group', false);//改变父级按钮的选中状态为非选中状态
    } else {//点击之前为非选中状态
      this.setFirstLevelChecked(index, this.jobList[index].member[i]);//给第一级添加选中状态
    }
  }
},
setFirstLevelChecked(index, data) {//给第一级添加选中状态
  for (let k in this.jobList[index].member) {
    if (!this.selectPeople.includes(this.jobList[index].member[k]) && data != this.jobList[index].member[k]) {//只要有其中一个未选中,就跳出循环,不给父级添加选中状态
      return false
    }
  }
  for (let i in this.jobList[index].son) {//循环添加有部门的人员(对应列表第三层级)
    for (let j in this.jobList[index].son[i].member) {
      if (!this.selectPeople.includes(this.jobList[index].son[i].member[j]) && data != this.jobList[index].son[i].member[j]) { //如果已经存在,就不用再进行添加
        return false
      }
    }
  }
  this.$set(this.isSelectAll[index], 'group', true);//改变第一级按钮的选中状态为选中状态
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
修改jquery里的dialog对话框插件为框架页(iframe) 的方法
Sep 14 Javascript
简单实用的全选反选按钮例子
Oct 18 Javascript
jquery修改网页背景颜色通过css方法实现
Jun 06 Javascript
jQuery实现表格颜色交替显示的方法
Mar 09 Javascript
Knockout自定义绑定创建方法
Dec 26 Javascript
JS实现图片的不间断连续滚动的简单实例
Jun 03 Javascript
jQuery获取attr()与prop()属性值的方法及区别介绍
Jul 06 Javascript
微信小程序之picker日期和时间选择器
Feb 09 Javascript
理解 javascript 中的函数表达式与函数声明
Jul 07 Javascript
Vue.js仿微信聊天窗口展示组件功能
Aug 11 Javascript
vue学习之Vue-Router用法实例分析
Jan 06 Javascript
微信小程序向Java后台传输参数的方法实现
Dec 10 Javascript
浅谈Vue数据响应
Nov 05 #Javascript
vue+canvas实现炫酷时钟效果的倒计时插件(已发布到npm的vue2插件,开箱即用)
Nov 05 #Javascript
基于vue2的canvas时钟倒计时组件步骤解析
Nov 05 #Javascript
基于Vue2实现简易的省市区县三级联动组件效果
Nov 05 #Javascript
使用vue2实现带地区编号和名称的省市县三级联动效果
Nov 05 #Javascript
vue router的基本使用和配置教程
Nov 05 #Javascript
微信小程序tabbar底部导航
Nov 05 #Javascript
You might like
Dedecms V3.1 生成HTML速度的优化办法
2007/03/18 PHP
PHP里的中文变量说明
2011/07/23 PHP
php 多文件上传的实现实例
2016/10/23 PHP
PHP实现链表的定义与反转功能示例
2018/06/09 PHP
PHP解析url并得到url参数方法总结
2018/10/11 PHP
javascript 常用方法总结
2009/06/03 Javascript
javascript 混合的构造函数和原型方式,动态原型方式
2009/12/07 Javascript
js parseInt(&quot;08&quot;)未指定进位制问题
2010/06/19 Javascript
jquery 查找新建元素代码
2010/07/06 Javascript
JQuery从头学起第三讲
2010/07/06 Javascript
jquery获取tagName再进行判断
2014/05/29 Javascript
[将免费进行到底]在Amazon的一年免费服务器上安装Node.JS, NPM和OurJS博客
2014/08/18 Javascript
在Ubuntu系统上安装Ghost博客平台的教程
2015/06/17 Javascript
jQuery实现的多滑动门,多选项卡效果代码
2016/03/28 Javascript
jquery.serialize() 函数语法及简单实例
2016/07/08 Javascript
微信小程序 传值取值的几种方法总结
2017/01/16 Javascript
jQuery实现的页面遮罩层功能示例【测试可用】
2017/10/14 jQuery
利用Node.js检测端口是否被占用的方法
2017/12/07 Javascript
Node.js实现注册邮箱激活功能的方法示例
2018/03/23 Javascript
在Vue项目中使用jsencrypt.js对数据进行加密传输的方法
2019/04/17 Javascript
jQuery操作attr、prop、val()/text()/html()、class属性
2019/05/23 jQuery
微信小程序获取当前位置和城市名
2019/11/13 Javascript
微信小程序 自定义弹窗实现过程(附代码)
2019/12/05 Javascript
node.JS的crypto加密模块使用方法详解(MD5,AES,Hmac,Diffie-Hellman加密)
2020/02/06 Javascript
[03:36]DOTA2完美大师赛coL战队趣味视频——我演你猜
2017/11/23 DOTA
利用Python实现命令行版的火车票查看器
2016/08/05 Python
浅析Python中MySQLdb的事务处理功能
2016/09/21 Python
Python 详解基本语法_函数_返回值
2017/01/22 Python
Python3实现将本地JSON大数据文件写入MySQL数据库的方法
2018/06/13 Python
对Python获取屏幕截图的4种方法详解
2019/08/27 Python
keras处理欠拟合和过拟合的实例讲解
2020/05/25 Python
路易威登和香奈儿手袋:LuxeDH
2017/01/12 全球购物
乡下人家教学反思
2014/02/01 职场文书
2014年幼儿园重阳节活动方案
2014/09/16 职场文书
团员年度个人总结
2015/02/26 职场文书
vue实现拖拽交换位置
2022/04/07 Vue.js