首先, 来看下效果图
效果图. gif
在线体验地址: 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);// 改变第一级按钮的选中状态为选中状态
- }
以上就是全部的实现过程, 如果有不懂的, 可以留言反馈.
项目 GitHub 地址: https://github.com/TangSY/multiple-list-demo
来源: http://www.jianshu.com/p/154ffc0abed4