企业微信通讯录效果?拿捏!!!

需求背景

对于流程审核,在常见的 OA、ERP、CRM 等系统中是很常见的功能。审核自然就会有审核人的选择,这其中就会涉及公司或部门的组织架构关系,以及人员列表。目前公司做的产品就包含这个功能,而且是在移动端上实现。有这种功能的产品也很多,比如:企业微信、钉钉,或者一些定制化的 oa 系统等。一眼觉得企业微信的通讯录效果不错,也可以满足当前需求,so?

先上一波效果图(给各位看官老爷的前菜):

需求前瞻

上图是 web 端人员选择的方式,左侧显示组织架构,右侧加载对应的人员列表。要在移动端上实现这种效果的我对 uview 文档翻来翻去 n 遍也没找到合适的组件。插件市场也搜了下,有同样效果的,但是数据源和后端接口返回的格式有点区别,需要去拼格式。所以放弃了。后面灵机一动发现企业微信的通讯录功能好像可行,那就手撸一个。

需求分析

从前面的 web 端效果图可以看出有两种数据结构:

  1. 组织结构树形数据
js 复制代码
[{
    id: 'xxxxxx',
    name: 'xxx',
    parentId: '0',
    children: [
        {
            id: 'xxxxxx',
            name: 'xxx',
            parent: 'xxxxxx'
        },
        {
            id: 'xxxxxx',
            name: 'xxx',
            parent: 'xxxxxx',
            children: []
        },
        ...
    ]
}]
  1. 人员列表一维数组数据
js 复制代码
[
    { id: 'xxxxxx', avatar: 'xxx', name: 'xxx', orgId: 'xxxxxx', orgName: 'xxx' },
    { id: 'xxxxxx', avatar: 'xxx', name: 'xxx', orgId: 'xxxxxx', orgName: 'xxx' },
    { id: 'xxxxxx', avatar: 'xxx', name: 'xxx', orgId: 'xxxxxx', orgName: 'xxx' },
    { id: 'xxxxxx', avatar: 'xxx', name: 'xxx', orgId: 'xxxxxx', orgName: 'xxx' },
    ...
]

再看企业微信通讯录,人员和组织数据渲染在一个页面,如何将两种类型的数据筛选出来?人员列表中有个关键字段为:orgId,对应的就是组织数据的 id。

需求实现

首先定义一个渲染列表 renderList,用来存放部门数据和人员数据,比如A部门下有M-1,M-2人员,D-1部门,D-1部门下又有 M-3 人员,... 。这里使用 pinia 来管理全局数据。

初始化获取到组织属性数据和所有人员列表数据后,进行数据筛选,我定义了 findChildrenById 和 findMemberByOrgId 方法来查找数据

ts 复制代码
// 根据 id 获取当前节点的 children
const findChildrenById = (root: OrgTreeNode, targetId: string) => {
	const searchNode = (node: OrgTreeNode) => {
		if (node.id === targetId) {
			return node.children?.map((item: OrgTreeNode) => {
				item.type = "department";
				return item;
			});
		}
		if (node.children) {
			for (const child of node.children) {
				const result = searchNode(child);
				if (result !== null) {
					return result;
				}
			}
		}
		return null;
	};
	const result = searchNode(root);
	return result || [];
};

// 根据组织的 id 和成员列表数据的 orgId 查询匹配的成员
const findMemberByOrgId = (targetId: string, memberList: any[]) => {
	const result: any[] = [];
	if (!memberList.length) return result;
	for (const member of memberList) {
		if (member.orgId === targetId) {
			result.push({
				id: member.id,
				name: member.name,
				orgId: member.orgId,
				orgName: member.orgName,
				gender: member.gender,
				avatar: member.avatar,
				positionName: member.positionName,
				type: "member"
			});
		}
	}
	return result;
};

最终 renderList 里面的数据就是两个数组数据,在两个方法中,分别给目标数据添加了 type 属性,用于区分是部门(可点击进入下一层级)还是人员

数据渲染模板代码如下:

html 复制代码
<template>
    <view class="select-department-member box-border p-x-20rpx p-t-15rpx p-b-20rpx">
        <template v-if="renderList.length">
            <view v-for="(item, index) in renderList" :key="index">
                <template v-if="item.type === 'member'">
                    <view class="item-box flex items-center" @click="handleClick(item)">
                        <view class="icon-box flex justify-center items-center">
                            <image :src="item.avatar" mode="scaleToFill" class="w-60 h-60"
                                style="border-radius: 10rpx;" />
                        </view>
                        <view class="member-name-box">
                            <view class="font-size-26rpx color-#2f3133">{{ item.name }}</view>
                            <view class="font-size-22rpx color-#81878c">{{ item.positionName || '--' }}</view>
                        </view>
                    </view>
                </template>
                <template v-if="item.type === 'department'">
                    <view class="item-box flex items-center" @click="handleClick(item)">
                        <view class="icon-box flex justify-center items-center">
                            <view class="bg-box flex justify-center items-center">
                                <image src="@/static/icons/folder.svg" mode="scaleToFill" class="w-60% h-60%" />
                            </view>
                        </view>
                        <view class="dept-name-box font-size-26rpx color-#2f3133">{{ item.name }}</view>
                    </view>
                </template>
            </view>
            <view class="font-size-26rpx color-#81878c m-t-40rpx m-b-20rpx text-center">共<text class="m-x-5rpx">{{
                totalMember }}</text>人</view>
        </template>
        <template v-else>
            <view class="p-y-150rpx">
                <empty-box paddingTop="10rpx" pWidth="200" pHeight="120"></empty-box>
            </view>
        </template>
    </view>
</template>

里面的子部门在做层级跳转时,始终是在当前页面,所以定义了一个 pathStack 数组来存放当前部门的id与名称。进入下一级就新增,回退时就删除,并根据部门 id 去重新查找数据来更新 renderList 列表。

最后再来一波实操效果,暂时就先这样了,后面看情况再优化改进(os:能不改就不改,有要求再改,哈哈!)

相关推荐
知否技术1 小时前
面试官最爱问的Vue3响应式原理:我给你讲明白了!
前端·vue.js
小周同学:2 小时前
vue将页面导出成word
前端·vue.js·word
wordbaby2 小时前
Vue3滚动轮播组件实现超标信息自动滚动
前端·vue.js
_xaboy3 小时前
基于Vue的低代码可视化表单设计器 FcDesigner 3.2.11更新说明
前端·vue.js·低代码·开源·表单设计器
云浮万里_14 小时前
保姆级教程:Vue3 + Django + MySQL 前后端联调(PyCharm+VSCode版)
vue.js·vscode·mysql·pycharm·django
程序员小刚4 小时前
基于SpringBoot + Vue 的考勤管理系统
vue.js·spring boot·后端
承前智5 小时前
基于Hbuilder X的uni-app连接OneNET云平台及AI交互 实战指南(三)——命令下发
uni-app·交互
code bean5 小时前
【C#】`Task.Factory.StartNew` 和 `Task.Run` 区别
前端·vue.js·c#
倔强青铜三5 小时前
WXT浏览器插件开发中文教程(9)----WXT配置详解之Vite配置
前端·javascript·vue.js
倔强青铜三5 小时前
WXT浏览器插件开发中文教程(10)----WXT配置详解之构建模式
前端·javascript·vue.js