树形选择,多级列表的全选、不选联动,根据点击顺序显示在已选列表,用的是uniapp

本文详细介绍了在uni-app中实现人员选择组件的联动逻辑,包括全选、不选及根据点击顺序更新已选列表的功能。通过自定义CheckBox组件和事件传递,实现了灵活的人员选择界面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

代码不是完整代码!不能直接复制粘贴

没有使用uniapp提供的 checkboxgroup 组件和它的 change 方法,不符合我们根据点击顺序排列到 已选列表 上。

components/selected-list/index.vue
  • 已选列表,只显示头像、姓名
<template>
  <view class="selected-list bg-white">
    <!-- 标题 -->
    <view class="select-title">已选列表</view>
    <!-- 横向滚动 -->
    <scroll-view scroll-x="true" scroll-left="9990" show-scrollbar="true">
      <view class="avatar-list align-center" >
        <!-- 默认显示的占位元素 -->
        <view class="avatar-list-item flex flex-column justify-center" :class="show?'show':'hide'">
          <view class="cu-avatar lg radius bg-gray">
            <text class="cuIcon-friendadd line-white"></text>
          </view>
          <view class="name"></view>
        </view>
        <!-- 选中的对象 -->
        <view class="avatar-list-item flex flex-column justify-center" v-for="(item,index) in list" :key="item.userId" :data-index="index" @tap="deleteOne(item, $event)">
          <!-- 头像 -->
          <view class="cu-avatar lg radius bg-gray" :style="{'backgroundImage':'url('+item.photoUrl+')'}">
            <view class="cu-tag badge cuIcon-close"></view>
          </view>
          <!-- 名称 -->
          <view class="name">{{item.userName || item.realName}}</view>
        </view>

      </view>
    </scroll-view>
  </view>
</template>

子组件处理数据的所有方法统一 $emit 到父组件去处理。

为了组件的灵活性,除了必要的组件自身逻辑(不会与其他组件产生互动的逻辑),其他逻辑,最好用 $emit 到父组件处理,这样可以应付不同父组件的处理需求。

export default {
    props:{
      list: {
        type: Array,
        default() {
          return []
        }
      }
    },
    data() {
        return {
          show: true,
        }
    },
    beforeUpdate() {
      // 当已选列表有数据时,隐藏默认占位元素
      if(this.list.length > 0) {
        console.log('this.list.length :>> ', this.list.length);
        this.show = false
      } else {
        this.show = true
      }
    },
    methods: {
        deleteOne(item, e) {
          this.$emit('change',item);
        }
    }

}
components/people-list/index.vue
  • 为了提高组件的复用性,在people-list里面,只渲染人员的列表,不包括 设计、咨询 这些 title
<template>
  <view class="people-list">
      <view class="list-item">
          <view
            class="list-cell uni-list-cell-pd flex justify-start align-center"
            v-for="item in userList"
            :key="item.id"
			@tap="clickItem(item)"
          >
            <!-- 选择框 -->
            <view class="checkbox" :class="item.checked?'checked':''" v-if="showCheckbox"></view>
            <view class="flex avatar padding-left" style="width:100%">
              <!-- 头像 -->
              <view class="cu-avatar md bg-gray">
                <image :src="item.photoUrl" mode="aspectFit" />
              </view>
              <!-- 名称 -->
              <view class="name padding-left" style="flex:1">{{ item.userName }}</view>
              <view class="flex justify-center align-center" style="width:10%">
                <text class="cuIcon-phone"></text>
              </view>
            </view>
          </view>
      </view>
  </view>
</template>


import global from '@/commonModule/global.js'
export default {
  props: {
    showCheckbox: { //显示checkbox
      type: [String,Boolean],
      default: true,
    },
    userList: { // 渲染的人员列表
      type: Array,
      default() {
        return [];
      },
    },
    userObj: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      isShow:true,//是否显示所有人员
    };
  },
  methods: {
    //点击人员,触发到父组件进行修改
    clickItem(item) {
      this.$emit('click-item', item, this.userObj)
    },
  },
};
components/member-list/index.vue
  • 构造外一层的组件,引入people-list,构造设计、咨询、其他等头部;
