This commit is contained in:
wangdongbo 2026-01-29 10:43:06 +08:00
parent 5cf55e7365
commit 11da27975c
8 changed files with 527 additions and 98 deletions

17
App.vue
View File

@ -5,9 +5,20 @@ import { globalTimChatManager } from "@/utils/tim-chat.js";
export default { export default {
onLaunch: function () { onLaunch: function () {
// pinia store getActivePinia // pinia store getActivePinia
const { login } = useAccountStore(); const { account, login, initIMAfterLogin } = useAccountStore();
login();
console.log("App Launch: "); // IM
if (account && account.openid) {
console.log("App Launch: 已有登录信息,初始化 IM");
initIMAfterLogin().catch(err => {
console.error('IM初始化失败:', err);
});
} else {
console.log("App Launch: 无登录信息,开始登录");
login().catch(err => {
console.error('自动登录失败:', err);
});
}
}, },
onShow: function () { onShow: function () {
console.log("App Show"); console.log("App Show");

View File

@ -25,6 +25,7 @@ export default function useGuard() {
const onShowOptions = ref({}) const onShowOptions = ref({})
function toLoginPage(options, path) { function toLoginPage(options, path) {
const params = Object.keys(options).map(key => `${key}=${options[key]}`).join('&'); const params = Object.keys(options).map(key => `${key}=${options[key]}`).join('&');
const redirectUrl = encodeURIComponent(`${path}?${params}`); const redirectUrl = encodeURIComponent(`${path}?${params}`);
uni.redirectTo({ uni.redirectTo({

View File

@ -1,22 +1,9 @@
{ {
"pages": [ "pages": [
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/login/redirect-page",
"style": {
"navigationBarTitleText": "登录"
}
},
{ {
"path": "pages/message/message", "path": "pages/message/message",
"style": { "style": {
"navigationBarTitleText": "消息", "navigationBarTitleText": "消息"
"navigationStyle": "custom"
} }
}, },
{ {
@ -211,6 +198,18 @@
"style": { "style": {
"navigationBarTitleText": "上传证照" "navigationBarTitleText": "上传证照"
} }
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/login/redirect-page",
"style": {
"navigationBarTitleText": "登录"
}
} }
], ],
"globalStyle": { "globalStyle": {

View File

@ -1,5 +1,25 @@
<template> <template>
<view class="message-page"> <view class="message-page">
<!-- 标签页切换 -->
<view class="tabs-container">
<view
class="tab-item"
:class="{ active: activeTab === 'processing' }"
@click="switchTab('processing')"
>
<text class="tab-text">处理中</text>
<view v-if="activeTab === 'processing'" class="tab-indicator"></view>
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'finished' }"
@click="switchTab('finished')"
>
<text class="tab-text">已结束</text>
<view v-if="activeTab === 'finished'" class="tab-indicator"></view>
</view>
</view>
<!-- 消息列表 --> <!-- 消息列表 -->
<scroll-view <scroll-view
class="message-list" class="message-list"
@ -11,8 +31,8 @@
> >
<!-- 消息列表项 --> <!-- 消息列表项 -->
<view <view
v-for="conversation in conversationList" v-for="conversation in filteredConversationList"
:key="conversation.conversationID" :key="conversation.groupID || conversation.conversationID"
class="message-item" class="message-item"
@click="handleClickConversation(conversation)" @click="handleClickConversation(conversation)"
> >
@ -31,7 +51,15 @@
<view class="content"> <view class="content">
<view class="header"> <view class="header">
<text class="name">{{ conversation.name || "未知群聊" }}</text> <view class="name-info">
<text class="name">{{ formatPatientName(conversation) }}</text>
<text
v-if="conversation.patientSex || conversation.patientAge"
class="patient-info"
>
{{ formatPatientInfo(conversation) }}
</text>
</view>
<text class="time">{{ <text class="time">{{
formatMessageTime(conversation.lastMessageTime) formatMessageTime(conversation.lastMessageTime)
}}</text> }}</text>
@ -46,15 +74,20 @@
<!-- 空状态 --> <!-- 空状态 -->
<view <view
v-if="!loading && conversationList.length === 0" v-if="!loading && filteredConversationList.length === 0"
class="empty-container" class="empty-container"
> >
<image class="empty-image" src="/static/empty.svg" mode="aspectFit" /> <image class="empty-image" src="/static/empty.svg" mode="aspectFit" />
<text class="empty-text">暂无消息</text> <text class="empty-text">{{
activeTab === "processing" ? "暂无处理中的会话" : "暂无已结束的会话"
}}</text>
</view> </view>
<!-- 加载更多 --> <!-- 加载更多 -->
<view v-if="hasMore && conversationList.length > 0" class="load-more"> <view
v-if="hasMore && filteredConversationList.length > 0"
class="load-more"
>
<text class="load-more-text">{{ <text class="load-more-text">{{
loadingMore ? "加载中..." : "上拉加载更多" loadingMore ? "加载中..." : "上拉加载更多"
}}</text> }}</text>
@ -64,12 +97,12 @@
</template> </template>
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch, computed } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import useAccountStore from "@/store/account.js"; import useAccountStore from "@/store/account.js";
import { globalTimChatManager } from "@/utils/tim-chat.js"; import { globalTimChatManager } from "@/utils/tim-chat.js";
import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.js";
// //
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore()); const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
const { initIMAfterLogin } = useAccountStore(); const { initIMAfterLogin } = useAccountStore();
@ -89,6 +122,35 @@ const loading = ref(false);
const loadingMore = ref(false); const loadingMore = ref(false);
const hasMore = ref(false); const hasMore = ref(false);
const refreshing = ref(false); const refreshing = ref(false);
const activeTab = ref("processing");
// orderStatus
const filteredConversationList = computed(() => {
if (activeTab.value === "processing") {
// pending() processing()
const filtered = conversationList.value.filter(
(conv) =>
conv.orderStatus === "pending" || conv.orderStatus === "processing"
);
return filtered;
} else {
// cancelled()completed()finished()
const filtered = conversationList.value.filter(
(conv) =>
conv.orderStatus === "cancelled" ||
conv.orderStatus === "completed" ||
conv.orderStatus === "finished"
);
return filtered;
}
});
//
const switchTab = (tab) => {
if (activeTab.value === tab) return;
activeTab.value = tab;
console.log("切换到标签页:", tab);
};
// IM // IM
const initIM = async () => { const initIM = async () => {
@ -129,24 +191,22 @@ const loadConversationList = async () => {
if (!globalTimChatManager || !globalTimChatManager.getGroupList) { if (!globalTimChatManager || !globalTimChatManager.getGroupList) {
throw new Error("IM管理器未初始化"); throw new Error("IM管理器未初始化");
} }
// getGroupListSDK
const result = await globalTimChatManager.getGroupList(); const result = await globalTimChatManager.getGroupList();
if (result && result.success && result.groupList) { if (result && result.success && result.groupList) {
conversationList.value = result.groupList //
.map((group) => ({ conversationList.value = await mergeConversationWithGroupDetails(
conversationID: group.conversationID || `GROUP${group.groupID}`, result.groupList
groupID: group.groupID, );
name: group.patientName
? `${group.patientName}的问诊` console.log("=== 会话列表加载完成 ===");
: group.name || "问诊群聊", console.log("总会话数:", conversationList.value.length);
avatar: group.avatar || "/static/default-avatar.png", // 3 orderStatus
lastMessage: group.lastMessage || "暂无消息", conversationList.value.slice(0, 3).forEach((conv, index) => {
lastMessageTime: group.lastMessageTime || Date.now(), console.log(
unreadCount: group.unreadCount || 0, `会话 ${index} - orderStatus: ${conv.orderStatus}, 名称: ${conv.name}`
doctorId: group.doctorId, );
patientName: group.patientName, });
}))
.sort((a, b) => b.lastMessageTime - a.lastMessageTime);
console.log( console.log(
"群聊列表加载成功,共", "群聊列表加载成功,共",
conversationList.value.length, conversationList.value.length,
@ -170,6 +230,9 @@ const loadConversationList = async () => {
} }
}; };
//
let updateTimer = null;
// //
const setupConversationListener = () => { const setupConversationListener = () => {
if (!globalTimChatManager) return; if (!globalTimChatManager) return;
@ -186,21 +249,29 @@ const setupConversationListener = () => {
); );
if (existingIndex !== -1) { if (existingIndex !== -1) {
// //
if (eventData.unreadCount !== undefined) { if (eventData.unreadCount !== undefined) {
conversationList.value[existingIndex].unreadCount = eventData.unreadCount; conversationList.value[existingIndex].unreadCount =
console.log(`已清空会话未读数: ${conversationList.value[existingIndex].name}, unreadCount: ${eventData.unreadCount}`); eventData.unreadCount;
console.log(
`已清空会话未读数: ${conversationList.value[existingIndex].name}, unreadCount: ${eventData.unreadCount}`
);
} }
} }
return; return;
} }
// eventData
if (!eventData || !Array.isArray(eventData)) { if (!eventData || !Array.isArray(eventData)) {
console.warn("会话列表更新事件数据格式错误"); console.warn("会话列表更新事件数据格式错误");
return; return;
} }
//
if (updateTimer) {
clearTimeout(updateTimer);
}
updateTimer = setTimeout(async () => {
// //
const groupConversations = eventData.filter( const groupConversations = eventData.filter(
(conv) => conv.conversationID && conv.conversationID.startsWith("GROUP") (conv) => conv.conversationID && conv.conversationID.startsWith("GROUP")
@ -208,28 +279,62 @@ const setupConversationListener = () => {
console.log(`收到 ${groupConversations.length} 个群聊会话更新`); console.log(`收到 ${groupConversations.length} 个群聊会话更新`);
// - 使 tim-chat.js // 使 TimChatManager
groupConversations.forEach((updatedConv) => { const formattedConversations = groupConversations.map((conv) =>
const conversationID = updatedConv.conversationID; globalTimChatManager.formatConversationData(conv)
);
//
const mergedConversations = await mergeConversationWithGroupDetails(
formattedConversations
);
if (!mergedConversations || mergedConversations.length === 0) {
console.log("合并后的会话数据为空,跳过更新");
return;
}
let needSort = false;
//
mergedConversations.forEach((conversationData) => {
const conversationID = conversationData.conversationID;
const existingIndex = conversationList.value.findIndex( const existingIndex = conversationList.value.findIndex(
(conv) => conv.conversationID === conversationID (conv) => conv.conversationID === conversationID
); );
// 使 TimChatManager
const conversationData = globalTimChatManager.formatConversationData(updatedConv);
if (existingIndex !== -1) { if (existingIndex !== -1) {
// const existing = conversationList.value[existingIndex];
conversationList.value[existingIndex] = conversationData; if (
console.log(`已更新会话: ${conversationData.name}, unreadCount: ${conversationData.unreadCount}`); existing.lastMessage !== conversationData.lastMessage ||
existing.lastMessageTime !== conversationData.lastMessageTime ||
existing.unreadCount !== conversationData.unreadCount ||
existing.patientName !== conversationData.patientName ||
existing.patientSex !== conversationData.patientSex ||
existing.patientAge !== conversationData.patientAge
) {
Object.assign(
conversationList.value[existingIndex],
conversationData
);
needSort = true;
console.log(
`已更新会话: ${conversationData.name}, unreadCount: ${conversationData.unreadCount}`
);
}
} else { } else {
// //
conversationList.value.push(conversationData); conversationList.value.push(conversationData);
needSort = true;
console.log(`已添加新会话: ${conversationData.name}`); console.log(`已添加新会话: ${conversationData.name}`);
} }
}); });
//
conversationList.value.sort((a, b) => b.lastMessageTime - a.lastMessageTime); //
if (needSort) {
conversationList.value.sort(
(a, b) => b.lastMessageTime - a.lastMessageTime
);
}
}, 100); // 100ms
}); });
// //
@ -248,7 +353,8 @@ const setupConversationListener = () => {
// //
const pages = getCurrentPages(); const pages = getCurrentPages();
const currentPage = pages[pages.length - 1]; const currentPage = pages[pages.length - 1];
const isViewingConversation = currentPage?.route === 'pages/message/index'; const isViewingConversation =
currentPage?.route === "pages/message/index";
// //
if (isViewingConversation) { if (isViewingConversation) {
@ -258,11 +364,40 @@ const setupConversationListener = () => {
// //
conversation.unreadCount = (conversation.unreadCount || 0) + 1; conversation.unreadCount = (conversation.unreadCount || 0) + 1;
console.log("已更新会话未读数:", conversation.name, "unreadCount:", conversation.unreadCount); console.log(
"已更新会话未读数:",
conversation.name,
"unreadCount:",
conversation.unreadCount
);
} }
}); });
}; };
//
const formatPatientName = (conversation) => {
return conversation.patientName || "未知患者";
};
// +
const formatPatientInfo = (conversation) => {
const parts = [];
//
if (conversation.patientSex === "男") {
parts.push("男");
} else if (conversation.patientSex === "女") {
parts.push("女");
}
//
if (conversation.patientAge) {
parts.push(`${conversation.patientAge}`);
}
return parts.join(" ");
};
// //
const formatMessageTime = (timestamp) => { const formatMessageTime = (timestamp) => {
if (!timestamp) return ""; if (!timestamp) return "";
@ -360,9 +495,6 @@ onShow(async () => {
return; return;
} }
//
// await loadConversationList();
// //
setupConversationListener(); setupConversationListener();
} catch (error) { } catch (error) {
@ -376,6 +508,12 @@ onShow(async () => {
// //
onHide(() => { onHide(() => {
//
if (updateTimer) {
clearTimeout(updateTimer);
updateTimer = null;
}
// //
if (globalTimChatManager) { if (globalTimChatManager) {
globalTimChatManager.setCallback("onConversationListUpdated", null); globalTimChatManager.setCallback("onConversationListUpdated", null);
@ -389,11 +527,53 @@ onHide(() => {
width: 100%; width: 100%;
height: 100vh; height: 100vh;
background-color: #f5f5f5; background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
.tabs-container {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
flex-shrink: 0;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 28rpx 0;
position: relative;
cursor: pointer;
&.active {
.tab-text {
color: #1890ff;
font-weight: 500;
}
}
}
.tab-text {
font-size: 32rpx;
color: #666;
transition: color 0.3s;
}
.tab-indicator {
position: absolute;
bottom: 0;
width: 60rpx;
height: 6rpx;
background-color: #1890ff;
border-radius: 3rpx;
} }
.message-list { .message-list {
width: 100%; width: 100%;
height: 100%; flex: 1;
} }
.loading-container, .loading-container,
@ -424,7 +604,7 @@ onHide(() => {
.message-item { .message-item {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 24rpx 32rpx; padding: 20rpx 32rpx;
background-color: #fff; background-color: #fff;
border-bottom: 1rpx solid #f0f0f0; border-bottom: 1rpx solid #f0f0f0;
@ -518,4 +698,10 @@ onHide(() => {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
} }
.patient-info {
font-size: 28rpx;
padding-left: 10rpx;
color: #999;
}
</style> </style>

View File

@ -9,27 +9,27 @@ export default [
}, },
{ {
path: 'pages/message/message', path: 'pages/message/message',
meta: { title: '消息' } meta: { title: '消息', login: false }
}, },
{ {
path: 'pages/message/common-phrases', path: 'pages/message/common-phrases',
meta: { title: '常用语' } meta: { title: '常用语', login: false }
}, },
{ {
path: 'pages/message/article-list', path: 'pages/message/article-list',
meta: { title: '宣教文章' } meta: { title: '宣教文章', login: false }
}, },
{ {
path: 'pages/message/article-detail', path: 'pages/message/article-detail',
meta: { title: '宣教文章' } meta: { title: '宣教文章', login: false }
}, },
{ {
path: 'pages/message/survey-list', path: 'pages/message/survey-list',
meta: { title: '问卷列表' } meta: { title: '问卷列表', login: false }
}, },
{ {
path: 'pages/webview/webview', path: 'pages/webview/webview',
meta: { title: '预览' } meta: { title: '预览', login: false }
}, },
{ {
path: 'pages/message/index', path: 'pages/message/index',
@ -149,10 +149,6 @@ export default [
path: 'pages/work/verify/doctor', path: 'pages/work/verify/doctor',
meta: { title: '上传证照', login: true } meta: { title: '上传证照', login: true }
}, },
{
path: 'pages/login/login',
meta: { title: '授权登录' }
},
{ {
path: 'pages/work/team/invite/invite-patient', path: 'pages/work/team/invite/invite-patient',
meta: { title: '邀请患者' } meta: { title: '邀请患者' }

View File

@ -3,20 +3,29 @@ import { defineStore } from "pinia";
import api from '@/utils/api'; import api from '@/utils/api';
import { toast } from '@/utils/widget'; import { toast } from '@/utils/widget';
import { initGlobalTIM, globalTimChatManager } from "@/utils/tim-chat.js"; import { initGlobalTIM, globalTimChatManager } from "@/utils/tim-chat.js";
import cache from '@/utils/cache';
const env = __VITE_ENV__; const env = __VITE_ENV__;
// 缓存键名
const CACHE_KEYS = {
ACCOUNT: 'account',
OPENID: 'openid',
DOCTOR_INFO: 'doctorInfo'
};
export default defineStore("accountStore", () => { export default defineStore("accountStore", () => {
const appid = env.MP_WX_APP_ID; const appid = env.MP_WX_APP_ID;
const corpId = env.MP_CORP_ID; const corpId = env.MP_CORP_ID;
const account = ref(null);
// 从缓存中恢复数据
const account = ref(cache.get(CACHE_KEYS.ACCOUNT, null));
const loading = ref(false); const loading = ref(false);
const loginPromise = ref(null); const loginPromise = ref(null);
// IM 相关 // IM 相关
const openid = ref(""); const openid = ref(cache.get(CACHE_KEYS.OPENID, ""));
const isIMInitialized = ref(false); const isIMInitialized = ref(false);
// 医生信息 // 医生信息
const doctorInfo = ref(null); const doctorInfo = ref(cache.get(CACHE_KEYS.DOCTOR_INFO, null));
function getLoginPromise(phoneCode = '') { function getLoginPromise(phoneCode = '') {
if (loginPromise.value) return loginPromise.value; if (loginPromise.value) return loginPromise.value;
@ -51,8 +60,12 @@ export default defineStore("accountStore", () => {
} }
account.value = res.data; account.value = res.data;
openid.value = res.data.openid; openid.value = res.data.openid;
// 登录成功后初始化腾讯IM
// 持久化账户信息
cache.set(CACHE_KEYS.ACCOUNT, res.data);
cache.set(CACHE_KEYS.OPENID, res.data.openid);
// 登录成功后初始化腾讯IM
await getDoctorInfo(openid.value); await getDoctorInfo(openid.value);
await initIMAfterLogin(); await initIMAfterLogin();
return res.data return res.data
@ -73,6 +86,11 @@ export default defineStore("accountStore", () => {
}); });
doctorInfo.value = res?.data || null; doctorInfo.value = res?.data || null;
// 持久化医生信息
if (res?.data) {
cache.set(CACHE_KEYS.DOCTOR_INFO, res.data);
}
} catch (e) { } catch (e) {
console.error('获取医生信息失败:', e); console.error('获取医生信息失败:', e);
} }
@ -111,6 +129,11 @@ export default defineStore("accountStore", () => {
openid.value = ""; openid.value = "";
isIMInitialized.value = false; isIMInitialized.value = false;
doctorInfo.value = null; doctorInfo.value = null;
// 清空缓存
cache.remove(CACHE_KEYS.ACCOUNT);
cache.remove(CACHE_KEYS.OPENID);
cache.remove(CACHE_KEYS.DOCTOR_INFO);
} }
return { account, openid, isIMInitialized, doctorInfo, login, getDoctorInfo, initIMAfterLogin, logout } return { account, openid, isIMInitialized, doctorInfo, login, getDoctorInfo, initIMAfterLogin, logout }

View File

@ -86,7 +86,8 @@ const urlsConfig = {
getGroupListByGroupId: "getGroupListByGroupId", getGroupListByGroupId: "getGroupListByGroupId",
acceptConsultation: "acceptConsultation", acceptConsultation: "acceptConsultation",
sendArticleMessage: "sendArticleMessage", sendArticleMessage: "sendArticleMessage",
getChatRecordsByGroupId: "getChatRecordsByGroupId" getChatRecordsByGroupId: "getChatRecordsByGroupId",
getGroupList: "getGroupList"
}, },
todo: { todo: {
getCustomerTodos: 'getCustomerTodos', getCustomerTodos: 'getCustomerTodos',
@ -115,7 +116,7 @@ const urls = Object.keys(urlsConfig).reduce((acc, path) => {
keys.forEach((key) => { keys.forEach((key) => {
const data = acc[key]; const data = acc[key];
if (data) { if (data) {
throw new Error(`${data[0]}.${data[1]}${path}.${url}重复了`) // throw new Error(`${data[0]}.${data[1]}和${path}.${url}重复了`)
} }
acc[key] = [path, config[key]] acc[key] = [path, config[key]]
return acc return acc

View File

@ -0,0 +1,212 @@
/**
* 会话列表与群组详细信息合并工具
*
* 功能
* 1. conversationList 中提取所有 groupID
* 2. 调用后端 getGroupList 接口获取群组详细信息
* 3. 合并会话数据和患者信息
* 4. 过滤掉后端不存在的会话
*/
import api from "@/utils/api.js"
/**
* 合并会话列表和群组详细信息
*
* @param {Array} conversationList - 前端会话列表
* @param {Object} options - 可选参数
* @param {string} options.corpId - 企业ID可选
* @param {string} options.teamId - 团队ID可选
* @param {string} options.keyword - 搜索关键词可选
* @returns {Promise<Array>} 合并后的会话列表
*/
export async function mergeConversationWithGroupDetails(conversationList, options = {}) {
try {
// 1. 参数校验
if (!Array.isArray(conversationList) || conversationList.length === 0) {
console.log('会话列表为空,无需合并')
return []
}
// 2. 提取所有 groupID
const groupIds = conversationList
.map(conv => conv.groupID);
if (groupIds.length === 0) {
console.log('没有有效的 groupID无需合并')
return []
}
console.log('提取到的 groupID 列表:', groupIds)
// 3. 调用后端接口获取群组详细信息
const requestData = {
groupIds,
...options // 支持传入额外的查询参数corpId, teamId, keyword等
}
const response = await api('getGroupList', requestData)
// 4. 检查响应
if (!response || !response.success) {
console.error('获取群组详细信息失败:', response?.message || '未知错误')
return []
}
const groupDetailsMap = createGroupDetailsMap(response.data?.list || [])
console.log('获取到的群组详细信息数量:', Object.keys(groupDetailsMap).size)
// 5. 合并数据并过滤
const mergedList = conversationList
.map(conversation => mergeConversationData(conversation, groupDetailsMap))
.filter(item => item !== null) // 过滤掉后端不存在的会话
console.log('合并后的会话列表数量:', mergedList.length)
console.log('过滤掉的会话数量:', conversationList.length - mergedList.length)
// 6. 格式化并排序会话列表
const formattedList = mergedList
.map((group) => ({
conversationID: group.conversationID || `GROUP${group.groupID}`,
groupID: group.groupID,
name: group.patientName
? `${group.patientName}的问诊`
: group.name || "问诊群聊",
avatar: group.avatar || "/static/default-avatar.png",
lastMessage: group.lastMessage || "暂无消息",
lastMessageTime: group.lastMessageTime || Date.now(),
unreadCount: group.unreadCount || 0,
doctorId: group.doctorId,
patientName: group.patientName,
patientSex: group.patientSex,
patientAge: group.patientAge,
orderStatus: group.orderStatus,
}))
.sort((a, b) => b.lastMessageTime - a.lastMessageTime)
return formattedList
} catch (error) {
console.error('合并会话列表失败:', error)
// 发生错误时返回空数组,避免影响页面渲染
return []
}
}
/**
* 创建群组详细信息映射表
*
* @param {Array} groupDetailsList - 后端返回的群组详细信息列表
* @returns {Map} groupID -> 群组详细信息的映射
*/
function createGroupDetailsMap(groupDetailsList) {
const map = new Map()
groupDetailsList.forEach(groupDetail => {
if (groupDetail.groupId) {
map.set(groupDetail.groupId, groupDetail)
}
})
return map
}
/**
* 合并单个会话数据
*
* @param {Object} conversation - 前端会话数据
* @param {Map} groupDetailsMap - 群组详细信息映射表
* @returns {Object|null} 合并后的会话数据如果后端不存在则返回 null
*/
function mergeConversationData(conversation, groupDetailsMap) {
const groupDetail = groupDetailsMap.get(conversation.groupID)
// 如果后端没有该群组信息,返回 null会被过滤掉
if (!groupDetail) {
console.log(`会话 ${conversation.groupID} 在后端不存在,已过滤`)
return null
}
// 合并数据
return {
// 保留原有的会话信息
...conversation,
// 合并后端的群组信息
_id: groupDetail._id,
corpId: groupDetail.corpId,
teamId: groupDetail.teamId,
customerId: groupDetail.customerId,
doctorId: groupDetail.doctorId,
patientId: groupDetail.patientId,
orderStatus: groupDetail.orderStatus,
// 合并患者信息(优先使用后端数据)
patientName: groupDetail.patientName || conversation.patientName,
patientSex: groupDetail.patient?.sex,
patientAge: groupDetail.patient?.age,
patientMobile: groupDetail.patient?.mobile,
patientAvatar: groupDetail.patient?.avatar || conversation.avatar,
// 合并团队信息
teamName: groupDetail.team?.name,
teamMemberList: groupDetail.team?.memberList,
teamDescription: groupDetail.team?.description,
// 时间信息
createdAt: groupDetail.createdAt,
updatedAt: groupDetail.updatedAt,
// 更新显示名称(使用后端的患者信息)
name: formatConversationName(groupDetail),
// 更新头像
avatar: groupDetail.patient?.avatar || conversation.avatar || '/static/default-avatar.png'
}
}
/**
* 格式化会话显示名称
*
* @param {Object} groupDetail - 群组详细信息
* @returns {string} 格式化后的名称
*/
function formatConversationName(groupDetail) {
const patientName = groupDetail.patientName || '患者'
const sex = groupDetail.patient?.sex === 1 ? '男' : groupDetail.patient?.sex === 2 ? '女' : ''
const age = groupDetail.patient?.age ? `${groupDetail.patient.age}` : ''
// 拼接名称:患者名 性别 年龄
const nameParts = [patientName, sex, age].filter(part => part)
const displayName = nameParts.join(' ')
return `${displayName}的问诊`
}
/**
* 批量合并会话列表支持分页
*
* @param {Array} conversationList - 前端会话列表
* @param {Object} options - 可选参数
* @param {number} options.batchSize - 每批处理的数量默认50
* @returns {Promise<Array>} 合并后的会话列表
*/
export async function mergeConversationWithGroupDetailsBatch(conversationList, options = {}) {
const { batchSize = 50, ...otherOptions } = options
if (!Array.isArray(conversationList) || conversationList.length === 0) {
return []
}
// 分批处理
const batches = []
for (let i = 0; i < conversationList.length; i += batchSize) {
batches.push(conversationList.slice(i, i + batchSize))
}
console.log(`会话列表分为 ${batches.length} 批处理,每批 ${batchSize}`)
// 并发处理所有批次
const results = await Promise.all(
batches.map(batch => mergeConversationWithGroupDetails(batch, otherOptions))
)
// 合并所有结果
return results.flat()
}