|
|
|
@ -17,7 +17,14 @@ Page({ |
|
|
|
connectionStatus: 'disconnected', // 连接状态: disconnected, connecting, connected, error
|
|
|
|
connectionMessage: '', // 连接状态提示消息
|
|
|
|
isMockMode: false, // 默认为真实WebSocket通信模式
|
|
|
|
isManager: false // 是否是与客服的聊天
|
|
|
|
isManager: false, // 是否是与客服的聊天
|
|
|
|
scrollToMessage: '', // 用于滚动到指定消息的ID
|
|
|
|
scrollTop: 0, // 用于scroll-top属性
|
|
|
|
showScrollToBottomBtn: false, // 是否显示"到达最底部"按钮
|
|
|
|
isNearBottom: true, // 用户是否接近底部
|
|
|
|
lastScrollTop: 0, // 上次滚动位置
|
|
|
|
scrollHeight: 0, // 滚动区域高度
|
|
|
|
contentHeight: 0 // 内容高度
|
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
@ -50,31 +57,78 @@ Page({ |
|
|
|
console.log('从本地存储加载了', storedMessages.length, '条历史消息,存储键:', successKey); |
|
|
|
|
|
|
|
// 处理消息数据,确保包含必要的字段
|
|
|
|
const messagesWithRequiredFields = storedMessages.map((msg, index) => ({ |
|
|
|
...msg, |
|
|
|
// 确保消息有id字段,避免被稳定性检查过滤
|
|
|
|
id: msg.id || `local_msg_${Date.now()}_${index}`, |
|
|
|
// 确保消息类型正确
|
|
|
|
type: msg.type || 'message', |
|
|
|
// 确保有sender字段
|
|
|
|
sender: msg.sender || (msg.isSelf ? 'me' : 'other'), |
|
|
|
// 确保有content字段
|
|
|
|
content: msg.content || '', |
|
|
|
// 确保有time字段
|
|
|
|
time: msg.time || this.getFormattedTime(), |
|
|
|
// 重新生成shortTime字段
|
|
|
|
shortTime: msg.time ? this.extractShortTime(msg.time) : this.getShortTime(), |
|
|
|
// 确保有status字段
|
|
|
|
status: msg.status || 'sent', |
|
|
|
// 确保有senderType字段
|
|
|
|
senderType: msg.senderType || 'unknown', |
|
|
|
// 确保有isRead字段
|
|
|
|
isRead: msg.isRead || true, |
|
|
|
// 确保有timestamp字段
|
|
|
|
timestamp: msg.timestamp || Date.now(), |
|
|
|
// 确保有showTime字段
|
|
|
|
showTime: msg.showTime || false |
|
|
|
})); |
|
|
|
const messagesWithRequiredFields = storedMessages.map((msg, index) => { |
|
|
|
// 获取原始时间
|
|
|
|
const originalTime = msg.time; |
|
|
|
let formattedTime; |
|
|
|
|
|
|
|
// 调试:记录原始时间
|
|
|
|
console.log('=== 历史消息时间调试 ==='); |
|
|
|
console.log('原始时间:', originalTime); |
|
|
|
console.log('原始时间类型:', typeof originalTime); |
|
|
|
|
|
|
|
// 无论是否有原始时间,都重新格式化,确保正确的北京时间显示
|
|
|
|
if (originalTime) { |
|
|
|
// 重新格式化所有时间,确保正确的北京时间
|
|
|
|
if (typeof originalTime === 'number') { |
|
|
|
// 数字时间戳,重新格式化
|
|
|
|
formattedTime = this.formatTimestampToTime(originalTime); |
|
|
|
console.log('数字时间戳重新格式化后:', formattedTime); |
|
|
|
} else if (typeof originalTime === 'string') { |
|
|
|
if (originalTime.includes('T')) { |
|
|
|
// ISO格式字符串,重新格式化
|
|
|
|
formattedTime = this.formatServerTime(originalTime); |
|
|
|
console.log('ISO字符串重新格式化后:', formattedTime); |
|
|
|
} else { |
|
|
|
// 其他字符串格式,检查是否是"月-日 时:分"格式
|
|
|
|
const dateTimeMatch = originalTime.match(/^(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{2})$/); |
|
|
|
if (dateTimeMatch) { |
|
|
|
// 已经是正确的北京时间格式,直接使用
|
|
|
|
formattedTime = originalTime; |
|
|
|
console.log('直接使用正确格式的时间:', formattedTime); |
|
|
|
} else { |
|
|
|
// 其他格式,使用当前时间
|
|
|
|
formattedTime = this.getFormattedTime(); |
|
|
|
console.log('格式不正确,使用当前时间:', formattedTime); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 其他类型,使用当前时间
|
|
|
|
formattedTime = this.getFormattedTime(); |
|
|
|
console.log('其他类型,使用当前时间:', formattedTime); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// 没有时间,使用当前时间
|
|
|
|
formattedTime = this.getFormattedTime(); |
|
|
|
console.log('没有原始时间,使用当前时间:', formattedTime); |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
...msg, |
|
|
|
// 确保消息有id字段,避免被稳定性检查过滤
|
|
|
|
id: msg.id || `local_msg_${Date.now()}_${index}`, |
|
|
|
// 确保消息类型正确
|
|
|
|
type: msg.type || 'message', |
|
|
|
// 确保有sender字段
|
|
|
|
sender: msg.sender || (msg.isSelf ? 'me' : 'other'), |
|
|
|
// 确保有content字段
|
|
|
|
content: msg.content || '', |
|
|
|
// 确保有正确格式的time字段(北京时间)
|
|
|
|
time: formattedTime, |
|
|
|
// 重新生成shortTime字段
|
|
|
|
shortTime: this.extractShortTime(formattedTime), |
|
|
|
// 确保有status字段
|
|
|
|
status: msg.status || 'sent', |
|
|
|
// 确保有senderType字段
|
|
|
|
senderType: msg.senderType || 'unknown', |
|
|
|
// 确保有isRead字段
|
|
|
|
isRead: msg.isRead || true, |
|
|
|
// 确保有timestamp字段
|
|
|
|
timestamp: msg.timestamp || Date.now(), |
|
|
|
// 确保有showTime字段
|
|
|
|
showTime: msg.showTime || false |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
// 重新处理时间显示逻辑
|
|
|
|
const processedMessages = this.processMessageTimes(messagesWithRequiredFields); |
|
|
|
@ -658,12 +712,28 @@ Page({ |
|
|
|
// 设置WebSocket事件监听
|
|
|
|
this.setupWebSocketListeners(); |
|
|
|
|
|
|
|
// 连接WebSocket服务器
|
|
|
|
socketManager.connect(wsUrl, { |
|
|
|
maxReconnectAttempts: 5, |
|
|
|
reconnectInterval: 3000, |
|
|
|
heartbeatTime: 30000 |
|
|
|
}); |
|
|
|
// 关键修复:使用应用级别的WebSocket连接,不再重新创建连接
|
|
|
|
// 检查应用级别连接状态
|
|
|
|
const app = getApp(); |
|
|
|
if (app.globalData.webSocketManager && app.globalData.webSocketManager.isConnected) { |
|
|
|
// 应用级别连接已存在,使用它
|
|
|
|
console.log('使用应用级别WebSocket连接'); |
|
|
|
this.setData({ |
|
|
|
connectionStatus: 'connected', |
|
|
|
connectionMessage: '已连接' |
|
|
|
}); |
|
|
|
// 发送认证消息
|
|
|
|
this.sendAuthMessage(); |
|
|
|
} else { |
|
|
|
// 应用级别连接不存在,创建新连接
|
|
|
|
console.log('创建新的WebSocket连接'); |
|
|
|
// 连接WebSocket服务器
|
|
|
|
socketManager.connect(wsUrl, { |
|
|
|
maxReconnectAttempts: 5, |
|
|
|
reconnectInterval: 3000, |
|
|
|
heartbeatTime: 30000 |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// 监听网络状态变化
|
|
|
|
this.startNetworkListener(); |
|
|
|
@ -677,6 +747,27 @@ Page({ |
|
|
|
console.log('延迟获取历史消息,确保WebSocket连接完全建立'); |
|
|
|
this.fetchHistoryMessages(); |
|
|
|
}, 1500); |
|
|
|
|
|
|
|
// 关键修复:注册全局新消息处理函数,确保在聊天页面时能收到新消息
|
|
|
|
this.originalGlobalMessageHandler = app.globalData.onNewMessage; |
|
|
|
app.globalData.onNewMessage = (newMessage) => { |
|
|
|
console.log('全局新消息处理函数被调用,消息:', newMessage); |
|
|
|
// 检查消息是否与当前聊天相关
|
|
|
|
const isCurrentChatMessage = newMessage.serverData && |
|
|
|
(newMessage.serverData.conversationId === this.data.conversationId || |
|
|
|
newMessage.serverData.senderId === this.data.userId || |
|
|
|
newMessage.serverData.receiverId === this.data.userId); |
|
|
|
|
|
|
|
if (isCurrentChatMessage) { |
|
|
|
// 如果是当前聊天的消息,直接添加到消息列表
|
|
|
|
this.processServerMessage(newMessage.serverData); |
|
|
|
} |
|
|
|
|
|
|
|
// 调用原始处理函数
|
|
|
|
if (this.originalGlobalMessageHandler && typeof this.originalGlobalMessageHandler === 'function') { |
|
|
|
this.originalGlobalMessageHandler(newMessage); |
|
|
|
} |
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
@ -1245,6 +1336,22 @@ Page({ |
|
|
|
const contentType = messageData.contentType || (messageData.data && messageData.data.contentType) || 1; |
|
|
|
const isImage = contentType === 2 || (messageData.fileUrl && contentType !== 1); |
|
|
|
|
|
|
|
// 调试:记录服务器返回的原始时间数据
|
|
|
|
console.log('=== 时间调试 ==='); |
|
|
|
console.log('原始服务器时间:', messageData.createdAt); |
|
|
|
console.log('原始服务器时间类型:', typeof messageData.createdAt); |
|
|
|
|
|
|
|
// 格式化时间,添加详细调试
|
|
|
|
let formattedTime; |
|
|
|
if (messageData.createdAt) { |
|
|
|
console.log('调用formatServerTime处理时间:', messageData.createdAt); |
|
|
|
formattedTime = this.formatServerTime(messageData.createdAt); |
|
|
|
console.log('格式化后的时间:', formattedTime); |
|
|
|
} else { |
|
|
|
formattedTime = this.getFormattedTime(); |
|
|
|
console.log('使用当前时间:', formattedTime); |
|
|
|
} |
|
|
|
|
|
|
|
// 创建符合前端UI要求的消息对象
|
|
|
|
const newMessage = { |
|
|
|
id: messageData.messageId || messageData.id || 'msg_' + Date.now(), |
|
|
|
@ -1254,7 +1361,7 @@ Page({ |
|
|
|
sender: sender, |
|
|
|
senderType: messageData.senderType || 'unknown', // 保存发送方类型
|
|
|
|
direction: messageData.direction || 'unknown', // 保存消息方向
|
|
|
|
time: messageData.createdAt ? this.formatServerTime(messageData.createdAt) : this.getFormattedTime(), |
|
|
|
time: formattedTime, |
|
|
|
status: 'sent', |
|
|
|
serverData: messageData, // 保存完整的服务器数据
|
|
|
|
// 【关键修复】添加真实用户消息标记
|
|
|
|
@ -1264,7 +1371,9 @@ Page({ |
|
|
|
userId: this.data.userId, |
|
|
|
isRealUserId: this.data.isRealUserId, |
|
|
|
matchedUser: isRealUserMessage, |
|
|
|
messageTimestamp: messageData.createdAt || Date.now() |
|
|
|
messageTimestamp: messageData.createdAt || Date.now(), |
|
|
|
rawCreatedAt: messageData.createdAt, |
|
|
|
formattedTime: formattedTime |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
@ -1348,11 +1457,19 @@ Page({ |
|
|
|
// 保存到本地存储
|
|
|
|
this.saveMessagesToStorage(messages); |
|
|
|
|
|
|
|
// 更新页面数据
|
|
|
|
this.setData({ messages }); |
|
|
|
|
|
|
|
// 滚动到底部
|
|
|
|
this.scrollToBottom(); |
|
|
|
// 更新页面数据,确保滚动在数据更新后执行
|
|
|
|
this.setData({ messages }, () => { |
|
|
|
// 根据当前滚动位置决定是否自动滚动到底部
|
|
|
|
if (this.data.isNearBottom) { |
|
|
|
// 如果接近底部,自动滚动到底部
|
|
|
|
this.scrollToBottom(); |
|
|
|
} else { |
|
|
|
// 如果不在底部,显示"到达最底部"按钮
|
|
|
|
this.setData({ |
|
|
|
showScrollToBottomBtn: true |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 如果是新消息,触发通知更新消息列表
|
|
|
|
if (newMessage.sender === 'other') { |
|
|
|
@ -1638,35 +1755,103 @@ Page({ |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 格式化服务器时间
|
|
|
|
// 格式化服务器时间 - 统一使用北京时间
|
|
|
|
formatServerTime: function(serverTime) { |
|
|
|
const date = new Date(serverTime); |
|
|
|
console.log('=== formatServerTime 调试 ==='); |
|
|
|
console.log('输入serverTime:', serverTime); |
|
|
|
console.log('输入类型:', typeof serverTime); |
|
|
|
|
|
|
|
// 处理不同类型的时间输入
|
|
|
|
let date; |
|
|
|
if (typeof serverTime === 'string' && serverTime.includes('T')) { |
|
|
|
// ISO 8601 格式的时间字符串,如 "2025-12-17T14:48:00.000Z"
|
|
|
|
console.log('ISO 8601格式时间字符串'); |
|
|
|
|
|
|
|
// 解析ISO字符串为Date对象
|
|
|
|
const utcDate = new Date(serverTime); |
|
|
|
console.log('UTC Date对象:', utcDate); |
|
|
|
|
|
|
|
// 转换为北京时间(UTC+8)
|
|
|
|
const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); |
|
|
|
console.log('转换为北京时间:', beijingTime); |
|
|
|
date = beijingTime; |
|
|
|
console.log('Date对象时间戳:', date.getTime()); |
|
|
|
} else if (typeof serverTime === 'number') { |
|
|
|
// 处理时间戳
|
|
|
|
console.log('数字时间戳'); |
|
|
|
let adjustedTimestamp = serverTime; |
|
|
|
console.log('原始时间戳:', adjustedTimestamp); |
|
|
|
// 检查是否是秒级时间戳(小于1e12,毫秒级时间戳通常大于1e12)
|
|
|
|
if (adjustedTimestamp < 1e12) { |
|
|
|
console.log('秒级时间戳,转换为毫秒级'); |
|
|
|
adjustedTimestamp *= 1000; |
|
|
|
console.log('转换后时间戳:', adjustedTimestamp); |
|
|
|
} else { |
|
|
|
console.log('已为毫秒级时间戳'); |
|
|
|
} |
|
|
|
|
|
|
|
// 假设时间戳是UTC时间,转换为北京时间
|
|
|
|
const utcDate = new Date(adjustedTimestamp); |
|
|
|
const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); |
|
|
|
date = beijingTime; |
|
|
|
console.log('转换为北京时间对象:', date); |
|
|
|
} else { |
|
|
|
console.log('其他格式,使用当前时间'); |
|
|
|
// 其他格式,直接使用当前北京时间
|
|
|
|
date = new Date(); |
|
|
|
console.log('当前时间Date对象:', date); |
|
|
|
} |
|
|
|
|
|
|
|
// 获取北京时间的各个组成部分
|
|
|
|
const year = date.getFullYear(); |
|
|
|
const month = date.getMonth() + 1; |
|
|
|
const day = date.getDate(); |
|
|
|
const hours = date.getHours().toString().padStart(2, '0'); |
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0'); |
|
|
|
return `${month}-${day} ${hours}:${minutes}`; |
|
|
|
const seconds = date.getSeconds().toString().padStart(2, '0'); |
|
|
|
|
|
|
|
const formattedTime = `${month}-${day} ${hours}:${minutes}`; |
|
|
|
console.log('格式化后的完整时间:', `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`); |
|
|
|
console.log('返回的格式化时间:', formattedTime); |
|
|
|
|
|
|
|
return formattedTime; |
|
|
|
}, |
|
|
|
|
|
|
|
// 解析消息时间字符串为时间戳
|
|
|
|
// 解析消息时间字符串为时间戳 - 统一使用北京时间
|
|
|
|
parseMessageTime: function(timeStr) { |
|
|
|
if (!timeStr) return Date.now(); |
|
|
|
if (!timeStr) return Date.now(); // 返回当前时间戳
|
|
|
|
|
|
|
|
// 处理iOS不支持的"月-日 时:分"格式
|
|
|
|
console.log('=== parseMessageTime 调试 ==='); |
|
|
|
console.log('输入时间字符串:', timeStr); |
|
|
|
|
|
|
|
// 处理"月-日 时:分"格式
|
|
|
|
const dateTimeMatch = timeStr.match(/^(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{2})$/); |
|
|
|
if (dateTimeMatch) { |
|
|
|
console.log('匹配到月-日 时:分格式'); |
|
|
|
const month = dateTimeMatch[1].padStart(2, '0'); |
|
|
|
const day = dateTimeMatch[2].padStart(2, '0'); |
|
|
|
const hours = dateTimeMatch[3].padStart(2, '0'); |
|
|
|
const minutes = dateTimeMatch[4]; |
|
|
|
const year = new Date().getFullYear(); // 使用当前年份
|
|
|
|
// 转换为iOS支持的格式
|
|
|
|
const iosCompatibleDate = `${year}-${month}-${day}T${hours}:${minutes}:00`; |
|
|
|
return new Date(iosCompatibleDate).getTime(); |
|
|
|
|
|
|
|
console.log('解析结果:', { year, month, day, hours, minutes }); |
|
|
|
|
|
|
|
// 直接构建Date对象,使用北京时间(不添加T和Z,避免时区问题)
|
|
|
|
const date = new Date(year, month - 1, day, hours, minutes, 0); |
|
|
|
const timestamp = date.getTime(); |
|
|
|
|
|
|
|
console.log('转换为Date对象:', date); |
|
|
|
console.log('返回时间戳:', timestamp); |
|
|
|
|
|
|
|
return timestamp; |
|
|
|
} |
|
|
|
|
|
|
|
// 对于其他格式,尝试直接解析
|
|
|
|
return new Date(timeStr).getTime(); |
|
|
|
const date = new Date(timeStr); |
|
|
|
console.log('直接解析为Date对象:', date); |
|
|
|
console.log('返回时间戳:', date.getTime()); |
|
|
|
|
|
|
|
return date.getTime(); |
|
|
|
}, |
|
|
|
|
|
|
|
// 提取短时间格式 (HH:MM)
|
|
|
|
@ -1683,15 +1868,59 @@ Page({ |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 将时间戳格式化为时间字符串 |
|
|
|
* 将时间戳格式化为时间字符串 - 统一使用北京时间 |
|
|
|
*/ |
|
|
|
formatTimestampToTime: function(timestamp) { |
|
|
|
const date = new Date(timestamp); |
|
|
|
console.log('=== formatTimestampToTime 调试 ==='); |
|
|
|
console.log('输入timestamp:', timestamp); |
|
|
|
console.log('输入类型:', typeof timestamp); |
|
|
|
|
|
|
|
// 处理不同类型的时间输入
|
|
|
|
let date; |
|
|
|
if (typeof timestamp === 'string' && timestamp.includes('T')) { |
|
|
|
// ISO 8601 格式的时间字符串,如 "2025-12-17T14:48:00.000Z"
|
|
|
|
console.log('ISO 8601格式时间字符串'); |
|
|
|
|
|
|
|
// 解析ISO字符串为Date对象
|
|
|
|
const utcDate = new Date(timestamp); |
|
|
|
console.log('UTC Date对象:', utcDate); |
|
|
|
|
|
|
|
// 转换为北京时间(UTC+8)
|
|
|
|
const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); |
|
|
|
console.log('转换为北京时间:', beijingTime); |
|
|
|
date = beijingTime; |
|
|
|
} else if (typeof timestamp === 'number') { |
|
|
|
// 处理时间戳
|
|
|
|
console.log('数字时间戳'); |
|
|
|
let adjustedTimestamp = timestamp; |
|
|
|
// 检查是否是秒级时间戳(小于1e12,毫秒级时间戳通常大于1e12)
|
|
|
|
if (adjustedTimestamp < 1e12) { |
|
|
|
// 秒级时间戳转换为毫秒级
|
|
|
|
adjustedTimestamp *= 1000; |
|
|
|
console.log('转换后时间戳:', adjustedTimestamp); |
|
|
|
} |
|
|
|
|
|
|
|
// 假设时间戳是UTC时间,转换为北京时间
|
|
|
|
const utcDate = new Date(adjustedTimestamp); |
|
|
|
const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); |
|
|
|
date = beijingTime; |
|
|
|
console.log('转换为北京时间对象:', date); |
|
|
|
} else { |
|
|
|
// 其他格式,直接使用当前北京时间
|
|
|
|
date = new Date(); |
|
|
|
console.log('其他格式,使用当前时间:', date); |
|
|
|
} |
|
|
|
|
|
|
|
// 获取北京时间的各个组成部分
|
|
|
|
const month = date.getMonth() + 1; |
|
|
|
const day = date.getDate(); |
|
|
|
const hours = date.getHours().toString().padStart(2, '0'); |
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0'); |
|
|
|
return `${month}-${day} ${hours}:${minutes}`; |
|
|
|
|
|
|
|
const formattedTime = `${month}-${day} ${hours}:${minutes}`; |
|
|
|
console.log('返回的格式化时间:', formattedTime); |
|
|
|
|
|
|
|
return formattedTime; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
@ -1920,51 +2149,56 @@ Page({ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 创建消息对象
|
|
|
|
// 调试:记录发送消息时的时间
|
|
|
|
console.log('=== 发送消息时间调试 ==='); |
|
|
|
console.log('当前时间(Date对象):', new Date()); |
|
|
|
|
|
|
|
// 获取当前北京时间,确保时间准确
|
|
|
|
const currentTime = this.getFormattedTime(); |
|
|
|
const shortTime = this.getShortTime(); |
|
|
|
|
|
|
|
console.log('getFormattedTime返回:', currentTime); |
|
|
|
console.log('getShortTime返回:', shortTime); |
|
|
|
|
|
|
|
// 创建消息对象,立即设置正确的时间
|
|
|
|
const newMessage = { |
|
|
|
type: 'message', |
|
|
|
sender: 'me', |
|
|
|
content: this.data.inputValue.trim(), |
|
|
|
time: this.getFormattedTime(), |
|
|
|
time: currentTime, |
|
|
|
shortTime: shortTime, |
|
|
|
showTime: this.shouldShowTime(), |
|
|
|
status: 'sending', // 初始状态为发送中
|
|
|
|
// 【关键修复】添加真实用户ID相关字段
|
|
|
|
targetUserId: this.data.userId |
|
|
|
targetUserId: this.data.userId, |
|
|
|
// 调试:添加发送时间详情
|
|
|
|
sendDebugInfo: { |
|
|
|
rawDate: new Date(), |
|
|
|
rawTimestamp: Date.now(), |
|
|
|
formattedTime: currentTime |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
console.log('开始发送消息,目标用户ID:', this.data.userId, '会话ID:', this.data.conversationId); |
|
|
|
|
|
|
|
// 添加新消息到消息列表
|
|
|
|
const messages = [...this.data.messages]; |
|
|
|
|
|
|
|
// 设置是否显示新消息的时间
|
|
|
|
let showTime = false; |
|
|
|
if (messages.length === 0) { |
|
|
|
showTime = true; |
|
|
|
} else { |
|
|
|
const lastMessage = messages[messages.length - 1]; |
|
|
|
const currentTime = this.parseMessageTime(this.getFormattedTime()); |
|
|
|
const lastTime = this.parseMessageTime(lastMessage.time); |
|
|
|
|
|
|
|
// 如果时间差超过5分钟,显示时间
|
|
|
|
if (currentTime - lastTime > 5 * 60 * 1000) { |
|
|
|
showTime = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
newMessage.showTime = showTime; |
|
|
|
newMessage.shortTime = this.getShortTime(); |
|
|
|
messages.push(newMessage); |
|
|
|
|
|
|
|
// 立即更新UI
|
|
|
|
// 重新处理所有消息的时间显示,确保时间显示正确
|
|
|
|
const processedMessages = this.processMessageTimes(messages); |
|
|
|
|
|
|
|
// 立即更新UI,确保滚动在数据更新后执行
|
|
|
|
this.setData({ |
|
|
|
messages: messages, |
|
|
|
messages: processedMessages, |
|
|
|
inputValue: '' |
|
|
|
}, () => { |
|
|
|
// 数据更新完成后滚动到底部
|
|
|
|
this.scrollToBottom(); |
|
|
|
}); |
|
|
|
|
|
|
|
// 保存消息到本地存储
|
|
|
|
this.saveMessagesToStorage(messages); |
|
|
|
|
|
|
|
this.scrollToBottom(); |
|
|
|
this.saveMessagesToStorage(processedMessages); |
|
|
|
|
|
|
|
// 会话ID检查和处理
|
|
|
|
if (!this.data.conversationId || this.data.conversationId.startsWith('temp_')) { |
|
|
|
@ -2427,18 +2661,31 @@ Page({ |
|
|
|
}, 1000); |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取格式化时间(带日期)
|
|
|
|
// 获取格式化时间(带日期)- 统一使用北京时间
|
|
|
|
getFormattedTime: function() { |
|
|
|
// 调试:记录当前时间
|
|
|
|
console.log('=== getFormattedTime 调试 ==='); |
|
|
|
console.log('当前Date对象:', new Date()); |
|
|
|
|
|
|
|
// 获取当前本地时间(设备的本地时间已经是北京时间)
|
|
|
|
const now = new Date(); |
|
|
|
const year = now.getFullYear(); |
|
|
|
const month = now.getMonth() + 1; |
|
|
|
const date = now.getDate(); |
|
|
|
const hours = now.getHours().toString().padStart(2, '0'); |
|
|
|
const minutes = now.getMinutes().toString().padStart(2, '0'); |
|
|
|
return `${month}-${date} ${hours}:${minutes}`; |
|
|
|
const seconds = now.getSeconds().toString().padStart(2, '0'); |
|
|
|
|
|
|
|
const formattedTime = `${month}-${date} ${hours}:${minutes}`; |
|
|
|
console.log('格式化后时间:', formattedTime); |
|
|
|
console.log('完整时间:', `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`); |
|
|
|
|
|
|
|
return formattedTime; |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取仅包含时分的时间格式
|
|
|
|
// 获取仅包含时分的时间格式 - 统一使用北京时间
|
|
|
|
getShortTime: function() { |
|
|
|
// 获取当前本地时间(设备的本地时间已经是北京时间)
|
|
|
|
const now = new Date(); |
|
|
|
const hours = now.getHours().toString().padStart(2, '0'); |
|
|
|
const minutes = now.getMinutes().toString().padStart(2, '0'); |
|
|
|
@ -2529,18 +2776,12 @@ Page({ |
|
|
|
// 滚动到底部
|
|
|
|
scrollToBottom: function() { |
|
|
|
setTimeout(() => { |
|
|
|
wx.createSelectorQuery().select('#message-container').boundingClientRect(res => { |
|
|
|
if (res) { |
|
|
|
wx.createSelectorQuery().select('#message-list').scrollOffset(res => { |
|
|
|
if (res) { |
|
|
|
wx.createSelectorQuery().select('#message-list').context(context => { |
|
|
|
context.scrollTo({ scrollTop: res.scrollHeight, animated: true }); |
|
|
|
}).exec(); |
|
|
|
} |
|
|
|
}).exec(); |
|
|
|
} |
|
|
|
}).exec(); |
|
|
|
}, 100); |
|
|
|
// 使用scroll-top属性滚动到底部,这是更可靠的方式
|
|
|
|
this.setData({ |
|
|
|
scrollTop: 999999, |
|
|
|
showScrollToBottomBtn: false |
|
|
|
}); |
|
|
|
}, 50); |
|
|
|
}, |
|
|
|
|
|
|
|
// 显示表情
|
|
|
|
@ -2653,7 +2894,55 @@ Page({ |
|
|
|
* 生命周期函数--监听页面显示 |
|
|
|
*/ |
|
|
|
onShow: function () { |
|
|
|
// 页面显示时,初始化滚动区域高度
|
|
|
|
this.initScrollHeight(); |
|
|
|
// 页面显示时,滚动到最新消息
|
|
|
|
console.log('聊天页面显示,滚动到最新消息'); |
|
|
|
// 只在页面首次显示时滚动到底部,避免频繁跳转
|
|
|
|
if (!this.hasScrolledOnShow) { |
|
|
|
this.scrollToBottom(); |
|
|
|
this.hasScrolledOnShow = true; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 初始化滚动区域高度 |
|
|
|
*/ |
|
|
|
initScrollHeight: function() { |
|
|
|
wx.createSelectorQuery().select('#message-list').boundingClientRect(res => { |
|
|
|
if (res) { |
|
|
|
this.setData({ |
|
|
|
scrollHeight: res.height |
|
|
|
}); |
|
|
|
} |
|
|
|
}).exec(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 滚动事件处理函数 |
|
|
|
*/ |
|
|
|
onScroll: function(e) { |
|
|
|
const { scrollTop, scrollHeight, contentHeight } = e.detail; |
|
|
|
|
|
|
|
// 更新滚动位置
|
|
|
|
this.setData({ |
|
|
|
lastScrollTop: scrollTop, |
|
|
|
contentHeight: contentHeight |
|
|
|
}); |
|
|
|
|
|
|
|
// 计算是否接近底部(距离底部50px以内)
|
|
|
|
const distanceToBottom = contentHeight - scrollTop - scrollHeight; |
|
|
|
const isNearBottom = distanceToBottom < 50 && distanceToBottom > -100; |
|
|
|
this.setData({ |
|
|
|
isNearBottom: isNearBottom |
|
|
|
}); |
|
|
|
|
|
|
|
// 如果接近底部,隐藏"到达最底部"按钮
|
|
|
|
if (isNearBottom) { |
|
|
|
this.setData({ |
|
|
|
showScrollToBottomBtn: false |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
@ -2736,6 +3025,13 @@ Page({ |
|
|
|
console.log('稳定性检查定时器已清理'); |
|
|
|
} |
|
|
|
|
|
|
|
// 关键修复:恢复原始的全局消息处理函数
|
|
|
|
const app = getApp(); |
|
|
|
if (this.originalGlobalMessageHandler) { |
|
|
|
app.globalData.onNewMessage = this.originalGlobalMessageHandler; |
|
|
|
console.log('已恢复原始的全局消息处理函数'); |
|
|
|
} |
|
|
|
|
|
|
|
// 关闭WebSocket连接
|
|
|
|
// 注意:这里可以根据实际需求决定是否关闭连接
|
|
|
|
// 如果是多页面共用一个连接,可以不在这里关闭
|
|
|
|
@ -3274,8 +3570,15 @@ Page({ |
|
|
|
}, () => { |
|
|
|
// 数据更新完成后的回调
|
|
|
|
console.log('消息列表已成功更新到UI'); |
|
|
|
// 滚动到底部
|
|
|
|
this.scrollToBottom(); |
|
|
|
|
|
|
|
// 关键修复:只在页面首次加载或用户接近底部时才滚动到底部,避免频繁跳转
|
|
|
|
// 如果是首次加载历史消息,或者用户当前已经接近底部,才自动滚动到底部
|
|
|
|
if (this.data.isNearBottom || this.data.messages.length === 0) { |
|
|
|
console.log('首次加载历史消息或用户接近底部,自动滚动到底部'); |
|
|
|
this.scrollToBottom(); |
|
|
|
} else { |
|
|
|
console.log('用户不在底部,不自动滚动,保持当前位置'); |
|
|
|
} |
|
|
|
|
|
|
|
// 【关键修复】根据消息情况显示不同的提示
|
|
|
|
const realUserMessages = processedMessages.filter(msg => msg.isRealUserMessage); |
|
|
|
@ -3549,11 +3852,12 @@ Page({ |
|
|
|
title: '加载更多消息...', |
|
|
|
}); |
|
|
|
|
|
|
|
// 简单实现,实际可以添加分页逻辑
|
|
|
|
// 实现真正的分页加载,根据已有消息数量计算页码
|
|
|
|
const page = Math.floor(this.data.messages.length / 50) + 1; |
|
|
|
socketManager.send({ |
|
|
|
type: 'get_messages', |
|
|
|
conversationId: conversationId, |
|
|
|
page: 1, // 实际应用中应该根据已有消息数量计算页码
|
|
|
|
page: page, // 根据已有消息数量计算页码
|
|
|
|
limit: 50 |
|
|
|
}); |
|
|
|
}, |
|
|
|
|