From b2f252602895157fddd458fa691a5e5a9a6a29de Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Wed, 11 Feb 2026 09:51:02 +0800
Subject: [PATCH 1/5] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=9C=AA=E8=AF=BB?=
=?UTF-8?q?=E6=B6=88=E6=81=AF=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
App.vue | 3 +
pages/message/index.vue | 16 +-
pages/message/message.vue | 281 +++++++++++++++++++-------------
store/account.js | 15 ++
utils/global-unread-listener.js | 146 +++++++++++++----
5 files changed, 315 insertions(+), 146 deletions(-)
diff --git a/App.vue b/App.vue
index 0fa925e..b4a9dbf 100644
--- a/App.vue
+++ b/App.vue
@@ -1,6 +1,7 @@
diff --git a/store/account.js b/store/account.js
index a48647a..0c62dd8 100644
--- a/store/account.js
+++ b/store/account.js
@@ -3,6 +3,7 @@ import { defineStore } from "pinia";
import api from '@/utils/api';
import { toast } from '@/utils/widget';
import { initGlobalTIM, globalTimChatManager } from "@/utils/tim-chat.js";
+import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
const env = __VITE_ENV__;
export default defineStore("accountStore", () => {
@@ -76,6 +77,10 @@ export default defineStore("accountStore", () => {
isIMInitialized.value = true;
console.log('IM 初始化成功');
+
+ // IM 初始化成功后,设置全局未读消息监听
+ globalUnreadListenerManager.setup();
+
return true;
} catch (error) {
console.log('IM初始化异常,跳过 IM 初始化:', error.message);
@@ -91,6 +96,11 @@ export default defineStore("accountStore", () => {
await globalTimChatManager.destroy();
console.log('腾讯IM退出成功');
}
+
+ // 清除全局未读监听
+ if (globalUnreadListenerManager.isInitialized) {
+ globalUnreadListenerManager.destroy();
+ }
} catch (error) {
console.error('退出腾讯IM失败:', error);
}
@@ -103,6 +113,11 @@ export default defineStore("accountStore", () => {
// 清除本地存储
uni.removeStorageSync('account');
uni.removeStorageSync('openid');
+
+ // 清除 tabBar 徽章
+ uni.removeTabBarBadge({
+ index: 1
+ });
}
async function getExternalUserId(corpId) {
diff --git a/utils/global-unread-listener.js b/utils/global-unread-listener.js
index 5be30c6..1baff59 100644
--- a/utils/global-unread-listener.js
+++ b/utils/global-unread-listener.js
@@ -7,8 +7,11 @@ import { globalTimChatManager } from './tim-chat.js';
class GlobalUnreadListenerManager {
constructor() {
this.isInitialized = false;
- this.originalConversationListCallback = null;
- this.originalMessageReceivedCallback = null;
+ this.callbackChain = {
+ onConversationListUpdated: [],
+ onMessageReceived: []
+ };
+ this.updateTimer = null; // 防抖定时器
}
/**
@@ -25,39 +28,76 @@ class GlobalUnreadListenerManager {
return;
}
- // 保存原始回调(在覆盖前保存)
- this.originalConversationListCallback = globalTimChatManager.callbacks.onConversationListUpdated;
- this.originalMessageReceivedCallback = globalTimChatManager.callbacks.onMessageReceived;
+ // 保存原始回调到回调链
+ const originalConversationListCallback = globalTimChatManager.callbacks.onConversationListUpdated;
+ const originalMessageReceivedCallback = globalTimChatManager.callbacks.onMessageReceived;
- console.log('保存原始回调:', {
- hasConversationListCallback: !!this.originalConversationListCallback,
- hasMessageReceivedCallback: !!this.originalMessageReceivedCallback
+ if (originalConversationListCallback && typeof originalConversationListCallback === 'function') {
+ this.callbackChain.onConversationListUpdated.push(originalConversationListCallback);
+ }
+ 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) => {
- console.log('onConversationListUpdated 触发,调用原始回调');
- // 调用原始回调(如果存在)
- if (this.originalConversationListCallback && typeof this.originalConversationListCallback === 'function') {
- this.originalConversationListCallback(eventData);
- }
- // 更新 tabBar 徽章
- this.updateTabBarBadge();
+ console.log('【全局未读监听】onConversationListUpdated 触发,回调链长度:', this.callbackChain.onConversationListUpdated.length);
+
+ // 执行回调链中的所有回调
+ this.callbackChain.onConversationListUpdated.forEach((callback, index) => {
+ try {
+ console.log(`【全局未读监听】执行回调链 #${index + 1}`);
+ callback(eventData);
+ } catch (error) {
+ console.error(`【全局未读监听】执行 onConversationListUpdated 回调 #${index + 1} 失败:`, error);
+ }
+ });
+
+ // 防抖更新 tabBar 徽章
+ this.debouncedUpdateTabBarBadge();
});
// 监听消息接收事件
globalTimChatManager.setCallback('onMessageReceived', (message) => {
- console.log('onMessageReceived 触发,调用原始回调');
- // 调用原始回调(如果存在)
- if (this.originalMessageReceivedCallback && typeof this.originalMessageReceivedCallback === 'function') {
- this.originalMessageReceivedCallback(message);
- }
- // 更新 tabBar 徽章
- this.updateTabBarBadge();
+ console.log('【全局未读监听】onMessageReceived 触发,回调链长度:', this.callbackChain.onMessageReceived.length);
+
+ // 执行回调链中的所有回调
+ this.callbackChain.onMessageReceived.forEach((callback, index) => {
+ try {
+ console.log(`【全局未读监听】执行回调链 #${index + 1}`);
+ callback(message);
+ } catch (error) {
+ console.error(`【全局未读监听】执行 onMessageReceived 回调 #${index + 1} 失败:`, error);
+ }
+ });
+
+ // 防抖更新 tabBar 徽章
+ this.debouncedUpdateTabBarBadge();
});
this.isInitialized = true;
console.log('全局未读消息监听已设置');
+
+ // 初始化时立即更新一次徽章
+ this.updateTabBarBadge();
+ }
+
+ /**
+ * 防抖更新 tabBar 徽章
+ * 避免频繁更新导致性能问题
+ */
+ debouncedUpdateTabBarBadge() {
+ if (this.updateTimer) {
+ clearTimeout(this.updateTimer);
+ }
+ this.updateTimer = setTimeout(() => {
+ this.updateTabBarBadge();
+ }, 200); // 减少到 200ms 防抖延迟,提高响应速度
}
/**
@@ -67,21 +107,22 @@ class GlobalUnreadListenerManager {
async updateTabBarBadge() {
try {
if (!globalTimChatManager || !globalTimChatManager.tim) {
- console.warn('globalTimChatManager 或 tim 未初始化');
+ console.warn('【全局未读监听】globalTimChatManager 或 tim 未初始化');
return;
}
const response = await globalTimChatManager.tim.getConversationList();
if (!response || !response.data || !response.data.conversationList) {
- console.warn('获取会话列表返回数据异常');
+ console.warn('【全局未读监听】获取会话列表返回数据异常');
return;
}
const totalUnreadCount = this.calculateGroupUnreadCount(response.data.conversationList);
+ console.log('【全局未读监听】计算总未读数:', totalUnreadCount);
this.setTabBarBadge(totalUnreadCount);
} catch (error) {
- console.error('更新 tabBar 徽章失败:', error);
+ console.error('【全局未读监听】更新 tabBar 徽章失败:', error);
}
}
@@ -107,25 +148,68 @@ class GlobalUnreadListenerManager {
index: tabIndex,
text: count > 99 ? '99+' : String(count)
});
- console.log(`已更新 tabBar 徽章(索引 ${tabIndex}):`, count);
+ console.log(`✓ 已更新 tabBar 徽章(索引 ${tabIndex}):`, count);
} else {
- // uni.removeTabBarBadge({
- // index: tabIndex
- // });
- console.log(`已移除 tabBar 徽章(索引 ${tabIndex})`);
+ uni.removeTabBarBadge({
+ index: tabIndex
+ });
+ console.log(`✓ 已移除 tabBar 徽章(索引 ${tabIndex})`);
}
}
-
+ /**
+ * 手动刷新徽章
+ * 用于页面显示时强制刷新
+ */
async refreshBadge() {
console.log('手动刷新 tabBar 徽章');
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() {
+ if (this.updateTimer) {
+ clearTimeout(this.updateTimer);
+ this.updateTimer = null;
+ }
+ this.callbackChain = {
+ onConversationListUpdated: [],
+ onMessageReceived: []
+ };
this.isInitialized = false;
console.log('全局未读消息监听已清除');
}
From 781c8803a8aecbf34c3b623610961add4bae1c77 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Wed, 11 Feb 2026 14:38:28 +0800
Subject: [PATCH 2/5] no message
---
pages/message/chat.scss | 23 ++++++++++++++++++-----
pages/message/components/chat-input.vue | 2 +-
pages/message/index.vue | 15 ++++++++++++++-
3 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index fbe22c3..2a9772e 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -6,17 +6,23 @@ $text-color-sub: #999;
$primary-color: #0877F1;
.chat-page {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
display: flex;
flex-direction: column;
- height: 100vh;
background-color: #f5f5f5;
overflow: hidden;
}
/* 患者信息栏样式 */
.patient-info-bar {
- position: sticky;
+ position: fixed;
top: 0;
+ left: 0;
+ right: 0;
background: #fff;
border-bottom: 1rpx solid #f0f0f0;
padding: 20rpx 32rpx;
@@ -110,11 +116,14 @@ $primary-color: #0877F1;
}
.chat-content {
- flex: 1;
+ position: fixed;
+ top: 100rpx; /* 患者信息栏高度,根据实际调整 */
+ left: 0;
+ right: 0;
+ bottom: 120rpx; /* 输入框高度,根据实际调整 */
box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
- min-height: 0;
}
.chat-content-compressed {
@@ -379,12 +388,16 @@ $primary-color: #0877F1;
}
.input-section {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
background: #fff;
border-top: 1rpx solid #e0e0e0;
- position: relative;
z-index: 200;
padding-bottom: env(safe-area-inset-bottom);
flex-shrink: 0;
+ transform: translateZ(0); /* 开启硬件加速,提升性能 */
}
.input-toolbar {
diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue
index 6e04776..a9d28bd 100644
--- a/pages/message/components/chat-input.vue
+++ b/pages/message/components/chat-input.vue
@@ -8,7 +8,7 @@
+ :auto-height="true" :show-confirm-bar="false" :adjust-position="false" :cursor-spacing="40" />
diff --git a/pages/message/index.vue b/pages/message/index.vue
index d7d12b9..9e9a9da 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -1,5 +1,6 @@
-
+
+
@@ -213,6 +214,9 @@ const chatInfo = ref({
avatar: "/static/home/avatar.svg",
});
+// 键盘高度
+const keyboardHeight = ref(0);
+
// 评价弹窗状态
const isEvaluationPopupOpen = ref(false);
@@ -443,6 +447,12 @@ onLoad((options) => {
chatInfo.value.userID = options.userID;
}
+ // 监听键盘高度变化
+ uni.onKeyboardHeightChange((res) => {
+ console.log("键盘高度变化:", res.height);
+ keyboardHeight.value = res.height;
+ });
+
checkLoginAndInitTIM();
updateNavigationTitle();
});
@@ -998,6 +1008,9 @@ const handleApplyConsult = async () => {
onUnmounted(() => {
clearMessageCache();
+ // 移除键盘监听
+ uni.offKeyboardHeightChange();
+
timChatManager.setCallback("onSDKReady", null);
timChatManager.setCallback("onSDKNotReady", null);
timChatManager.setCallback("onMessageReceived", null);
From b91723e561f89fe5c95d74c7cc3b71e6ab5c6290 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Wed, 11 Feb 2026 15:21:05 +0800
Subject: [PATCH 3/5] =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=A1=86=E8=B0=83?=
=?UTF-8?q?=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pages/message/chat.scss | 4 ++++
pages/message/components/chat-input.vue | 5 +++--
pages/message/index.vue | 12 ++++++++++++
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index 2a9772e..65608f3 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -15,6 +15,7 @@ $primary-color: #0877F1;
flex-direction: column;
background-color: #f5f5f5;
overflow: hidden;
+ transition: padding-bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* 患者信息栏样式 */
@@ -124,6 +125,7 @@ $primary-color: #0877F1;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
+ transition: bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.chat-content-compressed {
@@ -398,6 +400,8 @@ $primary-color: #0877F1;
padding-bottom: env(safe-area-inset-bottom);
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 {
diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue
index a9d28bd..054d8b0 100644
--- a/pages/message/components/chat-input.vue
+++ b/pages/message/components/chat-input.vue
@@ -1,5 +1,5 @@
-
+
@@ -8,7 +8,7 @@
+ :auto-height="true" :show-confirm-bar="false" :adjust-position="false" />
@@ -77,6 +77,7 @@ const props = defineProps({
groupId: { type: String, default: '' },
userId: { type: String, default: '' },
corpId: { type: String, default: '' },
+ keyboardHeight: { type: Number, default: 0 },
});
// Emits
diff --git a/pages/message/index.vue b/pages/message/index.vue
index 9e9a9da..b76da27 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -23,6 +23,7 @@
scrollToBottom(true)"
@messageSent="() => scrollToBottom(true)"
/>
@@ -450,7 +452,17 @@ onLoad((options) => {
// 监听键盘高度变化
uni.onKeyboardHeightChange((res) => {
console.log("键盘高度变化:", res.height);
+ const oldHeight = keyboardHeight.value;
keyboardHeight.value = res.height;
+
+ // 键盘弹出时(从0变为非0),自动滚动到底部
+ if (oldHeight === 0 && res.height > 0) {
+ nextTick(() => {
+ setTimeout(() => {
+ scrollToBottom(true);
+ }, 100);
+ });
+ }
});
checkLoginAndInitTIM();
From 110b6754f93c7a0e8857294ef869a1d34b025e90 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Wed, 11 Feb 2026 15:27:56 +0800
Subject: [PATCH 4/5] no message
---
pages/message/chat.scss | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index 65608f3..8cdb88e 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -397,7 +397,6 @@ $primary-color: #0877F1;
background: #fff;
border-top: 1rpx solid #e0e0e0;
z-index: 200;
- padding-bottom: env(safe-area-inset-bottom);
flex-shrink: 0;
transform: translateZ(0); /* 开启硬件加速,提升性能 */
transition: bottom 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
@@ -407,7 +406,8 @@ $primary-color: #0877F1;
.input-toolbar {
display: flex;
align-items: center;
- padding: 16rpx 20rpx;
+ padding: 12rpx 20rpx;
+ padding-bottom: env(safe-area-inset-bottom);
gap: 12rpx;
}
@@ -513,7 +513,8 @@ $primary-color: #0877F1;
justify-content: flex-start;
background: #fff;
border-top: 1rpx solid #eee;
- padding: 20rpx 0 40rpx 60rpx;
+ padding: 20rpx 0 20rpx 60rpx;
+ padding-bottom: env(safe-area-inset-bottom);
gap: 40rpx 50rpx;
flex-wrap: wrap;
background-color: #f5f5f5;
From abe38ad86fa704e6a5d6de14dc0f238d53b235a1 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Wed, 11 Feb 2026 17:11:20 +0800
Subject: [PATCH 5/5] no message
---
pages/message/components/chat-input.vue | 12 ++++++++----
pages/message/index.vue | 2 +-
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue
index 054d8b0..1b24085 100644
--- a/pages/message/components/chat-input.vue
+++ b/pages/message/components/chat-input.vue
@@ -8,7 +8,7 @@
+ :auto-height="true" :show-confirm-bar="false" :adjust-position="false" :hold-keyboard="true" />
@@ -151,11 +151,15 @@ const initRecorderManager = () => {
const sendTextMessage = async () => {
if (!inputText.value.trim()) return;
- await sendMessage("text", inputText.value);
+ const textToSend = inputText.value;
inputText.value = "";
- // 收起键盘
- uni.hideKeyboard();
+ await sendMessage("text", textToSend);
+
+ // 发送后保持焦点,不收起键盘
+ nextTick(() => {
+ // hold-keyboard 属性会自动保持键盘显示
+ });
};
diff --git a/pages/message/index.vue b/pages/message/index.vue
index b76da27..7c4b6be 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -23,7 +23,7 @@