文章目录
代码不是完整代码!不能直接复制粘贴
没有使用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()
},