<template>
  <view class="member-list">
    <view class="list-container">
      <view class="list-item">
        <view v-for="(litem, lindex) in customersObj.userList" :key="lindex">
          <view class="flex align-center label">
            <!-- 选择框 -->
            <view class="checkbox" :class="litem.checked?'checked':''" v-if="showCheckbox" @tap="selectAll(litem)"> </view>
            <!-- 分类:设计、咨询、其他 -->
            <view class="list-name cu-bar" style="flex:1">
              {{ litem.listName || litem.storeName }}<text :class="isShow ? 'cuIcon-fold' : 'cuIcon-unfold'"></text>
            </view>
          </view>
          <!-- 点击分类,控制显示隐藏该分类的人员列表 -->
          <view :class="litem.cShow ? 'show' : 'hide'">
            <!-- 人员列表 -->
            <PeopleList ref="peoplelist" :showCheckbox="showKidCheckbox" :user-list="litem.userList" :user-obj="litem" @click-item="clickMember"></PeopleList>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>
import PeopleList from '@/components/people-list/index.vue'
export default {
  props: {
    // 显示本组件的CheckBox
    showCheckbox: {
      type: Boolean,
      default: false
    },
    // 显示people-list组件的CheckBox
    showKidCheckbox: {
      type:Boolean,
      default: true
    },
    // 渲染的对象数据
    customersObj: {
      type: Object,
      default: {}
    }
  },
  data() {
    return {
      isShow: true, //是否显示所有人员
      allChecked: false,//全选
    };
  },
  mounted(){
	  console.log(this.customersObj)
  },
  components: {
    PeopleList,
  },
  methods: {
    // 选中
    // 所有人员全选/不选,触发到父组件进行修改
    selectAll(selectAll) {
      this.$emit('select-all', selectAll)
    },
    //点击人员,触发到父组件进行修改
    clickMember(item, userObj) {
      this.$emit('click-item',item, userObj)
    },
  },
};

将people-list、member-list组件放入到需要选择人员selectMan的页面。

  • people-list只渲染人员列表,这时候可以将,people-list和member-list两组结构不同的组件,一起放到selectMan里面显示。
  • 也可以把people-list用于通讯录的渲染。
  • 在搜索人员某个关键词时,也可以用people-list显示搜索出的这些人员结果。
  • 还可以用于其他不需要显示title,或者用于需要各种样式的title的人员列表中。
