|
|
|
@ -21,11 +21,15 @@ |
|
|
|
style="margin-bottom: 10px" |
|
|
|
style="margin-bottom: 10px" |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 标签页 --> |
|
|
|
<!-- 无搜索时展示标签页 --> |
|
|
|
<el-tabs 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 v-loading="loading" class="contact-list"> |
|
|
|
<div |
|
|
|
|
|
|
|
v-loading="loading" |
|
|
|
|
|
|
|
class="contact-list" |
|
|
|
|
|
|
|
@scroll="handleScroll" |
|
|
|
|
|
|
|
> |
|
|
|
<div |
|
|
|
<div |
|
|
|
v-for="member in displayMembers" |
|
|
|
v-for="member in displayMembers" |
|
|
|
:key="member.id" |
|
|
|
:key="member.id" |
|
|
|
@ -60,12 +64,25 @@ |
|
|
|
v-if="!loading && displayMembers.length === 0" |
|
|
|
v-if="!loading && displayMembers.length === 0" |
|
|
|
description="暂无联系人" |
|
|
|
description="暂无联系人" |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
<div v-if="loadMoreLoading" class="load-more-tip"> |
|
|
|
|
|
|
|
加载更多... |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div |
|
|
|
|
|
|
|
v-if="contactNoMore && displayMembers.length > 0" |
|
|
|
|
|
|
|
class="load-more-tip" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
没有更多数据了 |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
</el-tab-pane> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 最近群组 --> |
|
|
|
<!-- 最近群组 --> |
|
|
|
<el-tab-pane label="最近群组" name="recentGroups"> |
|
|
|
<el-tab-pane label="最近群组" name="recentGroups"> |
|
|
|
<div v-loading="loading" class="contact-list"> |
|
|
|
<div |
|
|
|
|
|
|
|
v-loading="loading" |
|
|
|
|
|
|
|
class="contact-list" |
|
|
|
|
|
|
|
@scroll="handleScroll" |
|
|
|
|
|
|
|
> |
|
|
|
<div v-for="group in recentGroups" :key="group.id"> |
|
|
|
<div v-for="group in recentGroups" :key="group.id"> |
|
|
|
<!-- 群组标题 --> |
|
|
|
<!-- 群组标题 --> |
|
|
|
<div |
|
|
|
<div |
|
|
|
@ -133,6 +150,15 @@ |
|
|
|
v-if="!loading && recentGroups.length === 0" |
|
|
|
v-if="!loading && recentGroups.length === 0" |
|
|
|
description="暂无群组" |
|
|
|
description="暂无群组" |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
<div v-if="loadMoreLoading" class="load-more-tip"> |
|
|
|
|
|
|
|
加载更多... |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div |
|
|
|
|
|
|
|
v-if="groupNoMore && recentGroups.length > 0" |
|
|
|
|
|
|
|
class="load-more-tip" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
没有更多数据了 |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
</el-tab-pane> |
|
|
|
|
|
|
|
|
|
|
|
@ -196,6 +222,47 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-tab-pane> |
|
|
|
</el-tab-pane> |
|
|
|
</el-tabs> |
|
|
|
</el-tabs> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 有搜索内容:隐藏tab,展示搜索结果同事列表 --> |
|
|
|
|
|
|
|
<div v-else class="search-result-wrap"> |
|
|
|
|
|
|
|
<div class="search-title">同事</div> |
|
|
|
|
|
|
|
<div v-loading="loading" class="contact-list"> |
|
|
|
|
|
|
|
<div |
|
|
|
|
|
|
|
v-for="member in displayMembers" |
|
|
|
|
|
|
|
:key="member.id" |
|
|
|
|
|
|
|
:class="[ |
|
|
|
|
|
|
|
'member-item', |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
active: isSelected(member.id), |
|
|
|
|
|
|
|
offline: !member.online, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
]" |
|
|
|
|
|
|
|
@click="toggleSelect(member)" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<el-checkbox |
|
|
|
|
|
|
|
v-model="member.selected" |
|
|
|
|
|
|
|
:disabled="isSelectDisabled && !member.selected" |
|
|
|
|
|
|
|
@change="toggleSelect(member)" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<div class="member-item-label"> |
|
|
|
|
|
|
|
<el-avatar |
|
|
|
|
|
|
|
:src=" |
|
|
|
|
|
|
|
$store.state.user.netConfig.MINIO_ENDPOINT_HTTPS + |
|
|
|
|
|
|
|
member.avatar |
|
|
|
|
|
|
|
" |
|
|
|
|
|
|
|
icon="el-icon-user-solid" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
<span class="member-name">{{ member.name }}</span> |
|
|
|
|
|
|
|
<span v-if="member.online" class="online-dot" /> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</el-checkbox> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<el-empty |
|
|
|
|
|
|
|
v-if="!loading && displayMembers.length === 0" |
|
|
|
|
|
|
|
description="未匹配到同事" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</el-col> |
|
|
|
</el-col> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧:已选人员 --> |
|
|
|
<!-- 右侧:已选人员 --> |
|
|
|
@ -299,11 +366,18 @@ export default { |
|
|
|
activeName: "recentContacts", |
|
|
|
activeName: "recentContacts", |
|
|
|
searchText: "", |
|
|
|
searchText: "", |
|
|
|
loading: false, |
|
|
|
loading: false, |
|
|
|
|
|
|
|
loadMoreLoading: false, |
|
|
|
confirmLoading: false, |
|
|
|
confirmLoading: false, |
|
|
|
selectedMembers: [], |
|
|
|
selectedMembers: [], |
|
|
|
|
|
|
|
contactPage: 1, |
|
|
|
|
|
|
|
groupPage: 1, |
|
|
|
|
|
|
|
pageSize: 10, |
|
|
|
memberList: [], |
|
|
|
memberList: [], |
|
|
|
recentGroups: [], |
|
|
|
recentGroups: [], |
|
|
|
treeData: [], |
|
|
|
treeData: [], |
|
|
|
|
|
|
|
contactNoMore: false, // 联系人无更多标识 |
|
|
|
|
|
|
|
groupNoMore: false, // 群组无更多标识 |
|
|
|
|
|
|
|
isSearching: false, |
|
|
|
}; |
|
|
|
}; |
|
|
|
}, |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
computed: { |
|
|
|
@ -320,27 +394,45 @@ export default { |
|
|
|
methods: { |
|
|
|
methods: { |
|
|
|
show() { |
|
|
|
show() { |
|
|
|
this.visible = true; |
|
|
|
this.visible = true; |
|
|
|
|
|
|
|
this.resetPage(); |
|
|
|
this.loadData(); |
|
|
|
this.loadData(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
// 重置分页、无更多标记、列表数据 |
|
|
|
|
|
|
|
resetPage() { |
|
|
|
|
|
|
|
this.contactPage = 1; |
|
|
|
|
|
|
|
this.groupPage = 1; |
|
|
|
|
|
|
|
this.contactNoMore = false; |
|
|
|
|
|
|
|
this.groupNoMore = false; |
|
|
|
|
|
|
|
this.memberList = []; |
|
|
|
|
|
|
|
this.recentGroups = []; |
|
|
|
|
|
|
|
}, |
|
|
|
async loadData() { |
|
|
|
async loadData() { |
|
|
|
await Promise.all([ |
|
|
|
await Promise.all([ |
|
|
|
this.fetchRecentContacts(), |
|
|
|
this.fetchRecentContacts(true), |
|
|
|
this.fetchRecentGroups(), |
|
|
|
this.fetchRecentGroups(true), |
|
|
|
this.fetchOrgTree(), |
|
|
|
this.fetchOrgTree(), |
|
|
|
]); |
|
|
|
]); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 获取最近联系人 |
|
|
|
/** |
|
|
|
async fetchRecentContacts() { |
|
|
|
* 获取联系人列表 |
|
|
|
this.loading = true; |
|
|
|
* @param {Boolean} isReset true=首页刷新,false=滚动追加 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
async fetchRecentContacts(isReset = false) { |
|
|
|
|
|
|
|
// 已经无更多且不是刷新首页,直接return |
|
|
|
|
|
|
|
if (this.contactNoMore && !isReset) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isReset) this.loading = true; |
|
|
|
|
|
|
|
else this.loadMoreLoading = true; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
scene: 1, |
|
|
|
scene: 1, |
|
|
|
page: 1, |
|
|
|
page: this.contactPage, |
|
|
|
size: 65535, |
|
|
|
size: this.pageSize, |
|
|
|
}); |
|
|
|
}); |
|
|
|
const list = res.data?.list || []; |
|
|
|
const list = res.data?.list || []; |
|
|
|
this.memberList = list.map((item) => ({ |
|
|
|
const formatList = list.map((item) => ({ |
|
|
|
id: item.id, |
|
|
|
id: item.id, |
|
|
|
name: item.name, |
|
|
|
name: item.name, |
|
|
|
role: item.role, |
|
|
|
role: item.role, |
|
|
|
@ -353,24 +445,47 @@ 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) { |
|
|
|
|
|
|
|
this.memberList = formatList; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.memberList.push(...formatList); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 核心判断:返回条数 < 每页条数,标记无更多,后续不再触底加载 |
|
|
|
|
|
|
|
if (list.length < this.pageSize) { |
|
|
|
|
|
|
|
this.contactNoMore = true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.contactNoMore = false; |
|
|
|
|
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.error("获取最近联系人失败", e); |
|
|
|
console.error("获取最近联系人失败", e); |
|
|
|
this.memberList = []; |
|
|
|
if (isReset) this.memberList = []; |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
this.loading = false; |
|
|
|
this.loading = false; |
|
|
|
|
|
|
|
this.loadMoreLoading = false; |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 获取最近群组 |
|
|
|
// 触底加载更多联系人 |
|
|
|
async fetchRecentGroups() { |
|
|
|
loadMoreContacts() { |
|
|
|
|
|
|
|
// 正在加载 / 无更多 直接阻断 |
|
|
|
|
|
|
|
if (this.loadMoreLoading || this.contactNoMore) return; |
|
|
|
|
|
|
|
this.contactPage++; |
|
|
|
|
|
|
|
this.fetchRecentContacts(false); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async fetchRecentGroups(isReset = false) { |
|
|
|
|
|
|
|
if (this.groupNoMore && !isReset) return; |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
const res = await getMessagesLatestContacts({ |
|
|
|
scene: 4, |
|
|
|
scene: 4, |
|
|
|
page: 1, |
|
|
|
page: this.groupPage, |
|
|
|
size: 65535, |
|
|
|
size: this.pageSize, |
|
|
|
}); |
|
|
|
}); |
|
|
|
const list = res.data?.list || []; |
|
|
|
const list = res.data?.list || []; |
|
|
|
this.recentGroups = list.map((item) => ({ |
|
|
|
const formatGroups = list.map((item) => ({ |
|
|
|
id: item.id, |
|
|
|
id: item.id, |
|
|
|
name: item.name, |
|
|
|
name: item.name, |
|
|
|
avatar: item.avatar, |
|
|
|
avatar: item.avatar, |
|
|
|
@ -378,13 +493,32 @@ export default { |
|
|
|
allSelected: false, |
|
|
|
allSelected: false, |
|
|
|
members: [], |
|
|
|
members: [], |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isReset) { |
|
|
|
|
|
|
|
this.recentGroups = formatGroups; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.recentGroups.push(...formatGroups); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 核心判断:返回条数不足一页,关闭触底加载 |
|
|
|
|
|
|
|
if (list.length < this.pageSize) { |
|
|
|
|
|
|
|
this.groupNoMore = true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.groupNoMore = false; |
|
|
|
|
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.error("获取最近群组失败", e); |
|
|
|
console.error("获取最近群组失败", e); |
|
|
|
this.recentGroups = []; |
|
|
|
if (isReset) this.recentGroups = []; |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 获取组织架构树 |
|
|
|
// 触底加载更多群组 |
|
|
|
|
|
|
|
loadMoreGroups() { |
|
|
|
|
|
|
|
if (this.loadMoreLoading || this.groupNoMore) return; |
|
|
|
|
|
|
|
this.groupPage++; |
|
|
|
|
|
|
|
this.fetchRecentGroups(false); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
async fetchOrgTree() { |
|
|
|
async fetchOrgTree() { |
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getGroupsList(); |
|
|
|
const res = await getGroupsList(); |
|
|
|
@ -396,7 +530,22 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 构建组织架构树 |
|
|
|
// 滚动触底统一处理 |
|
|
|
|
|
|
|
handleScroll(e) { |
|
|
|
|
|
|
|
const target = e.target; |
|
|
|
|
|
|
|
const scrollTop = target.scrollTop; |
|
|
|
|
|
|
|
const scrollHeight = target.scrollHeight; |
|
|
|
|
|
|
|
const clientHeight = target.clientHeight; |
|
|
|
|
|
|
|
// 距离底部50px触发加载 |
|
|
|
|
|
|
|
if (scrollTop + clientHeight >= scrollHeight - 50) { |
|
|
|
|
|
|
|
if (this.activeName === "recentContacts") { |
|
|
|
|
|
|
|
this.loadMoreContacts(); |
|
|
|
|
|
|
|
} else if (this.activeName === "recentGroups") { |
|
|
|
|
|
|
|
this.loadMoreGroups(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
buildOrgTree(list) { |
|
|
|
buildOrgTree(list) { |
|
|
|
if (!list || list.length === 0) return []; |
|
|
|
if (!list || list.length === 0) return []; |
|
|
|
return list.map((item) => ({ |
|
|
|
return list.map((item) => ({ |
|
|
|
@ -412,7 +561,6 @@ export default { |
|
|
|
})); |
|
|
|
})); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 加载组织下的用户 |
|
|
|
|
|
|
|
async loadOrgUsers(node) { |
|
|
|
async loadOrgUsers(node) { |
|
|
|
if (node.isLoaded || node.type === "user" || node.type === "all") return; |
|
|
|
if (node.isLoaded || node.type === "user" || node.type === "all") return; |
|
|
|
if (node.hasChildren && node.children && node.children.length > 0) { |
|
|
|
if (node.hasChildren && node.children && node.children.length > 0) { |
|
|
|
@ -450,13 +598,15 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 搜索联系人 |
|
|
|
|
|
|
|
async handleSearch() { |
|
|
|
async handleSearch() { |
|
|
|
const text = this.searchText.trim(); |
|
|
|
const text = this.searchText.trim(); |
|
|
|
if (!text) { |
|
|
|
if (!text) { |
|
|
|
await this.fetchRecentContacts(); |
|
|
|
this.isSearching = false; |
|
|
|
|
|
|
|
this.resetPage(); |
|
|
|
|
|
|
|
await this.fetchRecentContacts(true); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.isSearching = true; |
|
|
|
this.loading = true; |
|
|
|
this.loading = true; |
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await searchUsers({ |
|
|
|
const res = await searchUsers({ |
|
|
|
@ -479,6 +629,8 @@ 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; |
|
|
|
} catch (e) { |
|
|
|
} catch (e) { |
|
|
|
console.error("搜索联系人失败", e); |
|
|
|
console.error("搜索联系人失败", e); |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
@ -486,21 +638,16 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 清除搜索 |
|
|
|
|
|
|
|
handleClearSearch() { |
|
|
|
handleClearSearch() { |
|
|
|
this.searchText = ""; |
|
|
|
this.searchText = ""; |
|
|
|
this.fetchRecentContacts(); |
|
|
|
this.resetPage(); |
|
|
|
|
|
|
|
this.fetchRecentContacts(true); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 点击标签页 |
|
|
|
|
|
|
|
handleClick(tab) { |
|
|
|
handleClick(tab) { |
|
|
|
this.activeName = tab.name; |
|
|
|
this.activeName = tab.name; |
|
|
|
if (tab.name === "recentGroups") { |
|
|
|
|
|
|
|
this.loadGroupMembers(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 加载群组成员 |
|
|
|
|
|
|
|
async loadGroupMembers() { |
|
|
|
async loadGroupMembers() { |
|
|
|
for (const group of this.recentGroups) { |
|
|
|
for (const group of this.recentGroups) { |
|
|
|
if (!group.members || group.members.length === 0) { |
|
|
|
if (!group.members || group.members.length === 0) { |
|
|
|
@ -522,17 +669,13 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 切换群组展开 |
|
|
|
|
|
|
|
async toggleGroupExpand(group) { |
|
|
|
async toggleGroupExpand(group) { |
|
|
|
group.expanded = !group.expanded; |
|
|
|
group.expanded = !group.expanded; |
|
|
|
if (group.expanded) { |
|
|
|
if (group.expanded) { |
|
|
|
// 调用 getMultiChatInfo 接口获取群详细信息 |
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
const res = await getMultiChatInfo({ multi_chat_id: group.id }); |
|
|
|
const res = await getMultiChatInfo({ multi_chat_id: group.id }); |
|
|
|
const data = res.data || {}; |
|
|
|
const data = res.data || {}; |
|
|
|
// 使用接口返回的成员列表,过滤掉与 user_id 相同的用户 |
|
|
|
|
|
|
|
const userList = data.user_list || []; |
|
|
|
const userList = data.user_list || []; |
|
|
|
// 获取当前用户ID,用于过滤掉自己 |
|
|
|
|
|
|
|
group.members = userList |
|
|
|
group.members = userList |
|
|
|
.filter((item) => item.id !== data.user_id) |
|
|
|
.filter((item) => item.id !== data.user_id) |
|
|
|
.map((item) => { |
|
|
|
.map((item) => { |
|
|
|
@ -545,23 +688,19 @@ 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) { |
|
|
|
if (!data.isLoaded) { |
|
|
|
await this.loadOrgUsers(data); |
|
|
|
await this.loadOrgUsers(data); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 判断是否已选中 |
|
|
|
|
|
|
|
isSelected(userId) { |
|
|
|
isSelected(userId) { |
|
|
|
return this.selectedMembers.some((m) => m.id === userId); |
|
|
|
return this.selectedMembers.some((m) => m.id === userId); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 切换选择状态 |
|
|
|
|
|
|
|
toggleSelect(member) { |
|
|
|
toggleSelect(member) { |
|
|
|
if (this.isSelectDisabled && !member.selected) { |
|
|
|
if (this.isSelectDisabled && !member.selected) { |
|
|
|
this.$message.warning(`最多只能选择${this.maxSelectCount}个成员`); |
|
|
|
this.$message.warning(`最多只能选择${this.maxSelectCount}个成员`); |
|
|
|
@ -582,13 +721,10 @@ export default { |
|
|
|
this.syncSelection(member); |
|
|
|
this.syncSelection(member); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 同步选择状态 |
|
|
|
|
|
|
|
syncSelection(member) { |
|
|
|
syncSelection(member) { |
|
|
|
// 同步最近联系人 |
|
|
|
|
|
|
|
this.memberList.forEach((m) => { |
|
|
|
this.memberList.forEach((m) => { |
|
|
|
if (m.id === member.id) m.selected = member.selected; |
|
|
|
if (m.id === member.id) m.selected = member.selected; |
|
|
|
}); |
|
|
|
}); |
|
|
|
// 同步最近群组 |
|
|
|
|
|
|
|
this.recentGroups.forEach((group) => { |
|
|
|
this.recentGroups.forEach((group) => { |
|
|
|
if (group.members) { |
|
|
|
if (group.members) { |
|
|
|
const groupMember = group.members.find((m) => m.id === member.id); |
|
|
|
const groupMember = group.members.find((m) => m.id === member.id); |
|
|
|
@ -598,11 +734,9 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
// 同步组织架构 |
|
|
|
|
|
|
|
this.syncOrgTreeSelection(this.treeData, member); |
|
|
|
this.syncOrgTreeSelection(this.treeData, member); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 同步组织架构选择状态 |
|
|
|
|
|
|
|
syncOrgTreeSelection(nodes, member) { |
|
|
|
syncOrgTreeSelection(nodes, member) { |
|
|
|
nodes.forEach((node) => { |
|
|
|
nodes.forEach((node) => { |
|
|
|
if (node.id === member.id && node.type === "user") { |
|
|
|
if (node.id === member.id && node.type === "user") { |
|
|
|
@ -615,7 +749,6 @@ export default { |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 切换群组全选 |
|
|
|
|
|
|
|
toggleGroupAllSelect(group) { |
|
|
|
toggleGroupAllSelect(group) { |
|
|
|
if (!group.members || group.members.length === 0) return; |
|
|
|
if (!group.members || group.members.length === 0) return; |
|
|
|
if (group.allSelected) { |
|
|
|
if (group.allSelected) { |
|
|
|
@ -624,16 +757,14 @@ export default { |
|
|
|
const index = this.selectedMembers.findIndex( |
|
|
|
const index = this.selectedMembers.findIndex( |
|
|
|
(m) => m.id === member.id |
|
|
|
(m) => m.id === member.id |
|
|
|
); |
|
|
|
); |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) this.selectedMembers.splice(index, 1); |
|
|
|
this.selectedMembers.splice(index, 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
group.allSelected = false; |
|
|
|
group.allSelected = false; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
const selectedCount = group.members.filter((m) => !m.selected).length; |
|
|
|
const unselectedCount = group.members.filter((m) => !m.selected).length; |
|
|
|
if ( |
|
|
|
if ( |
|
|
|
this.maxSelectCount > 0 && |
|
|
|
this.maxSelectCount > 0 && |
|
|
|
this.selectedMembers.length + selectedCount > this.maxSelectCount |
|
|
|
this.selectedMembers.length + unselectedCount > this.maxSelectCount |
|
|
|
) { |
|
|
|
) { |
|
|
|
this.$message.warning(`最多只能选择${this.maxSelectCount}个成员`); |
|
|
|
this.$message.warning(`最多只能选择${this.maxSelectCount}个成员`); |
|
|
|
return; |
|
|
|
return; |
|
|
|
@ -653,14 +784,10 @@ export default { |
|
|
|
this.syncGroupSelection(group); |
|
|
|
this.syncGroupSelection(group); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 同步群组选择到其他列表 |
|
|
|
|
|
|
|
syncGroupSelection(group) { |
|
|
|
syncGroupSelection(group) { |
|
|
|
group.members.forEach((member) => { |
|
|
|
group.members.forEach((member) => this.syncSelection(member)); |
|
|
|
this.syncSelection(member); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 组织架构全选 |
|
|
|
|
|
|
|
handleOrgCheckAllChange(allNode) { |
|
|
|
handleOrgCheckAllChange(allNode) { |
|
|
|
const parentNode = this.findParentNode(this.treeData, allNode.id); |
|
|
|
const parentNode = this.findParentNode(this.treeData, allNode.id); |
|
|
|
if (!parentNode || !parentNode.children) return; |
|
|
|
if (!parentNode || !parentNode.children) return; |
|
|
|
@ -693,16 +820,13 @@ export default { |
|
|
|
users.forEach((user) => { |
|
|
|
users.forEach((user) => { |
|
|
|
user.selected = false; |
|
|
|
user.selected = false; |
|
|
|
const index = this.selectedMembers.findIndex((m) => m.id === user.id); |
|
|
|
const index = this.selectedMembers.findIndex((m) => m.id === user.id); |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) this.selectedMembers.splice(index, 1); |
|
|
|
this.selectedMembers.splice(index, 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
allNode.indeterminate = false; |
|
|
|
allNode.indeterminate = false; |
|
|
|
} |
|
|
|
} |
|
|
|
users.forEach((user) => this.syncSelection(user)); |
|
|
|
users.forEach((user) => this.syncSelection(user)); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 查找父节点 |
|
|
|
|
|
|
|
findParentNode(nodes, childId, parent = null) { |
|
|
|
findParentNode(nodes, childId, parent = null) { |
|
|
|
for (const node of nodes) { |
|
|
|
for (const node of nodes) { |
|
|
|
if (node.id === childId) return parent; |
|
|
|
if (node.id === childId) return parent; |
|
|
|
@ -714,7 +838,6 @@ export default { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 更新父节点全选状态 |
|
|
|
|
|
|
|
updateParentAllSelectStatus(userNode) { |
|
|
|
updateParentAllSelectStatus(userNode) { |
|
|
|
const parentNode = this.findParentNode(this.treeData, userNode.id); |
|
|
|
const parentNode = this.findParentNode(this.treeData, userNode.id); |
|
|
|
if (!parentNode || !parentNode.children) return; |
|
|
|
if (!parentNode || !parentNode.children) return; |
|
|
|
@ -728,17 +851,14 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 移除已选人员 |
|
|
|
|
|
|
|
removeSelected(member) { |
|
|
|
removeSelected(member) { |
|
|
|
const index = this.selectedMembers.findIndex((m) => m.id === member.id); |
|
|
|
const index = this.selectedMembers.findIndex((m) => m.id === member.id); |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) { |
|
|
|
this.selectedMembers.splice(index, 1); |
|
|
|
this.selectedMembers.splice(index, 1); |
|
|
|
member.selected = false; |
|
|
|
|
|
|
|
this.syncSelection({ id: member.id, selected: false }); |
|
|
|
this.syncSelection({ id: member.id, selected: false }); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 清空所有选择 |
|
|
|
|
|
|
|
clearAllSelected() { |
|
|
|
clearAllSelected() { |
|
|
|
this.selectedMembers = []; |
|
|
|
this.selectedMembers = []; |
|
|
|
this.memberList.forEach((m) => (m.selected = false)); |
|
|
|
this.memberList.forEach((m) => (m.selected = false)); |
|
|
|
@ -749,7 +869,6 @@ export default { |
|
|
|
this.clearOrgTreeSelected(this.treeData); |
|
|
|
this.clearOrgTreeSelected(this.treeData); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 清空组织架构选择 |
|
|
|
|
|
|
|
clearOrgTreeSelected(nodes) { |
|
|
|
clearOrgTreeSelected(nodes) { |
|
|
|
nodes.forEach((node) => { |
|
|
|
nodes.forEach((node) => { |
|
|
|
node.selected = false; |
|
|
|
node.selected = false; |
|
|
|
@ -757,14 +876,13 @@ export default { |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 关闭弹窗 |
|
|
|
|
|
|
|
handleClose(done) { |
|
|
|
handleClose(done) { |
|
|
|
this.clearAllSelected(); |
|
|
|
this.clearAllSelected(); |
|
|
|
this.searchText = ""; |
|
|
|
this.searchText = ""; |
|
|
|
|
|
|
|
this.resetPage(); |
|
|
|
this.visible = false; |
|
|
|
this.visible = false; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 确认创建群聊 |
|
|
|
|
|
|
|
async confirmCreateGroup() { |
|
|
|
async confirmCreateGroup() { |
|
|
|
if (this.selectedMembers.length < this.minSelectCount) { |
|
|
|
if (this.selectedMembers.length < this.minSelectCount) { |
|
|
|
this.$message.warning(`至少选择${this.minSelectCount}个成员`); |
|
|
|
this.$message.warning(`至少选择${this.minSelectCount}个成员`); |
|
|
|
@ -772,13 +890,10 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
this.confirmLoading = true; |
|
|
|
this.confirmLoading = true; |
|
|
|
try { |
|
|
|
try { |
|
|
|
// 1. 先执行 props 传入的回调 |
|
|
|
|
|
|
|
if (typeof this.confirmCallback === "function") { |
|
|
|
if (typeof this.confirmCallback === "function") { |
|
|
|
await this.confirmCallback(this.selectedMembers); |
|
|
|
await this.confirmCallback(this.selectedMembers); |
|
|
|
} |
|
|
|
} |
|
|
|
// 2. 触发 confirm 事件 |
|
|
|
|
|
|
|
this.$emit("confirm", this.selectedMembers); |
|
|
|
this.$emit("confirm", this.selectedMembers); |
|
|
|
// 3. 清空选择并关闭弹窗 |
|
|
|
|
|
|
|
this.clearAllSelected(); |
|
|
|
this.clearAllSelected(); |
|
|
|
this.searchText = ""; |
|
|
|
this.searchText = ""; |
|
|
|
this.visible = false; |
|
|
|
this.visible = false; |
|
|
|
@ -796,10 +911,28 @@ export default { |
|
|
|
.body { |
|
|
|
.body { |
|
|
|
height: 450px; |
|
|
|
height: 450px; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.search-result-wrap { |
|
|
|
|
|
|
|
.search-title { |
|
|
|
|
|
|
|
font-size: 14px; |
|
|
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
|
|
color: #606266; |
|
|
|
|
|
|
|
padding: 6px 0 8px; |
|
|
|
|
|
|
|
border-bottom: 1px solid #ebeef5; |
|
|
|
|
|
|
|
margin-bottom: 8px; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.contact-list { |
|
|
|
.contact-list { |
|
|
|
height: 400px; |
|
|
|
height: 400px; |
|
|
|
overflow-y: auto; |
|
|
|
overflow-y: auto; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.load-more-tip { |
|
|
|
|
|
|
|
text-align: center; |
|
|
|
|
|
|
|
padding: 8px 0; |
|
|
|
|
|
|
|
color: #909399; |
|
|
|
|
|
|
|
font-size: 13px; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.member-item { |
|
|
|
.member-item { |
|
|
|
display: flex; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
align-items: center; |
|
|
|
@ -809,6 +942,15 @@ export default { |
|
|
|
border-radius: 4px; |
|
|
|
border-radius: 4px; |
|
|
|
transition: background 0.2s; |
|
|
|
transition: background 0.2s; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 修复el-checkbox内部垂直偏移 |
|
|
|
|
|
|
|
::v-deep .el-checkbox { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
::v-deep .el-checkbox__inner { |
|
|
|
|
|
|
|
margin-top: 0 !important; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&:hover { |
|
|
|
&:hover { |
|
|
|
background: #f5f7fa; |
|
|
|
background: #f5f7fa; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -887,6 +1029,21 @@ export default { |
|
|
|
padding: 5px; |
|
|
|
padding: 5px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 组织树用户行修复复选框下沉 |
|
|
|
|
|
|
|
.userline { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
gap: 10px; |
|
|
|
|
|
|
|
padding: 8px 16px; |
|
|
|
|
|
|
|
::v-deep .el-checkbox { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
::v-deep .el-checkbox__inner { |
|
|
|
|
|
|
|
margin-top: 0 !important; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.org-icon { |
|
|
|
.org-icon { |
|
|
|
font-size: 16px; |
|
|
|
font-size: 16px; |
|
|
|
color: #409eff; |
|
|
|
color: #409eff; |
|
|
|
@ -983,14 +1140,6 @@ export default { |
|
|
|
justify-content: center; |
|
|
|
justify-content: center; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.userline { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
gap: 10px; |
|
|
|
|
|
|
|
padding: 8px 16px; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 群组详细信息样式 |
|
|
|
|
|
|
|
.group-detail-info { |
|
|
|
.group-detail-info { |
|
|
|
padding: 12px 16px; |
|
|
|
padding: 12px 16px; |
|
|
|
margin: 8px 0; |
|
|
|
margin: 8px 0; |
|
|
|
|