最近开发中,遇到了一个需求,需要使用到antd-vue下的Tree组件。需要实现的功能点如下:
1.树结构是部门的组织机构,包含的有部门内容和人员。
2.每个树节点标题需要显示(在线人数/总数)的格式。
3.对于在线人员,需要图标点亮,并且排序在最上方展示。对于离线人员,需要图标暗掉。
具体的需求图如下
下面,Yan会逐个讲解每个需求,并且怎么实现的。
我们首先分析一下:
1.既然需要在标题上面显示新的字样,那么一定用到了插槽,而在Tree组件中有一个
,这个可以实现我们在标题上显示(0/20)的字样。
2.我们肯定是需要知道后端返回的数据格式,可以初步知道一定是一个嵌套children的格式,
{
title:“xxx”,
key:"xxx",
children:[{},{}]
}
除此之外,后端可能还会在数据中加入其他字段,不难想出,我们这个功能是需要到后端有isOnline这个属性的,一会儿我们看一下后端的数据来进行验证我们的猜想,也会对我们后端数据字段进行一个解释。
3.明白了需要后端数据支持后,我们也不难想出,我们前端需要封装一个方法计算出我们的在线人数以及总人数,然后再给模板中使用。
好的,那我们开始吧。
分析后端返回的数据字段:
实现Tree标题有 0/20 的字样,并且每个节点有图标。
//1.标题插槽 在这个插槽里可以设置标题之后的(0/100)字样
(0/100)
//2.图标插槽 这个插槽可以实现我们的每个标题前面所展示的图标
//1.运用antd-vue的这些插槽之后,我们就初步实现了每个节点标题中都有(0/20)的字样。
//2.当然,我们顺便把人员在线不在线的图标给准备好,之后我们会根据在线状态,动态的让他们切换起来。
//3.现在全是静态,效果如下。
静态效果:
静态的已经实现了,那么现在我们着手动态的怎么让(0/20)计算起来,形成真正的数据。
并且我们的图标现在不会根据在线/不在线切换成对应的颜色。
让动态展示起来数据是有点麻烦的,我们先做简单的让图标动态切换起来。
(0/20)
//这里先让图标切换起来
'
//重点解释:
有的人很不理解,作用域插槽antd只提供了{key,selected},但是type和isOnline怎么来的?
答:因为antd-vue在你绑定:tree-data="departmentData"的时候,内部已经把这个给你提供回来了。
记性好的小伙伴,可以记起来这不正是我们后端返回数据里面的字段嘛?对!!!我们就是依靠这些字段来进行图标切换的。
当type==='1'时,我们就知道这是人员,它只展示人员图标。
当isOnline=='true'时,这不正好渲染我们的在线图标嘛?
这里不理解的小伙伴一定要好好理解下,为啥作用域可以解构出type和isOnline。 是因为antd把你提供的treeData又给你提供回来供你使用的。
下面我们实现动态计算(0/20)也需要用到解构出来其他的数据。
如果动态的展示图标你会了,那么下面的动态展示在线人数就好理解了。
//其实就这里改变了,这里我们执行了一个方法,这个方法就是动态的计算我们的在线人数。
{{ formatPersonCount(children) }}
//重点解释:
//这里我们通过图标那块知道,我们可以解构出我们提供的treeData,在这里我们需要拿出type和children
{{ formatPersonCount(children) }}
//type和children的作用就是:
type用来区分这是部门还是人员,部门的话才显示(0/20)
{{formatPersonCount(children)}}
在插值表达式中执行我们的动态计算的方法,children就是我们需要传递进去的参数。
理解到这里,我们就知道了,重点就是这个formatPersonCount(children)方法,下面我们详细解释。
// 在线数量/总数量--格式化 字符串在模板中调用
let formatPersonCount = (node: TreeOutputWithPerson[]): string => {
//node:就是我们模板中传递进来的children
let { total, onlineCount } = calculatePersonCount(node);
return `${onlineCount}/${total}`;
}
//计算在线人数和总人数
let calculatePersonCount = (node: TreeOutputWithPerson[]): { total: number, onlineCount: number } => {
let total = 0;//总数量
let onlineCount = 0;//在线数量
for (let i = 0; i < node.length; i++) {
let item = node[i];//children下边的每一个节点
//统计总人数
if (item.type == '1') {
total++;
}
// 统计在线的人员
if (item.isOnline) {
onlineCount++;
}
// 递归再次判断,将上次结果累加。
if (item.children) {
let result = calculatePersonCount(item.children);
total += result.total;
onlineCount += result.onlineCount;
}
}
return { total, onlineCount }
}
解释:
猛地一看,你绝对懵了,别着急,看解释:
1.formatPersonCount()其实就是格式化,为了显示出字符串`在线人数/总人数`
2.真正的动态计算在calculatePersonCount()这里面,这里面设置了
totalCount和onlineCount,递归的调用这个方法,把最后的计算结果返回出去
然后插值表达式{{formatPersonCount(children)}}展示就是动态的结果
最后效果如下:
小结:
到这里,我们第一个需求已经解决了,我知道肯定很难理解,大家不能理解的不要看下面的需求了。
先好好的搞明白这边,然后好好理解之后,明天再看下面的也行啊。
其实需求2和需求3:在线的排序在最上方是可以同时做出来的,因为它们的逻辑都是在一个方法里的。
现在我们的在线不在线的时候,图标已经可以进行绿和灰色的切换了(需求1已经做过了)。
但是,就这个图标的状态,你能依靠后端数据吗?
这样解释:我们是获取到后端数据之后,然后绑定到treeData上的,那么其实这时候后端的isOnlione字段绑定了,之后如果某个人员离线了,你的界面的状态还是在线的,那么这就不是实时的了。
对,实时!怎么让数据是实时的呐?
我们公司采用了消息传递机制:Bus公交车(事件总线)。
有两个消息:
1.在线时候 会接收到节点的key值(我们公司人员的mac)
2.离线时候 会接受到离线人的key(人员的mac)
下面我们讲解实现的思路,毕竟有思路才有一切:
思路:
1.当消息是在线的时候,我们需要接收到在线人员的key(mac)之后,在一个方法
setOnlineStatus()中,找到树结构中的这个人员,并且设置状态为在线,然后再找到该人员所属部门,再把该部门下的所有人员按照在线状态进行排序。
2.当消息是离线的时候,我们需要接收到离线人员的key(mac),然后通过mac找到树结构中的这个人员,并且设置状态为离线,然后再找到该人员所属部门,再把该部门下的所有人员按照在线状态进行排序。
当理解上面的思路后,我们就知道了,我们要先绑定消息通知:
// 生命周期
onMounted(() => {
getDepartmentApi();//获取组织机构
//绑定在线消息
Bus.$on("receiveLocation", (data) => {
console.log("消息", data);
data.forEach(item => {
console.log(item);
setOnlineStatus(item.m, true);//设置状态并排序
})
})
// 绑定离线消息
Bus.$on("receiveOffline", (mac) => {
console.log("离线", mac);
setOnlineStatus(mac, false);//设置状态并排序
})
})
//解释:
1.获得在线消息后,data其实就是传递的消息,数据格式为:
m:人员的mac 我们只需要关注这个,因为需要它去找树结构中的人员,并且设置在线状态,其他的属性没用。
{m: '00000000C391', x: '1336', y: '708', dl: '1', c: '0'}
2.获得离线消息后,mac就是返回的人员mac,用它去找树结构中的人员,并且设置在线状态。
3.最重要核心就在于setOnlineStatus(item.m, true);//设置状态并排序,下面我们要讲解这个方法。
2.设置状态的方法:
//1. 设置在线状态并排序
let setOnlineStatus = (mac: string, isOnline: boolean) => {
//根据消息返回的mac 找到树中的对应的人
let tempData = findPersonByMac(mac, departmentData.value);
console.log("找到了", tempData);
//设置人的状态为消息传来的状态
if (tempData != undefined) {
tempData.isOnline = isOnline;
}
//重新排序
let parentMac = useGetParentKey(mac, departmentData.value);//找到父级节点的key
let childrenArr = findPersonByMac(parentMac, departmentData.value);//找到该父节点下所有的孩子
if (childrenArr != null) {
//对该父级节点下所有的孩子进行排序
childrenArr.children = _.orderBy(childrenArr.children, ["isOnline"], ["desc"]);
}
}
//2.通过消息返回的mac查找树节点
let findPersonByMac = (mac: string, treeData) => {
for (let i = 0; i < treeData.length; i++) {
let item = treeData[i];//存放树节点
//如果找到了就返回
if (item.key == mac) {
return item;
} else {
// 如果没找到,就递归找
if (item.children != null && item.children.length > 0) {
let temp = findPersonByMac(mac, item.children);
if (temp != undefined) {
return temp;
}
}
}
}
}
//解释:
又懵了吧,我们从上往下看。
1.setOnlineStatus()就是设置状态然后排序的,核心都是在这里实现的。
2.findPersonByMac()就是通过人员的key(mac)来查找该节点数据的。
知道了这两个我们就知道了我们思路:
1.找到人员
2.修改人员状态
3.找到该人员所属的父节点(部门)
4.对该部门进行排序。
注意:
1.我们排序使用的是lodash插件中的,orderBy()方法,可以去官网看一下怎么使用,超级好理解的。
2.找到该人员的父节点我们用的是useGetParentKey(),这是我们封装的hooks,下面贴上代码
hooks 找到该人员的父节点(部门)useGetParentKey()
//该hooks可以找到人员的父节点(所属部门)的key值
let useGetParentKey = (key: string | number, tree: any,): string | number | undefined => {
let parentKey;//定义父节点的变量
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some(item => item.key === key)) {
parentKey = node.key;
} else if (useGetParentKey(key, node.children)) {
parentKey = useGetParentKey(key, node.children);
}
}
}
return parentKey;
};
export default useGetParentKey;
其实,写完这里,我们就已经可以根据实时的消息,来进行树结构中人员的在线状态的切换,并且在线的人排序在最上方了。
这一块真的超级难,从思路到代码实现,哪一个都是非常让人头痛的。
大家理解不了也不要灰心,随着时间,经验的累加,我们一定可以的。
要多多虚心学习!
抓紧时间练起来吧,兄dei,再不练你就废啦!
记得支持我哦,么么哒,祝您好事成双~~~~~~