/pages/selectMan/index.vue
<template>
  <view class="select-man-page" v-if="hasData">
      <view class="padding">
        <!-- 已选列表 -->
        <SelectedList :list="selectList" @change="selectChange" style="position:fixed;top:130rpx;z-index:11" :style="[{top:navBarHeight+'px'}]"></SelectedList>
        <scroll-view scroll-y="true" class="scrollview">     
            <view style="height:220rpx;"></view>
            <!-- 搜索框 -->
            <input placeholder-class="cuIcon-search text-center" placeholder="输入姓名查找" name="input" style="padding:2rpx 20rpx;border:1px solid #f1f1f1;height:90rpx;" v-model="searchText">
            <!-- 搜索到的人员列表显示 -->
            <view v-show="searchText">
                <PeopleList :user-list="listObj.userList" @click-item="selectItem"></PeopleList>
            </view>
            <!-- 未搜索时,显示的人员列表 -->
            <view class="alllist" v-show="!searchText">
                <!-- 内部人员(需求,只有客服人员登录才能看到这部分列表) -->
                <view v-if="memberType==='customer'">
                    <view class="flex align-center">
                        <!-- 选择框 -->
                        <view class="checkbox" :class="customersObj.checked ? 'checked' : ''" @tap="selectAll" ></view>
                        <!-- 按需求制作所需的标题样式 -->
                        <view class="list-name cu-bar" style="flex:1" >
                            内部人员
                            <text :class="showCustomerList ? 'cuIcon-fold' : 'cuIcon-unfold'"></text>
                        </view>
                    </view>
                    <view>
                        <MemberList :customers-obj="customersObj" :showCheckbox="true" :class="showCustomerList?'show':'hide'" ref="customerlist" @click-item="selectItem" @select-all="selectTypeAll"></MemberList>
                    </view>
                </view>
                <!-- 门店列表 -->
                <view>
                    <view class="flex align-center">
                        <!-- 选择框 -->
                        <view class="checkbox" :class="listObj.checked ? 'checked' : ''" @tap="selectStoreAll" ></view>
                        <!-- 按需求制作所需的标题样式 -->
                        <view class="list-name cu-bar" style="flex:1">
                            门店列表
                            <text :class="showPeopleList ? 'cuIcon-fold' : 'cuIcon-unfold'"></text>
                        </view>
                    </view>
                    <PeopleList ref="peoplelist" :user-list="listObj.userList" :class="showPeopleList?'show':'hide'" @click-item="selectItem"></PeopleList>
                    </view>
                </view>
            </view>
        </scroll-view>
      </view>
      <view class="cu-bar"></view>
      <view class="foot-bottom bg-white">
        <view class=" cu-bar padding">
            <text>已选:{{selectList.length}} 人</text>
            <button class="cu-btn  line-blue" @tap="submit" v-text="doType==='create'?'创建':'邀请'"></button>
        </view>
      </view>
  </view>
</template>
// 获取渲染people-list的数据
        getUsers() {
            let params = {}
            http.getPeoples(params).then(res => {
                if(res.code !== "ok"){
                        uni.showToast({
                            title: res.msg,
                            duration: 2000
                        }); 
                 } else {
                    ...
                    // 所需数据结构
                    {
                        checked:false,
                        userList: [{
                            id:11,
                            checked: false,
                            realName:'帅哥',
                            photoUrl:'http://xxxxxxxxxxx'
                        },{
                            id:12,
                            checked: false,
                            realName:'帅哥1',
                            photoUrl:'http://xxxxxxxxxxx'
                        }]
                	};
                }
            })
        },
// 获取渲染member-list的数据
        getUsers() {
            let params = {}
            http.xxx(params).then(res => {
                if(res.code !== "ok"){
                        uni.showToast({
                            title: res.msg,
                            duration: 2000
                        }); 
                 } else {
                    ...
                    // 所需数据结构
                    {
                        listName:'内部人员',
						checked: false,
						id: '000',
						userList:[{
                            listName:'设计',
                            id: 'SJ',
                            checked:false,
                            cShow: true,
                            userList: [{
                                listName:'设计',
                                id:11,
                                checked: false,
                                realName:'帅哥',
                                photoUrl:'http://xxxxxxxxxxx'
                            },{
                                listName:'设计',
                                id:12,
                                checked: false,
                                realName:'帅哥1',
                                photoUrl:'http://xxxxxxxxxxx'
                            }]
                        },{
							listName:'咨询',
                            id: 'ZX',
                            checked:false,
                            cShow: true,
                            userList: [{
                                listName:'咨询',
                                id:21,
                                checked: false,
                                realName:'帅哥2',
                                photoUrl:'http://xxxxxxxxxxx'
                            },{
                                listName:'咨询',
                                id:22,
                                checked: false,
                                realName:'帅哥12',
                                photoUrl:'http://xxxxxxxxxxx'
                            }]
                        },{
							listName:'其他',
                            id: 'QT',
                            checked:false,
                            cShow: true,
                            userList: [{
                                listName:'其他',
                                id:31,
                                checked: false,
                                realName:'帅哥3',
                                photoUrl:'http://xxxxxxxxxxx'
                            }]
                        }]
                	};
                    }
                }
            })
        },

