提交
This commit is contained in:
parent
5cf55e7365
commit
11da27975c
17
App.vue
17
App.vue
@ -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");
|
||||||
|
|||||||
@ -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({
|
||||||
|
|||||||
27
pages.json
27
pages.json
@ -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": {
|
||||||
|
|||||||
@ -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管理器未初始化");
|
||||||
}
|
}
|
||||||
// 直接调用getGroupList,它会自动等待SDK就绪
|
|
||||||
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>
|
||||||
|
|||||||
@ -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: '邀请患者' }
|
||||||
|
|||||||
@ -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 }
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
212
utils/conversation-merger.js
Normal file
212
utils/conversation-merger.js
Normal 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()
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user