1. 开方线上功能基本完成
2. 修复了一些抽屉组件的滑动失效问题
This commit is contained in:
@@ -4,7 +4,32 @@
|
||||
*/
|
||||
import { chatConfig } from '@/config/chat.js';
|
||||
import { sendWebSocketMessage, isWebSocketConnected } from '@/utils/ws/initWebSocket.js';
|
||||
import { getChatRegisterInfoApi, getMessagesByRoomIdApi } from '@/request/api/im.js';
|
||||
import { getChatRegisterInfoApi, getMessagesByRoomIdApi } from '@/api/chat.js';
|
||||
import { getMessagePreview } from '@/utils/chat/messagePreview.js';
|
||||
|
||||
function unwrapMessagesPayload(res) {
|
||||
if (res == null) return null;
|
||||
if (res.result !== undefined && res.result !== null) return res.result;
|
||||
if (res.data && res.data.result !== undefined && res.data.result !== null) return res.data.result;
|
||||
if (res.data && typeof res.data === 'object') return res.data;
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 房间 ID 归一化,便于 WS 与列表接口字段比较 */
|
||||
export function normalizeRoomId(id) {
|
||||
return String(id == null ? '' : id).trim();
|
||||
}
|
||||
|
||||
/** 仅在工作台 / 在线接诊列表为栈顶页时派发列表增量,减少后台页无意义合并 */
|
||||
function shouldEmitDoctorImListUpdate() {
|
||||
const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : [];
|
||||
const cur = pages && pages.length ? pages[pages.length - 1] : null;
|
||||
const route = cur && cur.route ? cur.route : '';
|
||||
return (
|
||||
route === 'pages/workbench/index' ||
|
||||
route === 'subPackages/sub_online_reception/pages/list'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天室管理器类
|
||||
@@ -33,16 +58,17 @@ class ChatRoomManager {
|
||||
* 职责:设置当前房间,重置未读数,保存到本地存储
|
||||
*/
|
||||
enterRoom(roomId) {
|
||||
if (!roomId) {
|
||||
const id = normalizeRoomId(roomId);
|
||||
if (!id) {
|
||||
console.warn('房间ID不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentRoomId = roomId;
|
||||
uni.setStorageSync('currentRoomId', roomId);
|
||||
this.resetUnreadCount(roomId);
|
||||
|
||||
console.log('进入聊天室:', roomId);
|
||||
|
||||
this.currentRoomId = id;
|
||||
uni.setStorageSync('currentRoomId', id);
|
||||
this.resetUnreadCount(id);
|
||||
|
||||
console.log('进入聊天室:', id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,14 +95,14 @@ class ChatRoomManager {
|
||||
* 职责:如果房间不是当前房间,则增加未读数并通知
|
||||
*/
|
||||
increaseUnreadCount(roomId) {
|
||||
if (!roomId) return;
|
||||
|
||||
// 如果是当前房间,不增加未读数
|
||||
if (this.getCurrentRoomId() === roomId) {
|
||||
const key = normalizeRoomId(roomId);
|
||||
if (!key) return;
|
||||
|
||||
if (normalizeRoomId(this.getCurrentRoomId()) === key) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.unreadCounts[roomId] = (this.unreadCounts[roomId] || 0) + 1;
|
||||
|
||||
this.unreadCounts[key] = (this.unreadCounts[key] || 0) + 1;
|
||||
this.notifyUnreadChange();
|
||||
}
|
||||
|
||||
@@ -86,8 +112,9 @@ class ChatRoomManager {
|
||||
* 职责:将指定房间的未读数重置为0并通知
|
||||
*/
|
||||
resetUnreadCount(roomId) {
|
||||
if (this.unreadCounts[roomId]) {
|
||||
this.unreadCounts[roomId] = 0;
|
||||
const key = normalizeRoomId(roomId);
|
||||
if (this.unreadCounts[key]) {
|
||||
this.unreadCounts[key] = 0;
|
||||
this.notifyUnreadChange();
|
||||
}
|
||||
}
|
||||
@@ -98,7 +125,8 @@ class ChatRoomManager {
|
||||
* @returns {number} 未读数
|
||||
*/
|
||||
getUnreadCount(roomId) {
|
||||
return this.unreadCounts[roomId] || 0;
|
||||
const key = normalizeRoomId(roomId);
|
||||
return this.unreadCounts[key] || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,18 +165,19 @@ class ChatRoomManager {
|
||||
* 职责:将消息添加到指定房间的消息列表
|
||||
*/
|
||||
addMessageToRoom(roomId, message) {
|
||||
if (!roomId || !message) return;
|
||||
|
||||
if (!this.roomMessages[roomId]) {
|
||||
this.roomMessages[roomId] = [];
|
||||
const key = normalizeRoomId(roomId);
|
||||
if (!key || !message) return;
|
||||
|
||||
if (!this.roomMessages[key]) {
|
||||
this.roomMessages[key] = [];
|
||||
}
|
||||
|
||||
|
||||
// 检查消息是否已存在(避免重复)
|
||||
const existingIndex = this.roomMessages[roomId].findIndex(m => m.id === message.id);
|
||||
const existingIndex = this.roomMessages[key].findIndex(m => m.id === message.id);
|
||||
if (existingIndex !== -1) {
|
||||
this.roomMessages[roomId][existingIndex] = message;
|
||||
this.roomMessages[key][existingIndex] = message;
|
||||
} else {
|
||||
this.roomMessages[roomId].push(message);
|
||||
this.roomMessages[key].push(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +187,8 @@ class ChatRoomManager {
|
||||
* @returns {Array} 消息列表
|
||||
*/
|
||||
getRoomMessages(roomId) {
|
||||
return this.roomMessages[roomId] || [];
|
||||
const key = normalizeRoomId(roomId);
|
||||
return this.roomMessages[key] || [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,28 +198,32 @@ class ChatRoomManager {
|
||||
* @returns {Promise<Array>} 消息列表
|
||||
*/
|
||||
async loadRoomMessages(roomId, lastMessageId = 0) {
|
||||
if (!roomId) {
|
||||
const key = normalizeRoomId(roomId);
|
||||
if (!key) {
|
||||
console.warn('房间ID不能为空');
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const res = await getMessagesByRoomIdApi({
|
||||
room_id: roomId,
|
||||
room_id: key,
|
||||
last_message_id: lastMessageId || 0
|
||||
});
|
||||
|
||||
if (res && res.data && res.data.result) {
|
||||
const messages = this.processMessages(res.data.result.list || []);
|
||||
this.lastMessageId = res.data.result.last_message_id || 0;
|
||||
|
||||
|
||||
const payload = unwrapMessagesPayload(res);
|
||||
if (payload && typeof payload === 'object') {
|
||||
const rawList = payload.list;
|
||||
const list = Array.isArray(rawList) ? rawList : rawList != null ? [rawList] : [];
|
||||
const messages = this.processMessages(list);
|
||||
this.lastMessageId = payload.last_message_id || 0;
|
||||
|
||||
// 如果是首次加载,替换消息列表;否则追加到前面
|
||||
if (lastMessageId === 0) {
|
||||
this.roomMessages[roomId] = messages;
|
||||
this.roomMessages[key] = messages;
|
||||
} else {
|
||||
this.roomMessages[roomId] = [...messages, ...(this.roomMessages[roomId] || [])];
|
||||
this.roomMessages[key] = [...messages, ...(this.roomMessages[key] || [])];
|
||||
}
|
||||
|
||||
|
||||
uni.$emit('load-room-message', lastMessageId === 0 ? 1 : 0);
|
||||
return messages;
|
||||
}
|
||||
@@ -255,13 +289,16 @@ class ChatRoomManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param {number} timestamp - 时间戳(秒)
|
||||
* @returns {string} 格式化后的时间(HH:mm)
|
||||
* 格式化时间(HH:mm),时间戳支持秒或毫秒
|
||||
* @param {number} timestamp
|
||||
* @returns {string}
|
||||
*/
|
||||
formatTime(timestamp) {
|
||||
if (!timestamp) return '';
|
||||
const date = new Date(timestamp * 1000);
|
||||
if (timestamp === undefined || timestamp === null || timestamp === '') return '';
|
||||
const n = Number(timestamp);
|
||||
if (Number.isNaN(n)) return '';
|
||||
const ms = n > 1e12 ? n : n * 1000;
|
||||
const date = new Date(ms);
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
@@ -280,8 +317,9 @@ class ChatRoomManager {
|
||||
id: parsedContent.id,
|
||||
message_id: message.id || ''
|
||||
}).then((res) => {
|
||||
if (res && res.data && res.data.result) {
|
||||
const resultWithFlag = { ...res.data.result, _loaded: true };
|
||||
const info = res && (res.result != null ? res.result : res.data && res.data.result);
|
||||
if (res && info) {
|
||||
const resultWithFlag = { ...info, _loaded: true };
|
||||
const roomId = message.room_id;
|
||||
const roomMessages = this.roomMessages[roomId];
|
||||
|
||||
@@ -312,7 +350,9 @@ class ChatRoomManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* 通过 WebSocket 发送消息(request_type: send_message)。
|
||||
* 在线接诊聊天页(sub_online_reception/pages/chat.vue)主路径已改为 Go IM `POST /api/send-to-user`(与 websocket 配置同基址),
|
||||
* 此处保留供其它仍走 WS 直发的场景;新页面发聊天消息请优先使用 HTTP API。
|
||||
* @param {Object} messageData - 消息数据
|
||||
* @param {string} messageData.room_id - 房间ID
|
||||
* @param {string} messageData.sender_user_id - 发送者ID
|
||||
@@ -382,21 +422,26 @@ class ChatRoomManager {
|
||||
if (data.request_type !== 'receive_message' && !data.room_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const roomId = data.room_id;
|
||||
|
||||
const roomId = normalizeRoomId(data.room_id);
|
||||
if (!roomId) return;
|
||||
|
||||
|
||||
const dataNorm = { ...data, room_id: roomId };
|
||||
|
||||
// 处理消息
|
||||
const message = this.processSingleMessage(data);
|
||||
|
||||
const message = this.processSingleMessage(dataNorm);
|
||||
|
||||
// 添加到房间消息列表
|
||||
this.addMessageToRoom(roomId, message);
|
||||
|
||||
|
||||
const currentNorm = normalizeRoomId(this.getCurrentRoomId());
|
||||
const incrementUnread = currentNorm !== roomId;
|
||||
|
||||
// 如果不是当前房间,增加未读数
|
||||
if (this.getCurrentRoomId() !== roomId) {
|
||||
if (incrementUnread) {
|
||||
this.increaseUnreadCount(roomId);
|
||||
}
|
||||
|
||||
|
||||
// 调用所有注册的处理器
|
||||
this.messageHandlers.forEach(handler => {
|
||||
try {
|
||||
@@ -405,6 +450,27 @@ class ChatRoomManager {
|
||||
console.error('消息处理器执行失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
const lastMessageTime =
|
||||
dataNorm.send_time != null && dataNorm.send_time !== ''
|
||||
? dataNorm.send_time
|
||||
: dataNorm.created_at != null && dataNorm.created_at !== ''
|
||||
? dataNorm.created_at
|
||||
: message.timestamp != null && message.timestamp !== ''
|
||||
? message.timestamp
|
||||
: message.created_at;
|
||||
|
||||
const unreadForRoom = this.getUnreadCount(roomId);
|
||||
|
||||
if (shouldEmitDoctorImListUpdate()) {
|
||||
uni.$emit('doctor-im-room-update', {
|
||||
roomId,
|
||||
last_message_preview: getMessagePreview(dataNorm),
|
||||
last_message_time: lastMessageTime,
|
||||
incrementUnread,
|
||||
unreadForRoom
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +479,8 @@ class ChatRoomManager {
|
||||
*/
|
||||
clearRoomCache(roomId = null) {
|
||||
if (roomId) {
|
||||
delete this.roomMessages[roomId];
|
||||
const key = normalizeRoomId(roomId);
|
||||
if (key) delete this.roomMessages[key];
|
||||
} else {
|
||||
this.roomMessages = {};
|
||||
}
|
||||
|
||||
54
utils/chat/messagePreview.js
Normal file
54
utils/chat/messagePreview.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 会话列表「最后一条消息」预览文案(与患者端 ChatManager.getMessagePreview 对齐)
|
||||
* @param {Object} message
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getMessagePreview(message) {
|
||||
if (!message || message.message_type === undefined) {
|
||||
return '您有一条新消息';
|
||||
}
|
||||
const messageType = message.message_type;
|
||||
const content = message.message_content;
|
||||
|
||||
switch (messageType) {
|
||||
case 0:
|
||||
return content != null ? String(content) : '';
|
||||
case 1:
|
||||
return '[图片]';
|
||||
case 2:
|
||||
return '[语音消息]';
|
||||
case 3:
|
||||
return '[视频]';
|
||||
case 4:
|
||||
return '[处方单]';
|
||||
case 5:
|
||||
return '[文件]';
|
||||
case 6:
|
||||
return '[视频通话]';
|
||||
case 7:
|
||||
return '[语音通话]';
|
||||
case 9:
|
||||
try {
|
||||
const parsed = typeof content === 'object' ? content : JSON.parse(content);
|
||||
if (parsed.type === 'price_update') {
|
||||
return parsed.message || '[价格更新]';
|
||||
}
|
||||
if (parsed.type === 'price_adjust') {
|
||||
return parsed.message || '[价格调整]';
|
||||
}
|
||||
return '[系统消息]';
|
||||
} catch (e) {
|
||||
return '[系统消息]';
|
||||
}
|
||||
case 10:
|
||||
return '[挂号信息]';
|
||||
case 11:
|
||||
return '[就诊信息]';
|
||||
case 12:
|
||||
return '[商品推荐]';
|
||||
case 13:
|
||||
return '[问诊已结束]';
|
||||
default:
|
||||
return '您有一条新消息';
|
||||
}
|
||||
}
|
||||
25
utils/chat/sessionListFormat.js
Normal file
25
utils/chat/sessionListFormat.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 会话列表卡片右上角相对时间(工作台「正在接诊」、在线接诊列表等共用)
|
||||
* @param {Object} item
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatSessionRelativeTime(item) {
|
||||
if (!item) return '';
|
||||
const t =
|
||||
item.last_message_time ||
|
||||
item.updated_at ||
|
||||
item.created_at ||
|
||||
item.register_time;
|
||||
if (t == null || t === '') return '';
|
||||
const ms = typeof t === 'number' ? (t > 1e12 ? t : t * 1000) : new Date(t).getTime();
|
||||
if (Number.isNaN(ms)) return '';
|
||||
const d = new Date(ms);
|
||||
const now = Date.now();
|
||||
const diff = Math.floor((now - ms) / 60000);
|
||||
if (diff < 1) return '刚刚';
|
||||
if (diff < 60) return `${diff}分钟前`;
|
||||
const h = Math.floor(diff / 60);
|
||||
if (h < 24) return `${h}小时前`;
|
||||
const pad = (n) => (n < 10 ? '0' + n : '' + n);
|
||||
return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
}
|
||||
Reference in New Issue
Block a user