全选、不选、联动选择逻辑

  • 关于全选、不选、联动选择的逻辑。要设置一个 checked 状态,点击一次人员的选择框,就置反这个选择框上一次的 Boolean 值。
  • 遍历数组中的 checked 状态,true 就把对应的 CheckBox 添加 “checked” 类名(这是自定义的checked样式,这里的CheckBox不是小程序原生的组件,而是用一个 view 自定义出来的假CheckBox,”checked“是勾上的样式。)false就删除 “ checked” 类名。
  • 遍历数组,如果这一级的 checked 全是 true,就把 父级 的 checked 变为 true(属性checked为true,CheckBox添加类名“checked” 的,属性 checked 为 false,CheckBox删除类名 ”checked“ ),如果有一个 false,就把 父 级的 checked 变为 false。
  • 我在这里犯过错,就是只通过判断 子级 checked 的 true 状态,来修改 父 级的 checked。在这种三层联动的组件里,通过全部为 true 的状态来判断,写出来的逻辑是又臭又长。 所以这里的思维要 反过来 ,不要判断 true 的数量等于 数量的总数,而是判断 只要出现false ,就 把上一级的 CheckBox 的 checked 变成false
// 选择单个人员
selectItem(item, userObj) {
    let tempItem = _.cloneDeep(item)
    if(this.memberType === 'customer') {
        // 点击 内部人员 memberlist 的成员,处理传入 memberlist 的数据,改变 checked 状态。
        this.customersObj.userList.map(k => {
            k.userList.map(h => {
                if(h.id === item.id){
                    h.checked = !h.checked
                    tempItem.checked = !tempItem.checked
                }
            })
        })
    }
	
    // 点击 门店列表 peoplelist 的成员,处理传入 peoplelist 的数据,改变 checked 状态。
    this.listObj.userList.map(k => {
        if(k.id === item.id) {
            k.checked = !k.checked
            tempItem.checked = !tempItem.checked
        }
    })
    // 检查 爷爷 层checked
    this.firstCheck()
    // 检查 父亲 层checked
    this.secondCheck()

    // 同步到已选列表,点击该人员,这个人员的 checked 是 true,直接push 到已选列表的数组里;
    // 实现根据点击顺序,在已选列表里增加/删减。
    if(tempItem.checked) {
        // 使用 JSON.stringify 的原因 请看上上篇 判断数组是否包含某个对象
        let idx = JSON.stringify(this.selectList).indexOf(JSON.stringify(tempItem))
        if(idx === -1) this.selectList.push(tempItem)
    } else {
        this.selectList.map((k,i) => {
            if(k.id === tempItem.id) this.selectList.splice(i,1)
        })
    }

	// 如果不需要根据点击顺序 显示隐藏,可以使用下面这个方法,(如果要使用,记得把 这个方法中的 return 删掉)
    this.selectUserList()
},

判断 父、爷级checked

// 检查 爷爷 层checked
    firstCheck() {
        // 内部人员
        if(this.memberType === 'customer') {
            this.customersObj.checked = true;
            this.customersObj.userList.map(k => {
                k.userList.map(h => {
                    if(!h.checked) this.customersObj.checked = false
                })
            })
        }
        //门店列表
        this.listObj.checked = true;
        this.listObj.userList.map(k => {
            if(!k.checked) this.listObj.checked = false
        })
    },
    // 检查 父亲 层checked
    secondCheck(){
        if(this.memberType === 'customer') {
            this.customersObj.userList.map(k => {
                k.checked = true
                k.userList.map(h => {
                    if(!h.checked) k.checked = false
                })
            })
        }
    },

