Compare commits

..

No commits in common. "932ec63261d20bca7b811367d7aa1ab2abcee391" and "2fe645a8861042fbaf3cb8cfa67362bf84c3f53d" have entirely different histories.

19 changed files with 194 additions and 464 deletions

View File

@ -1,7 +1,6 @@
<script> <script>
import dbStore from "@/store/db"; import dbStore from "@/store/db";
import accountStore from "@/store/account"; import accountStore from "@/store/account";
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
export default { export default {
async onLaunch() { async onLaunch() {
@ -39,8 +38,6 @@ export default {
const success = await account.initIMAfterLogin(); const success = await account.initIMAfterLogin();
if (success) { if (success) {
console.log("IM 初始化成功"); console.log("IM 初始化成功");
// IM
globalUnreadListenerManager.setup();
} else { } else {
console.warn("IM 初始化失败"); console.warn("IM 初始化失败");
} }

View File

@ -25,7 +25,7 @@ const props = defineProps({
} }
}) })
const list = computed(() => props.avatarList.map(i => i || '/static/default-avatar.png')) const list = computed(() => props.avatarList.map(i => i || '/static/default-avatar.svg'))
const size = computed(() => { const size = computed(() => {
const val = Number.isInteger(props.size) && props.size > 0 ? props.size : 144; const val = Number.isInteger(props.size) && props.size > 0 ? props.size : 144;

View File

@ -134,7 +134,6 @@ async function addArchive() {
mobile: account.value.mobile, mobile: account.value.mobile,
miniAppId: account.value.openid, miniAppId: account.value.openid,
externalUserId: externalUserId.value, externalUserId: externalUserId.value,
realUnionid: account.value.unionid || '',
} }
loading.value = false; loading.value = false;
const res = await api('addCustomer', { params }); const res = await api('addCustomer', { params });
@ -155,7 +154,7 @@ async function bindArchive(customerId) {
await toast('绑定成功'); await toast('绑定成功');
set('home-invite-teamId', teamId.value); set('home-invite-teamId', teamId.value);
uni.switchTab({ uni.switchTab({
url: '/pages/home/home' url:'/pages/home/home'
}) })
// uni.reLaunch({ url: `/pages/home/home?corpId=${corpId.value}&teamId=${teamId.value}` }) // uni.reLaunch({ url: `/pages/home/home?corpId=${corpId.value}&teamId=${teamId.value}` })
} else { } else {

View File

@ -254,7 +254,7 @@ const selectCate = async (cateId) => {
function goToDetail(item) { function goToDetail(item) {
if (!item?._id) return; if (!item?._id) return;
uni.navigateTo({ url: `/pages/article/article-detail?id=${item._id}&corpId=${corpId.value}` }); uni.navigateTo({ url: `/pages/article/article-detail?id=${item._id}` });
} }
onLoad(async (options) => { onLoad(async (options) => {

View File

@ -54,7 +54,7 @@ const articleIds = computed(() => {
function goToDetail(article) { function goToDetail(article) {
if (!article?._id) return; if (!article?._id) return;
uni.navigateTo({ url: `/pages/article/article-detail?id=${article._id}&corpId=${props.team.corpId}` }); uni.navigateTo({ url: `/pages/article/article-detail?id=${article._id}` });
} }
const loadArticles = async () => { const loadArticles = async () => {

View File

@ -20,7 +20,7 @@
<yc-home v-else /> <yc-home v-else />
</template> </template>
<script setup> <script setup>
import { computed, ref, watch } from "vue"; import { computed, ref } from "vue";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { onLoad, onShow } from "@dcloudio/uni-app"; import { onLoad, onShow } from "@dcloudio/uni-app";
// import useGuard from "@/hooks/useGuard"; // import useGuard from "@/hooks/useGuard";
@ -103,13 +103,6 @@ onShow(async () => {
teams.value = []; teams.value = [];
} }
}); });
watch(account, (n, o) => {
if (n && !o) {
getTeams();
} else if (!n && o) {
teams.value = [];
}
})
</script> </script>
<style scoped> <style scoped>
.home-container { .home-container {

View File

@ -13,7 +13,7 @@
<view v-for="i in teamates" :key="i.userid" <view v-for="i in teamates" :key="i.userid"
class="member-card flex flex-shrink-0 min-w-120 mr-15 p-15" class="member-card flex flex-shrink-0 min-w-120 mr-15 p-15"
@click="toHomePage(i)"> @click="toHomePage(i)">
<image class="flex-shrink-0 avatar mr-10" :src="i.avatar || '/static/default-avatar.png'" /> <image class="flex-shrink-0 avatar mr-10" :src="i.avatar || '/static/default-avatar.svg'" />
<view class="flex-grow flex flex-col justify-between"> <view class="flex-grow flex flex-col justify-between">
<view> <view>
<view class="member-name leading-normal h-24 text-base font-semibold text-dark whitespace-nowrap"> <view class="member-name leading-normal h-24 text-base font-semibold text-dark whitespace-nowrap">

View File

@ -6,24 +6,17 @@ $text-color-sub: #999;
$primary-color: #0877F1; $primary-color: #0877F1;
.chat-page { .chat-page {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh;
background-color: #f5f5f5; background-color: #f5f5f5;
overflow: hidden; overflow: hidden;
transition: padding-bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
} }
/* 患者信息栏样式 */ /* 患者信息栏样式 */
.patient-info-bar { .patient-info-bar {
position: fixed; position: sticky;
top: 0; top: 0;
left: 0;
right: 0;
background: #fff; background: #fff;
border-bottom: 1rpx solid #f0f0f0; border-bottom: 1rpx solid #f0f0f0;
padding: 20rpx 32rpx; padding: 20rpx 32rpx;
@ -117,15 +110,11 @@ $primary-color: #0877F1;
} }
.chat-content { .chat-content {
position: fixed; flex: 1;
top: 100rpx; /* 患者信息栏高度,根据实际调整 */
left: 0;
right: 0;
bottom: 120rpx; /* 输入框高度,根据实际调整 */
box-sizing: border-box; box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
transition: bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94); min-height: 0;
} }
.chat-content-compressed { .chat-content-compressed {
@ -390,24 +379,18 @@ $primary-color: #0877F1;
} }
.input-section { .input-section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff; background: #fff;
border-top: 1rpx solid #e0e0e0; border-top: 1rpx solid #e0e0e0;
position: relative;
z-index: 200; z-index: 200;
padding-bottom: env(safe-area-inset-bottom);
flex-shrink: 0; flex-shrink: 0;
transform: translateZ(0); /* 开启硬件加速,提升性能 */
transition: bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
will-change: bottom;
} }
.input-toolbar { .input-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 12rpx 20rpx; padding: 16rpx 20rpx;
padding-bottom: env(safe-area-inset-bottom);
gap: 12rpx; gap: 12rpx;
} }
@ -513,8 +496,7 @@ $primary-color: #0877F1;
justify-content: flex-start; justify-content: flex-start;
background: #fff; background: #fff;
border-top: 1rpx solid #eee; border-top: 1rpx solid #eee;
padding: 20rpx 0 20rpx 60rpx; padding: 20rpx 0 40rpx 60rpx;
padding-bottom: env(safe-area-inset-bottom);
gap: 40rpx 50rpx; gap: 40rpx 50rpx;
flex-wrap: wrap; flex-wrap: wrap;
background-color: #f5f5f5; background-color: #f5f5f5;

View File

@ -1,5 +1,5 @@
<template> <template>
<view class="input-section" :style="{ bottom: props.keyboardHeight + 'px' }"> <view class="input-section">
<view class="input-toolbar"> <view class="input-toolbar">
<view @click="toggleVoiceInput" class="voice-toggle-btn"> <view @click="toggleVoiceInput" class="voice-toggle-btn">
<image v-if="showVoiceInput" src="/static/jianpan.png" class="voice-toggle-icon" mode="aspectFit"></image> <image v-if="showVoiceInput" src="/static/jianpan.png" class="voice-toggle-icon" mode="aspectFit"></image>
@ -8,7 +8,7 @@
<view class="input-area"> <view class="input-area">
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..." <textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
@confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput" @confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput"
:auto-height="true" :show-confirm-bar="false" :adjust-position="false" :hold-keyboard="true" /> :auto-height="true" :show-confirm-bar="false" :adjust-position="true" :cursor-spacing="40" />
<input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord" <input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
@touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled> @touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled>
</input> </input>
@ -77,7 +77,6 @@ const props = defineProps({
groupId: { type: String, default: '' }, groupId: { type: String, default: '' },
userId: { type: String, default: '' }, userId: { type: String, default: '' },
corpId: { type: String, default: '' }, corpId: { type: String, default: '' },
keyboardHeight: { type: Number, default: 0 },
}); });
// Emits // Emits
@ -151,15 +150,11 @@ const initRecorderManager = () => {
const sendTextMessage = async () => { const sendTextMessage = async () => {
if (!inputText.value.trim()) return; if (!inputText.value.trim()) return;
const textToSend = inputText.value; await sendMessage("text", inputText.value);
inputText.value = ""; inputText.value = "";
await sendMessage("text", textToSend); //
uni.hideKeyboard();
//
nextTick(() => {
// hold-keyboard
});
}; };

View File

@ -10,7 +10,7 @@ export default function useGroupAvatars() {
const groupAvatarMap = ref({}) // { groupID: [avatarUrl1, avatarUrl2, ...] } const groupAvatarMap = ref({}) // { groupID: [avatarUrl1, avatarUrl2, ...] }
const teamStore = useTeamStore() const teamStore = useTeamStore()
const patientDefaultAvatar = '/static/default-patient-avatar.png' const patientDefaultAvatar = '/static/default-patient-avatar.png'
const teamMemberDefaultAvatar = '/static/default-avatar.png' const teamMemberDefaultAvatar = '/static/default-avatar.svg'
/** /**
* 获取单个群聊的头像列表 * 获取单个群聊的头像列表
@ -25,6 +25,7 @@ export default function useGroupAvatars() {
console.warn(`群聊 ${groupID} 没有 teamId无法获取头像`) console.warn(`群聊 ${groupID} 没有 teamId无法获取头像`)
return [] return []
} }
// 获取团队成员的头像和名称 // 获取团队成员的头像和名称
const memberMap = await teamStore.getTeamMemberAvatarsAndName(teamId) const memberMap = await teamStore.getTeamMemberAvatarsAndName(teamId)

View File

@ -47,7 +47,7 @@ export default function useGroupChat(groupID) {
const getUserAvatar = (userId) => { const getUserAvatar = (userId) => {
const member = chatMember.value[userId] const member = chatMember.value[userId]
if (!member) { if (!member) {
return userId === openid.value ? '/static/default-patient-avatar.png' : '/static/default-avatar.png' return userId === openid.value ? '/static/default-patient-avatar.png' : '/static/default-avatar.svg'
} }
// 如果有头像且不为空字符串,返回头像 // 如果有头像且不为空字符串,返回头像
@ -55,7 +55,7 @@ export default function useGroupChat(groupID) {
return member.avatar return member.avatar
} }
return member.isTeamMember ? '/static/default-avatar.png' : '/static/default-patient-avatar.png' return member.isTeamMember ? '/static/default-avatar.svg' : '/static/default-patient-avatar.png'
} }
// 获取群聊信息和成员头像 // 获取群聊信息和成员头像
async function getGroupInfo() { async function getGroupInfo() {
@ -76,7 +76,6 @@ export default function useGroupChat(groupID) {
// 2. 如果有teamId获取团队成员头像和名称 // 2. 如果有teamId获取团队成员头像和名称
if (groupResult.data.teamId) { if (groupResult.data.teamId) {
const memberMap = await teamStore.getTeamMemberAvatarsAndName(groupResult.data.teamId) const memberMap = await teamStore.getTeamMemberAvatarsAndName(groupResult.data.teamId)
// 3. 存储团队成员ID列表 // 3. 存储团队成员ID列表

View File

@ -1,8 +1,5 @@
<template> <template>
<page-meta <view class="chat-page">
:page-style="'overflow:' + (keyboardHeight > 0 ? 'hidden' : 'visible')"
></page-meta>
<view class="chat-page" :style="{ paddingBottom: keyboardHeight + 'px' }">
<!-- 患者信息栏 --> <!-- 患者信息栏 -->
<view class="patient-info-bar" v-if="patientInfo.name"> <view class="patient-info-bar" v-if="patientInfo.name">
<view class="patient-info-content"> <view class="patient-info-content">
@ -25,9 +22,6 @@
<!-- 聊天消息区域 --> <!-- 聊天消息区域 -->
<scroll-view <scroll-view
class="chat-content" class="chat-content"
:style="{
bottom: (keyboardHeight > 0 ? keyboardHeight + 60 : 60) + 'px',
}"
scroll-y="true" scroll-y="true"
enhanced="true" enhanced="true"
bounces="false" bounces="false"
@ -152,7 +146,6 @@
" "
:userId="openid" :userId="openid"
:corpId="corpId.value" :corpId="corpId.value"
:keyboardHeight="keyboardHeight"
@scrollToBottom="() => scrollToBottom(true)" @scrollToBottom="() => scrollToBottom(true)"
@messageSent="() => scrollToBottom(true)" @messageSent="() => scrollToBottom(true)"
/> />
@ -165,7 +158,6 @@ 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 { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
import { import {
startIMMonitoring, startIMMonitoring,
stopIMMonitoring, stopIMMonitoring,
@ -220,9 +212,6 @@ const chatInfo = ref({
avatar: "/static/home/avatar.svg", avatar: "/static/home/avatar.svg",
}); });
//
const keyboardHeight = ref(0);
// //
const isEvaluationPopupOpen = ref(false); const isEvaluationPopupOpen = ref(false);
@ -267,7 +256,6 @@ const scrollIntoView = ref("");
const isLoadingMore = ref(false); const isLoadingMore = ref(false);
const isCompleted = ref(false); const isCompleted = ref(false);
const lastFirstMessageId = ref(""); const lastFirstMessageId = ref("");
const isCallbacksInitialized = ref(false); //
// //
function isSystemMessage(message) { function isSystemMessage(message) {
@ -299,13 +287,10 @@ function isSystemMessage(message) {
// //
const fetchGroupOrderStatus = async () => { const fetchGroupOrderStatus = async () => {
try { try {
const result = await api( const result = await api("getGroupListByGroupId", {
"getGroupListByGroupId",
{
groupId: groupId.value, groupId: groupId.value,
}, });
false
);
if (result.success && result.data) { if (result.success && result.data) {
orderStatus.value = result.data.orderStatus || ""; orderStatus.value = result.data.orderStatus || "";
corpId.value = result.data.corpId || ""; corpId.value = result.data.corpId || "";
@ -457,22 +442,6 @@ onLoad((options) => {
chatInfo.value.userID = options.userID; chatInfo.value.userID = options.userID;
} }
//
uni.onKeyboardHeightChange((res) => {
console.log("键盘高度变化:", res.height);
const oldHeight = keyboardHeight.value;
keyboardHeight.value = res.height;
// 00
if (oldHeight === 0 && res.height > 0) {
nextTick(() => {
setTimeout(() => {
scrollToBottom(true);
}, 100);
});
}
});
checkLoginAndInitTIM(); checkLoginAndInitTIM();
updateNavigationTitle(); updateNavigationTitle();
}); });
@ -507,14 +476,12 @@ const checkLoginAndInitTIM = async () => {
// IM // IM
const initTIMCallbacks = async () => { const initTIMCallbacks = async () => {
//
isCallbacksInitialized.value = true;
timChatManager.setCallback("onSDKReady", () => { timChatManager.setCallback("onSDKReady", () => {
if (messageList.value.length === 0 && !isLoading.value) { if (messageList.value.length === 0 && !isLoading.value) {
loadMessageList(); loadMessageList();
} }
}); });
timChatManager.setCallback("onSDKNotReady", () => {}); timChatManager.setCallback("onSDKNotReady", () => {});
timChatManager.setCallback("onMessageReceived", (message) => { timChatManager.setCallback("onMessageReceived", (message) => {
@ -560,10 +527,9 @@ const initTIMCallbacks = async () => {
.setMessageRead({ .setMessageRead({
conversationID: chatInfo.value.conversationID, conversationID: chatInfo.value.conversationID,
}) })
.then(async () => { .then(() => {
console.log("✓ 收到新消息后已标记为已读"); console.log("✓ 收到新消息后已标记为已读");
// tabBar // tabBar
await globalUnreadListenerManager.refreshBadge();
}) })
.catch((error) => { .catch((error) => {
console.error("✗ 标记已读失败:", error); console.error("✗ 标记已读失败:", error);
@ -714,10 +680,9 @@ const loadMessageList = async () => {
.setMessageRead({ .setMessageRead({
conversationID: chatInfo.value.conversationID, conversationID: chatInfo.value.conversationID,
}) })
.then(async () => { .then(() => {
console.log("✓ 会话已标记为已读:", chatInfo.value.conversationID); console.log("✓ 会话已标记为已读:", chatInfo.value.conversationID);
// tabBar // tabBar
await globalUnreadListenerManager.refreshBadge();
}) })
.catch((error) => { .catch((error) => {
console.error("✗ 标记会话已读失败:", error); console.error("✗ 标记会话已读失败:", error);
@ -861,49 +826,17 @@ onShow(() => {
}); });
return; return;
} }
if (!isIMInitialized.value) { if (!isIMInitialized.value) {
checkLoginAndInitTIM(); checkLoginAndInitTIM();
} else if (timChatManager.tim && !timChatManager.isLoggedIn) { } else if (timChatManager.tim && !timChatManager.isLoggedIn) {
// IM timChatManager.ensureIMConnection();
timChatManager.ensureIMConnection().then(() => {
//
if (timChatManager.isLoggedIn && chatInfo.value.conversationID) {
console.log("✓ 重连成功,重新注册消息监听回调");
initTIMCallbacks();
timChatManager.setConversationID(chatInfo.value.conversationID);
}
});
} else if ( } else if (
timChatManager.tim && timChatManager.tim &&
timChatManager.isLoggedIn && timChatManager.isLoggedIn &&
chatInfo.value.conversationID chatInfo.value.conversationID
) { )
// IMID
console.log("✓ 页面显示重新设置当前会话ID");
timChatManager.setConversationID(chatInfo.value.conversationID);
//
if (!isCallbacksInitialized.value) {
console.log("✓ 回调未初始化,进行初始化");
initTIMCallbacks();
}
//
if (timChatManager.tim && timChatManager.isLoggedIn) {
timChatManager.tim
.setMessageRead({
conversationID: chatInfo.value.conversationID,
})
.then(() => {
console.log("✓ 页面显示时已标记会话为已读");
})
.catch((error) => {
console.error("✗ 标记会话已读失败:", error);
});
}
startIMMonitoring(30000); startIMMonitoring(30000);
}
}); });
// //
@ -912,11 +845,6 @@ onHide(() => {
// ID // ID
timChatManager.currentConversationID = null; timChatManager.currentConversationID = null;
console.log("✓ 页面隐藏已清空当前会话ID"); console.log("✓ 页面隐藏已清空当前会话ID");
// tabBar
if (globalUnreadListenerManager.isInitialized) {
globalUnreadListenerManager.refreshBadge();
}
}); });
// //
@ -986,13 +914,10 @@ const handleApplyConsult = async () => {
}); });
try { try {
// IDcorpId // IDcorpId
const groupInfo = await api( const groupInfo = await api("getGroupListByGroupId", {
"getGroupListByGroupId",
{
groupId: groupId.value, groupId: groupId.value,
}, });
false
);
if (!groupInfo.success || !groupInfo.data) { if (!groupInfo.success || !groupInfo.data) {
throw new Error("获取群组信息失败"); throw new Error("获取群组信息失败");
} }
@ -1065,9 +990,6 @@ const handleApplyConsult = async () => {
onUnmounted(() => { onUnmounted(() => {
clearMessageCache(); clearMessageCache();
//
uni.offKeyboardHeightChange();
timChatManager.setCallback("onSDKReady", null); timChatManager.setCallback("onSDKReady", null);
timChatManager.setCallback("onSDKNotReady", null); timChatManager.setCallback("onSDKNotReady", null);
timChatManager.setCallback("onMessageReceived", null); timChatManager.setCallback("onMessageReceived", null);

View File

@ -76,13 +76,12 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onUnmounted } from "vue"; import { ref, 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"; import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.js";
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
import useGroupAvatars from "./hooks/use-group-avatars.js"; import useGroupAvatars from "./hooks/use-group-avatars.js";
import GroupAvatar from "@/components/group-avatar.vue"; import GroupAvatar from "@/components/group-avatar.vue";
@ -252,10 +251,12 @@ const loadConversationList = async () => {
// //
let updateTimer = null; let updateTimer = null;
// //
const handleConversationListUpdate = async (eventData) => { const setupConversationListener = () => {
console.log("【消息列表页】会话列表更新事件:", eventData); if (!globalTimChatManager) return;
//
globalTimChatManager.setCallback("onConversationListUpdated", (eventData) => {
console.log("会话列表更新事件:", eventData);
// //
if (eventData && !Array.isArray(eventData) && eventData.conversationID) { if (eventData && !Array.isArray(eventData) && eventData.conversationID) {
const conversationID = eventData.conversationID; const conversationID = eventData.conversationID;
@ -269,23 +270,16 @@ const handleConversationListUpdate = async (eventData) => {
conversationList.value[existingIndex].unreadCount = conversationList.value[existingIndex].unreadCount =
eventData.unreadCount; eventData.unreadCount;
console.log( console.log(
`【消息列表页】已更新会话未读数: ${conversationList.value[existingIndex].name}, unreadCount: ${eventData.unreadCount}` `已清空会话未读数: ${conversationList.value[existingIndex].name}, unreadCount: ${eventData.unreadCount}`
); );
} }
//
if (eventData.lastMessage) {
conversationList.value[existingIndex].lastMessage = eventData.lastMessage.messageForShow || '';
conversationList.value[existingIndex].lastMessageTime = eventData.lastMessage.lastTime || Date.now();
//
conversationList.value.sort((a, b) => b.lastMessageTime - a.lastMessageTime);
}
} }
return; return;
} }
// eventData // eventData
if (!eventData || !Array.isArray(eventData)) { if (!eventData || !Array.isArray(eventData)) {
console.warn("【消息列表页】会话列表更新事件数据格式错误"); console.warn("会话列表更新事件数据格式错误");
return; return;
} }
@ -300,7 +294,7 @@ const handleConversationListUpdate = async (eventData) => {
(conv) => conv.conversationID && conv.conversationID.startsWith("GROUP") (conv) => conv.conversationID && conv.conversationID.startsWith("GROUP")
); );
console.log(`【消息列表页】收到 ${groupConversations.length} 个群聊会话更新`); console.log(`收到 ${groupConversations.length} 个群聊会话更新`);
// 使 TimChatManager // 使 TimChatManager
const formattedConversations = groupConversations.map((conv) => const formattedConversations = groupConversations.map((conv) =>
@ -313,7 +307,7 @@ const handleConversationListUpdate = async (eventData) => {
); );
if (!mergedConversations || mergedConversations.length === 0) { if (!mergedConversations || mergedConversations.length === 0) {
console.log("【消息列表页】合并后的会话数据为空,跳过更新"); console.log("合并后的会话数据为空,跳过更新");
return; return;
} }
@ -339,19 +333,20 @@ const handleConversationListUpdate = async (eventData) => {
...conversationData, ...conversationData,
// //
avatar: existing.avatar || conversationData.avatar, avatar: existing.avatar || conversationData.avatar,
// 使 TIM SDK // 使 TIM SDK 使 Math.max
// unreadCount N 0
unreadCount: conversationData.unreadCount || 0 unreadCount: conversationData.unreadCount || 0
}; };
needSort = true; needSort = true;
console.log( console.log(
`【消息列表页】已更新会话: ${conversationData.name}, unreadCount: ${conversationList.value[existingIndex].unreadCount}` `已更新会话: ${conversationData.name}, unreadCount: ${conversationList.value[existingIndex].unreadCount}`
); );
} }
} else { } else {
// //
conversationList.value.push(conversationData); conversationList.value.push(conversationData);
needSort = true; needSort = true;
console.log(`【消息列表页】已添加新会话: ${conversationData.name}`); console.log(`已添加新会话: ${conversationData.name}`);
} }
}); });
@ -362,33 +357,19 @@ const handleConversationListUpdate = async (eventData) => {
); );
} }
}, 100); // 100ms }, 100); // 100ms
}; });
// //
const handleMessageReceived = (message) => { globalTimChatManager.setCallback("onMessageReceived", (message) => {
console.log("【消息列表页】收到新消息:", message); console.log("消息列表页面收到新消息:", message);
//
// TIM SDK onConversationListUpdated // TIM SDK onConversationListUpdated
// // +1
}; //
// onConversationListUpdated
// //
const setupConversationListener = () => { });
if (!globalTimChatManager) return;
console.log("【消息列表页】设置会话监听器");
//
//
if (globalUnreadListenerManager.isInitialized) {
globalUnreadListenerManager.addCallback("onConversationListUpdated", handleConversationListUpdate);
globalUnreadListenerManager.addCallback("onMessageReceived", handleMessageReceived);
console.log("【消息列表页】已添加回调到全局监听器回调链");
} else {
console.warn("【消息列表页】全局未读监听器未初始化,使用直接回调方式");
//
globalTimChatManager.setCallback("onConversationListUpdated", handleConversationListUpdate);
globalTimChatManager.setCallback("onMessageReceived", handleMessageReceived);
}
}; };
// //
@ -436,8 +417,6 @@ const formatMessageTime = (timestamp) => {
// //
const handleClickConversation = async (conversation) => { const handleClickConversation = async (conversation) => {
console.log("点击会话:", conversation); console.log("点击会话:", conversation);
//
const conversationIndex = conversationList.value.findIndex( const conversationIndex = conversationList.value.findIndex(
(conv) => conv.conversationID === conversation.conversationID (conv) => conv.conversationID === conversation.conversationID
); );
@ -445,21 +424,6 @@ const handleClickConversation = async (conversation) => {
conversationList.value[conversationIndex].unreadCount = 0; conversationList.value[conversationIndex].unreadCount = 0;
} }
//
if (globalTimChatManager && globalTimChatManager.tim) {
try {
await globalTimChatManager.tim.setMessageRead({
conversationID: conversation.conversationID,
});
console.log("✓ 已标记会话为已读:", conversation.conversationID);
// tabBar
await globalUnreadListenerManager.refreshBadge();
} catch (error) {
console.error("✗ 标记会话已读失败:", error);
}
}
// //
uni.navigateTo({ uni.navigateTo({
url: `/pages/message/index?conversationID=${conversation.conversationID}&groupID=${conversation.groupID}`, url: `/pages/message/index?conversationID=${conversation.conversationID}&groupID=${conversation.groupID}`,
@ -513,42 +477,19 @@ const cleanMessageText = (text) => {
// //
onShow(async () => { onShow(async () => {
// tabBar
if (globalUnreadListenerManager.isInitialized) {
await globalUnreadListenerManager.refreshBadge();
}
}); });
// //
onHide(() => { onHide(() => {
console.log("【消息列表页】页面隐藏");
// //
if (updateTimer) { if (updateTimer) {
clearTimeout(updateTimer); clearTimeout(updateTimer);
updateTimer = null; updateTimer = null;
} }
if (globalTimChatManager) {
// globalTimChatManager globalTimChatManager.setCallback("onConversationListUpdated", null);
// globalTimChatManager.setCallback("onMessageReceived", null);
//
});
//
onUnmounted(() => {
console.log("【消息列表页】页面卸载,清理回调");
//
if (updateTimer) {
clearTimeout(updateTimer);
updateTimer = null;
}
//
if (globalUnreadListenerManager.isInitialized) {
globalUnreadListenerManager.removeCallback("onConversationListUpdated", handleConversationListUpdate);
globalUnreadListenerManager.removeCallback("onMessageReceived", handleMessageReceived);
console.log("【消息列表页】已从回调链移除回调");
} }
}); });
</script> </script>

View File

@ -2,7 +2,7 @@
<view v-if="member" class="flex flex-col h-full items-center justify-center"> <view v-if="member" class="flex flex-col h-full items-center justify-center">
<view class="business-card"> <view class="business-card">
<view class="flex"> <view class="flex">
<image class="mr-10 avatar" :src="member.avatar || '/static/default-avatar.png'"></image> <image class="mr-10 avatar" :src="member.avatar || '/static/default-avatar.svg'"></image>
<view class="w-0 flex-grow leading-normal"> <view class="w-0 flex-grow leading-normal">
<view class="flex items-center"> <view class="flex items-center">
<view class="mr-5 text-lg font-semibold text-dark">{{ member.anotherName }}</view> <view class="mr-5 text-lg font-semibold text-dark">{{ member.anotherName }}</view>

View File

@ -25,7 +25,7 @@
</view> </view>
<view v-for="i in teammate.leaders" :key="i._id" class="mt-12 flex p-10 border-primary rounded-sm" <view v-for="i in teammate.leaders" :key="i._id" class="mt-12 flex p-10 border-primary rounded-sm"
@click="toHomePage(i.userid)"> @click="toHomePage(i.userid)">
<image class="flex-shrink-0 mr-10 avatar" :src="i.avatar || '/static/default-avatar.png'"></image> <image class="flex-shrink-0 mr-10 avatar" :src="i.avatar || '/static/default-avatar.svg'"></image>
<view class="w-0 flex-grow leading-normal"> <view class="w-0 flex-grow leading-normal">
<view class="flex items-center justify-between"> <view class="flex items-center justify-between">
<view class="flex-shrink-0 mr-5 view-lg text-dark font-semibold">{{ i.anotherName }}</view> <view class="flex-shrink-0 mr-5 view-lg text-dark font-semibold">{{ i.anotherName }}</view>
@ -50,7 +50,7 @@
</view> </view>
<view v-for="i in teammate.members" :key="i._id" class="mt-12 flex p-10 border-primary rounded-sm" <view v-for="i in teammate.members" :key="i._id" class="mt-12 flex p-10 border-primary rounded-sm"
@click="toHomePage(i.userid)"> @click="toHomePage(i.userid)">
<image class="flex-shrink-0 mr-10 avatar" :src="i.avatar || '/static/default-avatar.png'"></image> <image class="flex-shrink-0 mr-10 avatar" :src="i.avatar || '/static/default-avatar.svg'"></image>
<view class="w-0 flex-grow leading-normal"> <view class="w-0 flex-grow leading-normal">
<view class="flex items-center justify-between"> <view class="flex items-center justify-between">
<view class="flex-shrink-0 mr-5 view-lg text-dark font-semibold">{{ i.anotherName }}</view> <view class="flex-shrink-0 mr-5 view-lg text-dark font-semibold">{{ i.anotherName }}</view>

View File

@ -3,7 +3,6 @@ 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 { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
const env = __VITE_ENV__; const env = __VITE_ENV__;
export default defineStore("accountStore", () => { export default defineStore("accountStore", () => {
@ -77,10 +76,6 @@ export default defineStore("accountStore", () => {
isIMInitialized.value = true; isIMInitialized.value = true;
console.log('IM 初始化成功'); console.log('IM 初始化成功');
// IM 初始化成功后,设置全局未读消息监听
globalUnreadListenerManager.setup();
return true; return true;
} catch (error) { } catch (error) {
console.log('IM初始化异常跳过 IM 初始化:', error.message); console.log('IM初始化异常跳过 IM 初始化:', error.message);
@ -96,11 +91,6 @@ export default defineStore("accountStore", () => {
await globalTimChatManager.destroy(); await globalTimChatManager.destroy();
console.log('腾讯IM退出成功'); console.log('腾讯IM退出成功');
} }
// 清除全局未读监听
if (globalUnreadListenerManager.isInitialized) {
globalUnreadListenerManager.destroy();
}
} catch (error) { } catch (error) {
console.error('退出腾讯IM失败:', error); console.error('退出腾讯IM失败:', error);
} }
@ -113,11 +103,6 @@ export default defineStore("accountStore", () => {
// 清除本地存储 // 清除本地存储
uni.removeStorageSync('account'); uni.removeStorageSync('account');
uni.removeStorageSync('openid'); uni.removeStorageSync('openid');
// 清除 tabBar 徽章
uni.removeTabBarBadge({
index: 1
});
} }
async function getExternalUserId(corpId) { async function getExternalUserId(corpId) {

View File

@ -44,7 +44,7 @@ export default defineStore("teamStore", () => {
const res = await api('getTeamMemberAvatarsAndName', { const res = await api('getTeamMemberAvatarsAndName', {
teamId, teamId,
corpId: account.value.corpId corpId: account.value.corpId
}, false); });
if (res && res.success && res.data) { if (res && res.success && res.data) {
return res.data; return res.data;
} }

View File

@ -144,7 +144,7 @@ function mergeConversationData(conversation, groupDetailsMap) {
name: formatConversationName(groupDetail), name: formatConversationName(groupDetail),
// 更新头像(优先使用已有头像,避免闪动) // 更新头像(优先使用已有头像,避免闪动)
avatar: conversation.avatar || groupDetail.patient?.avatar || '/static/default-avatar.png', avatar: conversation.avatar || groupDetail.patient?.avatar || '/static/default-avatar.svg',
// 【修复】保留未读消息数(确保不被覆盖) // 【修复】保留未读消息数(确保不被覆盖)
unreadCount: conversation.unreadCount || 0 unreadCount: conversation.unreadCount || 0

View File

@ -7,11 +7,8 @@ import { globalTimChatManager } from './tim-chat.js';
class GlobalUnreadListenerManager { class GlobalUnreadListenerManager {
constructor() { constructor() {
this.isInitialized = false; this.isInitialized = false;
this.callbackChain = { this.originalConversationListCallback = null;
onConversationListUpdated: [], this.originalMessageReceivedCallback = null;
onMessageReceived: []
};
this.updateTimer = null; // 防抖定时器
} }
/** /**
@ -28,76 +25,39 @@ class GlobalUnreadListenerManager {
return; return;
} }
// 保存原始回调到回调链 // 保存原始回调(在覆盖前保存)
const originalConversationListCallback = globalTimChatManager.callbacks.onConversationListUpdated; this.originalConversationListCallback = globalTimChatManager.callbacks.onConversationListUpdated;
const originalMessageReceivedCallback = globalTimChatManager.callbacks.onMessageReceived; this.originalMessageReceivedCallback = globalTimChatManager.callbacks.onMessageReceived;
if (originalConversationListCallback && typeof originalConversationListCallback === 'function') { console.log('保存原始回调:', {
this.callbackChain.onConversationListUpdated.push(originalConversationListCallback); hasConversationListCallback: !!this.originalConversationListCallback,
} hasMessageReceivedCallback: !!this.originalMessageReceivedCallback
if (originalMessageReceivedCallback && typeof originalMessageReceivedCallback === 'function') {
this.callbackChain.onMessageReceived.push(originalMessageReceivedCallback);
}
console.log('全局未读监听初始化,回调链长度:', {
onConversationListUpdated: this.callbackChain.onConversationListUpdated.length,
onMessageReceived: this.callbackChain.onMessageReceived.length
}); });
// 监听会话列表更新事件(包括消息接收和已读状态变化) // 监听会话列表更新事件(包括消息接收和已读状态变化)
globalTimChatManager.setCallback('onConversationListUpdated', (eventData) => { globalTimChatManager.setCallback('onConversationListUpdated', (eventData) => {
console.log('【全局未读监听】onConversationListUpdated 触发,回调链长度:', this.callbackChain.onConversationListUpdated.length); console.log('onConversationListUpdated 触发,调用原始回调');
// 调用原始回调(如果存在)
// 执行回调链中的所有回调 if (this.originalConversationListCallback && typeof this.originalConversationListCallback === 'function') {
this.callbackChain.onConversationListUpdated.forEach((callback, index) => { this.originalConversationListCallback(eventData);
try {
console.log(`【全局未读监听】执行回调链 #${index + 1}`);
callback(eventData);
} catch (error) {
console.error(`【全局未读监听】执行 onConversationListUpdated 回调 #${index + 1} 失败:`, error);
} }
}); // 更新 tabBar 徽章
this.updateTabBarBadge();
// 防抖更新 tabBar 徽章
this.debouncedUpdateTabBarBadge();
}); });
// 监听消息接收事件 // 监听消息接收事件
globalTimChatManager.setCallback('onMessageReceived', (message) => { globalTimChatManager.setCallback('onMessageReceived', (message) => {
console.log('【全局未读监听】onMessageReceived 触发,回调链长度:', this.callbackChain.onMessageReceived.length); console.log('onMessageReceived 触发,调用原始回调');
// 调用原始回调(如果存在)
// 执行回调链中的所有回调 if (this.originalMessageReceivedCallback && typeof this.originalMessageReceivedCallback === 'function') {
this.callbackChain.onMessageReceived.forEach((callback, index) => { this.originalMessageReceivedCallback(message);
try {
console.log(`【全局未读监听】执行回调链 #${index + 1}`);
callback(message);
} catch (error) {
console.error(`【全局未读监听】执行 onMessageReceived 回调 #${index + 1} 失败:`, error);
} }
}); // 更新 tabBar 徽章
this.updateTabBarBadge();
// 防抖更新 tabBar 徽章
this.debouncedUpdateTabBarBadge();
}); });
this.isInitialized = true; this.isInitialized = true;
console.log('全局未读消息监听已设置'); console.log('全局未读消息监听已设置');
// 初始化时立即更新一次徽章
this.updateTabBarBadge();
}
/**
* 防抖更新 tabBar 徽章
* 避免频繁更新导致性能问题
*/
debouncedUpdateTabBarBadge() {
if (this.updateTimer) {
clearTimeout(this.updateTimer);
}
this.updateTimer = setTimeout(() => {
this.updateTabBarBadge();
}, 200); // 减少到 200ms 防抖延迟,提高响应速度
} }
/** /**
@ -107,22 +67,21 @@ class GlobalUnreadListenerManager {
async updateTabBarBadge() { async updateTabBarBadge() {
try { try {
if (!globalTimChatManager || !globalTimChatManager.tim) { if (!globalTimChatManager || !globalTimChatManager.tim) {
console.warn('【全局未读监听】globalTimChatManager 或 tim 未初始化'); console.warn('globalTimChatManager 或 tim 未初始化');
return; return;
} }
const response = await globalTimChatManager.tim.getConversationList(); const response = await globalTimChatManager.tim.getConversationList();
if (!response || !response.data || !response.data.conversationList) { if (!response || !response.data || !response.data.conversationList) {
console.warn('【全局未读监听】获取会话列表返回数据异常'); console.warn('获取会话列表返回数据异常');
return; return;
} }
const totalUnreadCount = this.calculateGroupUnreadCount(response.data.conversationList); const totalUnreadCount = this.calculateGroupUnreadCount(response.data.conversationList);
console.log('【全局未读监听】计算总未读数:', totalUnreadCount);
this.setTabBarBadge(totalUnreadCount); this.setTabBarBadge(totalUnreadCount);
} catch (error) { } catch (error) {
console.error('【全局未读监听】更新 tabBar 徽章失败:', error); console.error('更新 tabBar 徽章失败:', error);
} }
} }
@ -148,68 +107,25 @@ class GlobalUnreadListenerManager {
index: tabIndex, index: tabIndex,
text: count > 99 ? '99+' : String(count) text: count > 99 ? '99+' : String(count)
}); });
console.log(`已更新 tabBar 徽章(索引 ${tabIndex}:`, count); console.log(`已更新 tabBar 徽章(索引 ${tabIndex}:`, count);
} else { } else {
uni.removeTabBarBadge({ // uni.removeTabBarBadge({
index: tabIndex // index: tabIndex
}); // });
console.log(`已移除 tabBar 徽章(索引 ${tabIndex}`); console.log(`已移除 tabBar 徽章(索引 ${tabIndex}`);
} }
} }
/**
* 手动刷新徽章
* 用于页面显示时强制刷新
*/
async refreshBadge() { async refreshBadge() {
console.log('手动刷新 tabBar 徽章'); console.log('手动刷新 tabBar 徽章');
await this.updateTabBarBadge(); await this.updateTabBarBadge();
} }
/**
* 添加回调到回调链
* @param {string} eventName - 事件名称
* @param {Function} callback - 回调函数
*/
addCallback(eventName, callback) {
if (this.callbackChain[eventName] && typeof callback === 'function') {
// 避免重复添加同一个回调
if (!this.callbackChain[eventName].includes(callback)) {
this.callbackChain[eventName].push(callback);
console.log(`✓ 已添加回调到 ${eventName} 回调链,当前链长度:`, this.callbackChain[eventName].length);
} else {
console.log(`⚠️ 回调已存在于 ${eventName} 回调链中,跳过添加`);
}
}
}
/**
* 从回调链中移除回调
* @param {string} eventName - 事件名称
* @param {Function} callback - 回调函数
*/
removeCallback(eventName, callback) {
if (this.callbackChain[eventName]) {
const index = this.callbackChain[eventName].indexOf(callback);
if (index > -1) {
this.callbackChain[eventName].splice(index, 1);
console.log(`✓ 已从 ${eventName} 回调链移除回调,当前链长度:`, this.callbackChain[eventName].length);
}
}
}
/** /**
* 清除监听可选 * 清除监听可选
*/ */
destroy() { destroy() {
if (this.updateTimer) {
clearTimeout(this.updateTimer);
this.updateTimer = null;
}
this.callbackChain = {
onConversationListUpdated: [],
onMessageReceived: []
};
this.isInitialized = false; this.isInitialized = false;
console.log('全局未读消息监听已清除'); console.log('全局未读消息监听已清除');
} }