|
|
|
|
@ -42,7 +42,12 @@ |
|
|
|
|
@click="handleRefresh" |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
<div v-loading="loading" class="member-list"> |
|
|
|
|
<!-- 滚动事件只在常用联系人场景生效 --> |
|
|
|
|
<div |
|
|
|
|
v-loading="loading" |
|
|
|
|
class="member-list" |
|
|
|
|
@scroll="handleScroll" |
|
|
|
|
> |
|
|
|
|
<div |
|
|
|
|
v-for="member in memberList" |
|
|
|
|
:key="member.id" |
|
|
|
|
@ -65,6 +70,11 @@ |
|
|
|
|
v-if="!loading && !memberList.length" |
|
|
|
|
description="暂无成员" |
|
|
|
|
/> |
|
|
|
|
<!-- 分页底部提示,仅常用联系人展示 --> |
|
|
|
|
<div v-if="isLatestContacts && memberList.length > 0" class="load-more-tip"> |
|
|
|
|
<span v-if="loadMoreLoading">加载更多...</span> |
|
|
|
|
<span v-if="noMore && !loadMoreLoading">没有更多数据了</span> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
@ -152,6 +162,11 @@ export default { |
|
|
|
|
loading: false, |
|
|
|
|
isLatestContacts: false, |
|
|
|
|
userGroupId: null, |
|
|
|
|
// 分页配置(仅常用联系人/搜索使用) |
|
|
|
|
pageNum: 1, |
|
|
|
|
pageSize: 15, |
|
|
|
|
noMore: false, |
|
|
|
|
loadMoreLoading: false, |
|
|
|
|
// 左侧顶部菜单配置 |
|
|
|
|
leftTabList: [ |
|
|
|
|
{ |
|
|
|
|
@ -197,13 +212,107 @@ export default { |
|
|
|
|
setSelectUser(user) { |
|
|
|
|
this.currentUser = { ...user }; |
|
|
|
|
}, |
|
|
|
|
// 重置分页参数 |
|
|
|
|
resetPage() { |
|
|
|
|
this.pageNum = 1; |
|
|
|
|
this.noMore = false; |
|
|
|
|
this.memberList = []; |
|
|
|
|
}, |
|
|
|
|
// 滚动触底 |
|
|
|
|
handleScroll(e) { |
|
|
|
|
// 组织架构不触发分页加载,直接返回 |
|
|
|
|
if (!this.isLatestContacts) return; |
|
|
|
|
const el = e.target; |
|
|
|
|
const scrollTop = el.scrollTop; |
|
|
|
|
const scrollHeight = el.scrollHeight; |
|
|
|
|
const clientHeight = el.clientHeight; |
|
|
|
|
// 距离底部50px触发加载 |
|
|
|
|
if (scrollTop + clientHeight >= scrollHeight - 50) { |
|
|
|
|
this.loadNextPage(); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 加载下一页 |
|
|
|
|
loadNextPage() { |
|
|
|
|
// 多重拦截:加载中 / 无更多数据 |
|
|
|
|
if (this.loadMoreLoading || this.noMore || this.loading) return; |
|
|
|
|
this.pageNum++; |
|
|
|
|
this.loadMemberList(false); |
|
|
|
|
}, |
|
|
|
|
// 统一加载列表 |
|
|
|
|
// isRefresh: true=第一页刷新 false=滚动追加 |
|
|
|
|
async loadMemberList(isRefresh = true) { |
|
|
|
|
if (isRefresh) { |
|
|
|
|
this.loading = true; |
|
|
|
|
this.resetPage(); |
|
|
|
|
} else { |
|
|
|
|
this.loadMoreLoading = true; |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
const keyword = this.searchText.trim(); |
|
|
|
|
let resList = []; |
|
|
|
|
// 搜索场景(分页) |
|
|
|
|
if (keyword) { |
|
|
|
|
const { data } = await searchUsers({ |
|
|
|
|
search_key: keyword, |
|
|
|
|
user_ids: [], |
|
|
|
|
page: this.pageNum, |
|
|
|
|
size: this.pageSize, |
|
|
|
|
}); |
|
|
|
|
resList = data.list || []; |
|
|
|
|
this.isLatestContacts = true; |
|
|
|
|
this.currentStructureName = "搜索结果"; |
|
|
|
|
this.currentGroupId = null; |
|
|
|
|
this.$refs.orgTree.setCurrentKey(null); |
|
|
|
|
} else if (this.isLatestContacts) { |
|
|
|
|
// 常用联系人(分页) |
|
|
|
|
const { data } = await getMessagesLatestContacts({ |
|
|
|
|
scene: 1, |
|
|
|
|
page: this.pageNum, |
|
|
|
|
size: this.pageSize, |
|
|
|
|
}); |
|
|
|
|
resList = data.list || []; |
|
|
|
|
} else { |
|
|
|
|
// 组织架构:不分页,一次性全量获取 |
|
|
|
|
const { data } = await getGroupsListUser({ group_id: this.currentGroupId }); |
|
|
|
|
resList = data.list || []; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const formatList = resList.map(item => ({ |
|
|
|
|
...item, |
|
|
|
|
online: [1, true].includes(item.online) |
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
if (isRefresh) { |
|
|
|
|
this.memberList = formatList; |
|
|
|
|
} else { |
|
|
|
|
this.memberList.push(...formatList); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 仅常用/搜索场景判断是否还有下一页 |
|
|
|
|
if (this.isLatestContacts) { |
|
|
|
|
this.noMore = formatList.length < this.pageSize; |
|
|
|
|
} else { |
|
|
|
|
this.noMore = true; // 组织架构永远无更多 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 首次加载自动选中第一条 |
|
|
|
|
if (isRefresh && this.memberList.length) { |
|
|
|
|
this.setSelectUser(this.memberList[0]); |
|
|
|
|
} |
|
|
|
|
} catch (err) { |
|
|
|
|
console.error("加载列表失败", err); |
|
|
|
|
if (isRefresh) this.memberList = []; |
|
|
|
|
} finally { |
|
|
|
|
this.loading = false; |
|
|
|
|
this.loadMoreLoading = false; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 树形结构相关 |
|
|
|
|
async fetchOrgTree() { |
|
|
|
|
try { |
|
|
|
|
const { data } = await getGroupsList(); |
|
|
|
|
this.treeData = data.list; |
|
|
|
|
// 默认只展开一级节点 |
|
|
|
|
this.defaultExpandedKeys = this.treeData.map((node) => node.id); |
|
|
|
|
this.defaultExpandedKeys = this.treeData.map(node => node.id); |
|
|
|
|
if (!this.userGroupId) return; |
|
|
|
|
const parentKeys = this.findParentKeys(this.treeData, this.userGroupId); |
|
|
|
|
this.$nextTick(() => { |
|
|
|
|
@ -211,7 +320,8 @@ export default { |
|
|
|
|
const node = this.findNodeById(this.treeData, this.userGroupId); |
|
|
|
|
if (node) { |
|
|
|
|
this.currentStructureName = node.name; |
|
|
|
|
this.fetchMembers(this.userGroupId); |
|
|
|
|
this.currentGroupId = node.id; |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} catch (err) { |
|
|
|
|
@ -221,11 +331,8 @@ export default { |
|
|
|
|
findParentKeys(tree, targetId, parents = []) { |
|
|
|
|
for (const node of tree) { |
|
|
|
|
if (node.id === targetId) return [...parents, node.id]; |
|
|
|
|
if (node.children.length) { |
|
|
|
|
const res = this.findParentKeys(node.children, targetId, [ |
|
|
|
|
...parents, |
|
|
|
|
node.id, |
|
|
|
|
]); |
|
|
|
|
if (node.child && node.child.length) { |
|
|
|
|
const res = this.findParentKeys(node.child, targetId, [...parents, node.id]); |
|
|
|
|
if (res.length) return res; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -234,97 +341,56 @@ export default { |
|
|
|
|
findNodeById(tree, id) { |
|
|
|
|
for (const node of tree) { |
|
|
|
|
if (node.id === id) return node; |
|
|
|
|
if (node.children) { |
|
|
|
|
const res = this.findNodeById(node.children, id); |
|
|
|
|
if (node.child) { |
|
|
|
|
const res = this.findNodeById(node.child, id); |
|
|
|
|
if (res) return res; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
}, |
|
|
|
|
// 树点击 |
|
|
|
|
// 树点击(组织架构) |
|
|
|
|
handleNodeClick(data) { |
|
|
|
|
this.isLatestContacts = false; |
|
|
|
|
this.currentStructureName = data.name; |
|
|
|
|
this.currentGroupId = data.id; |
|
|
|
|
this.currentUser = null; |
|
|
|
|
this.fetchMembers(data.id); |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
}, |
|
|
|
|
// 获取部门成员 |
|
|
|
|
async fetchMembers(groupId) { |
|
|
|
|
this.loading = true; |
|
|
|
|
try { |
|
|
|
|
const { data } = await getGroupsListUser({ group_id: groupId }); |
|
|
|
|
this.memberList = data.list || []; |
|
|
|
|
this.memberList.length && this.setSelectUser(this.memberList[0]); |
|
|
|
|
} catch (err) { |
|
|
|
|
console.error("获取成员失败", err); |
|
|
|
|
this.memberList = []; |
|
|
|
|
} finally { |
|
|
|
|
this.loading = false; |
|
|
|
|
} |
|
|
|
|
// 兼容旧接口 |
|
|
|
|
fetchMembers(groupId) { |
|
|
|
|
this.currentGroupId = groupId; |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
}, |
|
|
|
|
// 常用联系人 |
|
|
|
|
async showLatestContacts() { |
|
|
|
|
// 常用联系人入口 |
|
|
|
|
showLatestContacts() { |
|
|
|
|
this.isLatestContacts = true; |
|
|
|
|
this.currentStructureName = "最近联系人"; |
|
|
|
|
this.currentGroupId = null; |
|
|
|
|
this.currentUser = null; |
|
|
|
|
this.$refs.orgTree.setCurrentKey(null); |
|
|
|
|
this.loading = true; |
|
|
|
|
try { |
|
|
|
|
const { data } = await getMessagesLatestContacts({ |
|
|
|
|
scene: 1, |
|
|
|
|
page: 1, |
|
|
|
|
size: 65535, |
|
|
|
|
}); |
|
|
|
|
this.memberList = data.list || []; |
|
|
|
|
this.memberList.length && this.setSelectUser(this.memberList[0]); |
|
|
|
|
} catch (err) { |
|
|
|
|
console.error("常用联系人异常", err); |
|
|
|
|
this.memberList = []; |
|
|
|
|
} finally { |
|
|
|
|
this.loading = false; |
|
|
|
|
} |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
}, |
|
|
|
|
// 搜索 |
|
|
|
|
async handleSearch() { |
|
|
|
|
handleSearch() { |
|
|
|
|
const keyword = this.searchText.trim(); |
|
|
|
|
if (!keyword) return this.handleClearSearch(); |
|
|
|
|
this.loading = true; |
|
|
|
|
try { |
|
|
|
|
const { data } = await searchUsers({ |
|
|
|
|
search_key: keyword, |
|
|
|
|
user_ids: [], |
|
|
|
|
page: 1, |
|
|
|
|
size: 10000, |
|
|
|
|
}); |
|
|
|
|
this.isLatestContacts = true; |
|
|
|
|
this.currentStructureName = "搜索结果"; |
|
|
|
|
this.currentGroupId = null; |
|
|
|
|
this.$refs.orgTree.setCurrentKey(null); |
|
|
|
|
this.memberList = (data.list || []).map((item) => ({ |
|
|
|
|
...item, |
|
|
|
|
online: [1, true].includes(item.online), |
|
|
|
|
})); |
|
|
|
|
this.memberList.length && this.setSelectUser(this.memberList[0]); |
|
|
|
|
} catch (err) { |
|
|
|
|
console.error("搜索失败", err); |
|
|
|
|
} finally { |
|
|
|
|
this.loading = false; |
|
|
|
|
} |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
}, |
|
|
|
|
// 清空搜索 |
|
|
|
|
handleClearSearch() { |
|
|
|
|
this.searchText = ""; |
|
|
|
|
if (this.isLatestContacts) this.showLatestContacts(); |
|
|
|
|
else if (this.currentGroupId) this.fetchMembers(this.currentGroupId); |
|
|
|
|
else if (this.userGroupId) this.fetchMembers(this.userGroupId); |
|
|
|
|
if (this.isLatestContacts) { |
|
|
|
|
this.showLatestContacts(); |
|
|
|
|
} else if (this.currentGroupId) { |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
} else if (this.userGroupId) { |
|
|
|
|
this.currentGroupId = this.userGroupId; |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 刷新 |
|
|
|
|
handleRefresh() { |
|
|
|
|
this.isLatestContacts |
|
|
|
|
? this.showLatestContacts() |
|
|
|
|
: this.fetchMembers(this.currentGroupId); |
|
|
|
|
this.loadMemberList(true); |
|
|
|
|
}, |
|
|
|
|
// 跳转发消息 |
|
|
|
|
sendMessage() { |
|
|
|
|
@ -450,16 +516,14 @@ export default { |
|
|
|
|
flex-shrink: 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.load-more { |
|
|
|
|
.load-more-tip { |
|
|
|
|
padding: 12px; |
|
|
|
|
text-align: center; |
|
|
|
|
.loading-text { |
|
|
|
|
font-size: 12px; |
|
|
|
|
color: #909399; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// 右侧详情 |
|
|
|
|
.addrbook-right { |
|
|
|
|
flex: 1; |
|
|
|
|
|