|
|
|
@ -22,7 +22,11 @@ |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 无搜索时展示标签页 --> |
|
|
|
<!-- 无搜索时展示标签页 --> |
|
|
|
<el-tabs v-if="!isSearching" v-model="activeName" @tab-click="handleClick"> |
|
|
|
<el-tabs |
|
|
|
|
|
|
|
v-if="!isSearching" |
|
|
|
|
|
|
|
v-model="activeName" |
|
|
|
|
|
|
|
@tab-click="handleClick" |
|
|
|
|
|
|
|
> |
|
|
|
<!-- 最近联系人 --> |
|
|
|
<!-- 最近联系人 --> |
|
|
|
<el-tab-pane label="最近联系人" name="recentContacts"> |
|
|
|
<el-tab-pane label="最近联系人" name="recentContacts"> |
|
|
|
<div |
|
|
|
<div |
|
|
|
@ -173,6 +177,7 @@ |
|
|
|
node-key="id" |
|
|
|
node-key="id" |
|
|
|
:expand-on-click-node="false" |
|
|
|
:expand-on-click-node="false" |
|
|
|
@node-click="handleOrgNodeClick" |
|
|
|
@node-click="handleOrgNodeClick" |
|
|
|
|
|
|
|
@node-expand="handleOrgNodeClick" |
|
|
|
> |
|
|
|
> |
|
|
|
<template #default="{ node, data }"> |
|
|
|
<template #default="{ node, data }"> |
|
|
|
<div class="custom-tree-node"> |
|
|
|
<div class="custom-tree-node"> |
|
|
|
@ -192,7 +197,16 @@ |
|
|
|
</el-checkbox> |
|
|
|
</el-checkbox> |
|
|
|
</template> |
|
|
|
</template> |
|
|
|
<!-- 用户节点 --> |
|
|
|
<!-- 用户节点 --> |
|
|
|
<div v-else-if="data.type === 'user'" class="userline"> |
|
|
|
<div |
|
|
|
|
|
|
|
v-else-if="data.type === 'user'" |
|
|
|
|
|
|
|
:class="[ |
|
|
|
|
|
|
|
'member-item', |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
offline: !data.online, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
]" |
|
|
|
|
|
|
|
@click="toggleSelect(data)" |
|
|
|
|
|
|
|
> |
|
|
|
<el-checkbox |
|
|
|
<el-checkbox |
|
|
|
:value="isSelected(data.id)" |
|
|
|
:value="isSelected(data.id)" |
|
|
|
@change="toggleSelect(data)" |
|
|
|
@change="toggleSelect(data)" |
|
|
|
@ -397,7 +411,6 @@ export default { |
|
|
|
this.resetPage(); |
|
|
|
this.resetPage(); |
|
|
|
this.loadData(); |
|
|
|
this.loadData(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
// 重置分页、无更多标记、列表数据 |
|
|
|
|
|
|
|
resetPage() { |
|
|
|
resetPage() { |
|
|
|
this.contactPage = 1; |
|
|
|
this.contactPage = 1; |
|
|
|
this.groupPage = 1; |
|
|
|
this.groupPage = 1; |
|
|
|
@ -414,17 +427,10 @@ export default { |
|
|
|
]); |
|
|
|
]); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 获取联系人列表 |
|
|
|
|
|
|
|
* @param {Boolean} isReset true=首页刷新,false=滚动追加 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
async fetchRecentContacts(isReset = false) { |
|
|
|
async fetchRecentContacts(isReset = false) { |
|
|
|
// 已经无更多且不是刷新首页,直接return |
|
|
|
|
|
|
|
if (this.contactNoMore && !isReset) return; |
|
|
|
if (this.contactNoMore && !isReset) return; |
|
|
|
|
|
|
|
|
|
|
|
if (isReset) this.loading = true; |
|
|
|
if (isReset) this.loading = true; |
|
|
|
else this.loadMoreLoading = true; |
|
|
|
else this.loadMoreLoading = true; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
scene: 1, |
|
|
|
scene: 1, |
|
|
|
@ -445,14 +451,11 @@ export default { |
|
|
|
online: item.online === 1 || item.online === true, |
|
|
|
online: item.online === 1 || item.online === true, |
|
|
|
selected: this.isSelected(item.id), |
|
|
|
selected: this.isSelected(item.id), |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
if (isReset) { |
|
|
|
if (isReset) { |
|
|
|
this.memberList = formatList; |
|
|
|
this.memberList = formatList; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.memberList.push(...formatList); |
|
|
|
this.memberList.push(...formatList); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 核心判断:返回条数 < 每页条数,标记无更多,后续不再触底加载 |
|
|
|
|
|
|
|
if (list.length < this.pageSize) { |
|
|
|
if (list.length < this.pageSize) { |
|
|
|
this.contactNoMore = true; |
|
|
|
this.contactNoMore = true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
@ -467,9 +470,7 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 触底加载更多联系人 |
|
|
|
|
|
|
|
loadMoreContacts() { |
|
|
|
loadMoreContacts() { |
|
|
|
// 正在加载 / 无更多 直接阻断 |
|
|
|
|
|
|
|
if (this.loadMoreLoading || this.contactNoMore) return; |
|
|
|
if (this.loadMoreLoading || this.contactNoMore) return; |
|
|
|
this.contactPage++; |
|
|
|
this.contactPage++; |
|
|
|
this.fetchRecentContacts(false); |
|
|
|
this.fetchRecentContacts(false); |
|
|
|
@ -477,7 +478,6 @@ export default { |
|
|
|
|
|
|
|
|
|
|
|
async fetchRecentGroups(isReset = false) { |
|
|
|
async fetchRecentGroups(isReset = false) { |
|
|
|
if (this.groupNoMore && !isReset) return; |
|
|
|
if (this.groupNoMore && !isReset) return; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
scene: 4, |
|
|
|
scene: 4, |
|
|
|
@ -493,14 +493,11 @@ export default { |
|
|
|
allSelected: false, |
|
|
|
allSelected: false, |
|
|
|
members: [], |
|
|
|
members: [], |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
if (isReset) { |
|
|
|
if (isReset) { |
|
|
|
this.recentGroups = formatGroups; |
|
|
|
this.recentGroups = formatGroups; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.recentGroups.push(...formatGroups); |
|
|
|
this.recentGroups.push(...formatGroups); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 核心判断:返回条数不足一页,关闭触底加载 |
|
|
|
|
|
|
|
if (list.length < this.pageSize) { |
|
|
|
if (list.length < this.pageSize) { |
|
|
|
this.groupNoMore = true; |
|
|
|
this.groupNoMore = true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
@ -512,7 +509,6 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 触底加载更多群组 |
|
|
|
|
|
|
|
loadMoreGroups() { |
|
|
|
loadMoreGroups() { |
|
|
|
if (this.loadMoreLoading || this.groupNoMore) return; |
|
|
|
if (this.loadMoreLoading || this.groupNoMore) return; |
|
|
|
this.groupPage++; |
|
|
|
this.groupPage++; |
|
|
|
@ -530,13 +526,11 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 滚动触底统一处理 |
|
|
|
|
|
|
|
handleScroll(e) { |
|
|
|
handleScroll(e) { |
|
|
|
const target = e.target; |
|
|
|
const target = e.target; |
|
|
|
const scrollTop = target.scrollTop; |
|
|
|
const scrollTop = target.scrollTop; |
|
|
|
const scrollHeight = target.scrollHeight; |
|
|
|
const scrollHeight = target.scrollHeight; |
|
|
|
const clientHeight = target.clientHeight; |
|
|
|
const clientHeight = target.clientHeight; |
|
|
|
// 距离底部50px触发加载 |
|
|
|
|
|
|
|
if (scrollTop + clientHeight >= scrollHeight - 50) { |
|
|
|
if (scrollTop + clientHeight >= scrollHeight - 50) { |
|
|
|
if (this.activeName === "recentContacts") { |
|
|
|
if (this.activeName === "recentContacts") { |
|
|
|
this.loadMoreContacts(); |
|
|
|
this.loadMoreContacts(); |
|
|
|
@ -552,21 +546,22 @@ export default { |
|
|
|
id: item.id, |
|
|
|
id: item.id, |
|
|
|
name: item.name, |
|
|
|
name: item.name, |
|
|
|
type: "org", |
|
|
|
type: "org", |
|
|
|
isLoaded: false, |
|
|
|
isLoaded: false, // 是否加载过本部门人员 |
|
|
|
hasChildren: item.child && item.child.length > 0, |
|
|
|
hasChildren: item.child && item.child.length > 0, |
|
|
|
children: |
|
|
|
children: |
|
|
|
item.child && item.child.length > 0 |
|
|
|
item.child && item.child.length > 0 |
|
|
|
? this.buildOrgTree(item.child) |
|
|
|
? this.buildOrgTree(item.child) |
|
|
|
: [], |
|
|
|
: [], |
|
|
|
|
|
|
|
userList: [], // 存储当前部门下的用户 |
|
|
|
})); |
|
|
|
})); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 【重写】任意层级组织节点点击加载人员,不再判断是否有子部门 |
|
|
|
async loadOrgUsers(node) { |
|
|
|
async loadOrgUsers(node) { |
|
|
|
if (node.isLoaded || node.type === "user" || node.type === "all") return; |
|
|
|
// 已经加载过直接返回缓存 |
|
|
|
if (node.hasChildren && node.children && node.children.length > 0) { |
|
|
|
if (node.isLoaded) return; |
|
|
|
node.isLoaded = true; |
|
|
|
// 只处理组织节点 |
|
|
|
return; |
|
|
|
if (node.type !== "org") return; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getGroupsListUser({ group_id: node.id }); |
|
|
|
const res = await getGroupsListUser({ group_id: node.id }); |
|
|
|
const list = res.data?.list || []; |
|
|
|
const list = res.data?.list || []; |
|
|
|
@ -578,6 +573,9 @@ export default { |
|
|
|
type: "user", |
|
|
|
type: "user", |
|
|
|
selected: this.isSelected(item.id), |
|
|
|
selected: this.isSelected(item.id), |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
// 保存当前部门人员到userList缓存 |
|
|
|
|
|
|
|
node.userList = [...users]; |
|
|
|
|
|
|
|
// 拼接全选节点 |
|
|
|
if (users.length > 0) { |
|
|
|
if (users.length > 0) { |
|
|
|
const allSelected = users.every((u) => u.selected); |
|
|
|
const allSelected = users.every((u) => u.selected); |
|
|
|
const selectedCount = users.filter((u) => u.selected).length; |
|
|
|
const selectedCount = users.filter((u) => u.selected).length; |
|
|
|
@ -589,11 +587,14 @@ export default { |
|
|
|
indeterminate: selectedCount > 0 && selectedCount < users.length, |
|
|
|
indeterminate: selectedCount > 0 && selectedCount < users.length, |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
node.children = users; |
|
|
|
|
|
|
|
|
|
|
|
// 【关键改动】先展示原有下级部门,再展示本部门全选、人员 |
|
|
|
|
|
|
|
const originChildNodes = node.children || []; |
|
|
|
|
|
|
|
node.children = [...originChildNodes, ...users]; |
|
|
|
node.isLoaded = true; |
|
|
|
node.isLoaded = true; |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.error(`获取组织 ${node.name} 下的用户失败`, e); |
|
|
|
console.error(`获取组织 ${node.name} 下的用户失败`, e); |
|
|
|
node.children = []; |
|
|
|
node.userList = []; |
|
|
|
node.isLoaded = true; |
|
|
|
node.isLoaded = true; |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
@ -629,7 +630,6 @@ export default { |
|
|
|
online: item.online === 1 || item.online === true, |
|
|
|
online: item.online === 1 || item.online === true, |
|
|
|
selected: this.isSelected(item.id), |
|
|
|
selected: this.isSelected(item.id), |
|
|
|
})); |
|
|
|
})); |
|
|
|
// 搜索结果一次性返回,关闭分页加载 |
|
|
|
|
|
|
|
this.contactNoMore = true; |
|
|
|
this.contactNoMore = true; |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.error("搜索联系人失败", e); |
|
|
|
console.error("搜索联系人失败", e); |
|
|
|
@ -688,13 +688,13 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 树节点点击:任意部门都加载人员 |
|
|
|
async handleOrgNodeClick(data) { |
|
|
|
async handleOrgNodeClick(data) { |
|
|
|
if (data.type === "org") { |
|
|
|
if (data.type === "org") { |
|
|
|
this.$refs.orgTree.setCurrentKey(data.id); |
|
|
|
this.$refs.orgTree.setCurrentKey(data.id); |
|
|
|
if (!data.isLoaded) { |
|
|
|
// 不管有没有子部门,都执行加载人员接口 |
|
|
|
await this.loadOrgUsers(data); |
|
|
|
await this.loadOrgUsers(data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
isSelected(userId) { |
|
|
|
isSelected(userId) { |
|
|
|
@ -1029,19 +1029,63 @@ export default { |
|
|
|
padding: 5px; |
|
|
|
padding: 5px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 组织树用户行修复复选框下沉 |
|
|
|
// 组织树用户行样式 - 与member-item保持一致 |
|
|
|
.userline { |
|
|
|
.userline { |
|
|
|
display: flex; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
align-items: center; |
|
|
|
gap: 10px; |
|
|
|
gap: 10px; |
|
|
|
padding: 8px 16px; |
|
|
|
padding: 8px 12px; |
|
|
|
|
|
|
|
cursor: pointer; |
|
|
|
|
|
|
|
border-radius: 4px; |
|
|
|
|
|
|
|
transition: background 0.2s; |
|
|
|
|
|
|
|
height: 40px; |
|
|
|
|
|
|
|
box-sizing: border-box; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 修复el-checkbox内部垂直偏移 |
|
|
|
::v-deep .el-checkbox { |
|
|
|
::v-deep .el-checkbox { |
|
|
|
display: flex; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
align-items: center; |
|
|
|
|
|
|
|
height: 24px; |
|
|
|
} |
|
|
|
} |
|
|
|
::v-deep .el-checkbox__inner { |
|
|
|
::v-deep .el-checkbox__inner { |
|
|
|
margin-top: 0 !important; |
|
|
|
margin-top: 0 !important; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
::v-deep .el-checkbox__label { |
|
|
|
|
|
|
|
line-height: 24px; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&:hover { |
|
|
|
|
|
|
|
background: #f5f7fa; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&.offline { |
|
|
|
|
|
|
|
.member-name { |
|
|
|
|
|
|
|
color: #909399; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.member-item-label { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
gap: 8px; |
|
|
|
|
|
|
|
height: 24px; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.member-name { |
|
|
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
|
|
color: #303133; |
|
|
|
|
|
|
|
line-height: 24px; |
|
|
|
|
|
|
|
overflow: hidden; |
|
|
|
|
|
|
|
text-overflow: ellipsis; |
|
|
|
|
|
|
|
white-space: nowrap; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.online-dot { |
|
|
|
|
|
|
|
width: 8px; |
|
|
|
|
|
|
|
height: 8px; |
|
|
|
|
|
|
|
border-radius: 50%; |
|
|
|
|
|
|
|
background: #67c23a; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.org-icon { |
|
|
|
.org-icon { |
|
|
|
|