点击 父 层的 CheckBox ,联动全选 子 层的所有CheckBox

		// 点击 父 层CheckBox
		selectTypeAll(item){
            let tempItem = _.cloneDeep(item)
			this.customersObj.userList.map(k => {
				if(k.id === item.id) {
                    k.checked = !k.checked
                    tempItem.checked = !tempItem.checked
					if(k.checked){
						k.userList.map(h => h.checked = true )
					} else {
						k.userList.map(h => h.checked = false )
					}
				}
            })
            // 检查 爷爷 层CheckBox
            this.firstCheck()

            // 同步到已选列表
            if(tempItem.checked) {
                tempItem.userList.map(k => {
                    let idx = JSON.stringify(this.selectList).indexOf(k.id)
                    if(idx === -1) this.selectList.push(k)
                })
            } else {
                tempItem.userList.map((k,i) => {
                    this.selectList.map((s,j) => {
                        if(k.id === s.id) this.selectList.splice(j,1)
                    })
                })
            }
			this.selectUserList()
        },
            
            
        // 点击 爷爷 层 CheckBox 联动所有 内部人员
        selectAll() {
            this.customersObj.checked = !this.customersObj.checked;
            if(this.customersObj.checked) {
                this.customersObj.userList.map(k => {
                    k.checked = true;
                    k.userList.map(h => {
                        h.checked = true;
                        // 同步到已选列表
                        let idx = JSON.stringify(this.selectList).indexOf(h.id)
                        if(idx === -1) this.selectList.push(h)
                    })
                })
            } else {
                this.customersObj.userList.map(k => {
                    k.checked = false;
                    k.userList.map(h => {
                        h.checked = false;
                        // 同步到已选列表
                        this.selectList.map((s,j) => {
                            if(h.id === s.id) this.selectList.splice(j,1)
                        })
                    })
                })
            }
            this.selectUserList()
        },
            
            
        // 选择所有门店
        selectStoreAll() {
            this.listObj.checked = !this.listObj.checked;
            if(this.listObj.checked) {
                this.listObj.userList.map(k => {
                    k.checked = true
                    // 同步到已选列表
                    let idx = JSON.stringify(this.selectList).indexOf(k.id)
                    if(idx === -1) this.selectList.push(k)
                })
            } else {
                this.listObj.userList.map(k => {
                    k.checked = false;
                    // 同步到已选列表
                    this.selectList.map((s,j) => {
                        if(k.id === s.id) this.selectList.splice(j,1)
                    })
                })
            }
            this.selectUserList()
        },

点击已选列表,删除已选列表的 头像,并对应修改 爷、父、子 层的 checked 状态。

		// 加入已选列表
		selectUserList(){
            // 这里不是按照点击顺序push,而是根据人员列表本身的顺序排序,要的不是这种效果,所以return。
            // 如果不需要按照点击顺序排序,就启用这个
            return
            this.selectList = []
            if(this.memberType === 'customer') {
                this.customersObj.userList.map(k => {
                    k.userList.map(h => {
                        if(h.checked) list.push(h)
                    })
                })
            }
            console.log(this.selectList, list)
            this.listObj.userList.map(k => {
                if(k.checked) this.selectList.push(k)
            })
		},
            
            
		// 点击已选列表的人员,进行删除和联动
		selectChange(item) {
            this.selectList.map((k, i) => {
                if(k.id === item.id) this.selectList.splice(i, 1)
            })
            if(item.listName) {
                // 如果存在 listName ,就是 内部人员列表 
                this.customersObj.userList.map(k => {
                    k.userList.map(h => {
                        if(h.id === item.id) {
                            h.checked = false;
                        }
                    })
                })
            } else {
                // 如果不存在 listName ,就是 门店列表 
                this.listObj.userList.map(k => {
                    if(k.id === item.id) {
                        k.checked = false
                    }
                })
            }
            // 检查 爷爷 checked
            this.firstCheck()
            // 检查 父亲 checked
            this.secondCheck()
		},
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值