You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
14 KiB
474 lines
14 KiB
|
3 months ago
|
// pages/chat/index.js
|
||
|
|
import socketManager from '../../utils/websocket.js';
|
||
|
|
|
||
|
|
Page({
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 页面的初始数据
|
||
|
|
*/
|
||
|
|
data: {
|
||
|
|
messages: [], // 初始为空数组,后续通过数据获取和WebSocket更新
|
||
|
|
activeTab: 'all',
|
||
|
|
webSocketUrl: '' // WebSocket服务器地址
|
||
|
|
},
|
||
|
|
|
||
|
|
// WebSocket消息处理函数
|
||
|
|
handleWebSocketMessage: function(message) {
|
||
|
|
console.log('聊天列表页面接收到消息:', message);
|
||
|
|
|
||
|
|
// 判断是否为新消息 - 支持'new_message'类型(服务器实际发送的类型)
|
||
|
|
if (message.type === 'new_message') {
|
||
|
|
const newMessage = message.payload;
|
||
|
|
this.updateMessageList(newMessage);
|
||
|
|
} else if (message.type === 'chat_message') {
|
||
|
|
// 保留原有的'chat_message'类型支持,兼容不同的消息格式
|
||
|
|
const newMessage = message.data;
|
||
|
|
this.updateMessageList(newMessage);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 更新消息列表
|
||
|
|
updateMessageList: function(newMessage) {
|
||
|
|
console.log('更新消息列表:', newMessage);
|
||
|
|
const messages = [...this.data.messages];
|
||
|
|
|
||
|
|
// 确定消息发送者ID - 处理服务器返回的数据格式
|
||
|
|
const senderId = newMessage.senderId;
|
||
|
|
const existingIndex = messages.findIndex(item => item.id === senderId);
|
||
|
|
|
||
|
|
// 格式化消息时间 - 服务器使用createdAt字段
|
||
|
|
const now = new Date();
|
||
|
|
const messageDate = new Date(newMessage.createdAt || newMessage.timestamp || Date.now());
|
||
|
|
let displayTime = '';
|
||
|
|
|
||
|
|
if (messageDate.toDateString() === now.toDateString()) {
|
||
|
|
// 今天的消息只显示时间
|
||
|
|
displayTime = messageDate.getHours().toString().padStart(2, '0') + ':' +
|
||
|
|
messageDate.getMinutes().toString().padStart(2, '0');
|
||
|
|
} else if (messageDate.toDateString() === new Date(now.getTime() - 86400000).toDateString()) {
|
||
|
|
// 昨天的消息
|
||
|
|
displayTime = '昨天';
|
||
|
|
} else {
|
||
|
|
// 其他日期显示月日
|
||
|
|
displayTime = (messageDate.getMonth() + 1) + '月' + messageDate.getDate() + '日';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取当前用户ID,确定消息方向
|
||
|
|
const app = getApp();
|
||
|
|
const currentUserId = app.globalData.userInfo?.userId || wx.getStorageSync('userId') || 'unknown';
|
||
|
|
|
||
|
|
// 确定显示的用户ID(如果是自己发送的消息,显示接收者ID)
|
||
|
|
const displayUserId = senderId === currentUserId ? newMessage.receiverId : senderId;
|
||
|
|
|
||
|
|
if (existingIndex >= 0) {
|
||
|
|
// 存在该用户的消息,更新内容和时间
|
||
|
|
messages[existingIndex] = {
|
||
|
|
...messages[existingIndex],
|
||
|
|
content: newMessage.content || '',
|
||
|
|
time: displayTime,
|
||
|
|
isRead: false
|
||
|
|
};
|
||
|
|
// 将更新的消息移到列表顶部
|
||
|
|
const [updatedMessage] = messages.splice(existingIndex, 1);
|
||
|
|
messages.unshift(updatedMessage);
|
||
|
|
} else {
|
||
|
|
// 新用户消息,添加到列表顶部
|
||
|
|
// 这里暂时使用ID作为用户名,实际应用中应该从用户信息中获取
|
||
|
|
const displayName = `用户${displayUserId}`;
|
||
|
|
messages.unshift({
|
||
|
|
id: displayUserId,
|
||
|
|
name: displayName,
|
||
|
|
avatar: displayName.charAt(0),
|
||
|
|
content: newMessage.content || '',
|
||
|
|
time: displayTime,
|
||
|
|
isRead: false
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// 使用setData更新视图
|
||
|
|
this.setData({ messages });
|
||
|
|
|
||
|
|
// 触发消息提示振动(可选)
|
||
|
|
wx.vibrateShort();
|
||
|
|
|
||
|
|
// 更新TabBar未读消息数(如果需要)
|
||
|
|
this.updateTabBarBadge();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 更新TabBar未读消息提示 - 使用自定义TabBar兼容方式
|
||
|
|
updateTabBarBadge: function() {
|
||
|
|
console.log('更新TabBar未读提示,当前消息数:', this.data.messages.length);
|
||
|
|
// 检查是否有未读消息
|
||
|
|
const hasUnread = this.data.messages.some(msg => !msg.isRead);
|
||
|
|
console.log('是否有未读消息:', hasUnread);
|
||
|
|
|
||
|
|
// 对于自定义TabBar,使用全局状态来管理未读标记
|
||
|
|
const app = getApp();
|
||
|
|
if (app && app.globalData) {
|
||
|
|
app.globalData.tabBarBadge = {
|
||
|
|
chat: hasUnread ? ' ' : ''
|
||
|
|
};
|
||
|
|
console.log('已更新全局TabBar未读标记状态:', hasUnread ? '显示' : '隐藏');
|
||
|
|
|
||
|
|
// 尝试通过getTabBar方法通知自定义TabBar更新
|
||
|
|
try {
|
||
|
|
const tabBar = this.getTabBar();
|
||
|
|
if (tabBar) {
|
||
|
|
tabBar.setData({
|
||
|
|
selected: 'buyer', // 假设聊天页是buyer tab
|
||
|
|
badge: hasUnread ? ' ' : ''
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.log('TabBar更新失败,将在下一次页面显示时自动更新');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 清理所有未读消息状态
|
||
|
|
clearAllUnreadStatus: function() {
|
||
|
|
console.log('清理所有未读消息状态');
|
||
|
|
try {
|
||
|
|
// 1. 更新本地消息列表中的未读状态
|
||
|
|
const updatedMessages = this.data.messages.map(msg => ({
|
||
|
|
...msg,
|
||
|
|
isRead: true
|
||
|
|
}));
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
messages: updatedMessages
|
||
|
|
});
|
||
|
|
|
||
|
|
// 2. 对于自定义TabBar,使用全局状态来管理未读标记
|
||
|
|
const app = getApp();
|
||
|
|
if (app && app.globalData) {
|
||
|
|
app.globalData.tabBarBadge = {
|
||
|
|
chat: ''
|
||
|
|
};
|
||
|
|
console.log('已清理全局TabBar未读标记');
|
||
|
|
|
||
|
|
// 尝试通过getTabBar方法通知自定义TabBar更新
|
||
|
|
try {
|
||
|
|
const tabBar = this.getTabBar();
|
||
|
|
if (tabBar) {
|
||
|
|
tabBar.setData({
|
||
|
|
selected: 'buyer', // 假设聊天页是buyer tab
|
||
|
|
badge: ''
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.log('TabBar更新失败,将在下一次页面显示时自动更新');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. 显示成功提示
|
||
|
|
wx.showToast({
|
||
|
|
title: '已清除所有未读提示',
|
||
|
|
icon: 'success',
|
||
|
|
duration: 2000
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('清理未读状态失败:', error);
|
||
|
|
wx.showToast({
|
||
|
|
title: '清理失败',
|
||
|
|
icon: 'none',
|
||
|
|
duration: 2000
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 初始化WebSocket连接
|
||
|
|
initWebSocket: function() {
|
||
|
|
try {
|
||
|
|
const app = getApp();
|
||
|
|
|
||
|
|
// 使用正确的WebSocket服务器地址 - 开发环境通常是ws://localhost:3003
|
||
|
|
// 动态构建WebSocket URL,基于全局配置或环境
|
||
|
|
let wsProtocol = 'ws://';
|
||
|
|
let wsHost = app.globalData.webSocketUrl || 'localhost:3003';
|
||
|
|
let wsUrl;
|
||
|
|
|
||
|
|
// 如果wsHost已经包含协议,直接使用
|
||
|
|
if (wsHost.startsWith('ws://') || wsHost.startsWith('wss://')) {
|
||
|
|
wsUrl = wsHost;
|
||
|
|
} else {
|
||
|
|
// 否则添加协议前缀
|
||
|
|
wsUrl = `${wsProtocol}${wsHost}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.setData({ webSocketUrl: wsUrl });
|
||
|
|
|
||
|
|
console.log('WebSocket连接初始化,使用地址:', wsUrl);
|
||
|
|
|
||
|
|
// 连接WebSocket
|
||
|
|
socketManager.connect(wsUrl);
|
||
|
|
|
||
|
|
// 添加消息监听
|
||
|
|
socketManager.on('message', this.handleWebSocketMessage.bind(this));
|
||
|
|
|
||
|
|
// 添加状态监听,以便调试
|
||
|
|
socketManager.on('status', (status) => {
|
||
|
|
console.log('WebSocket状态更新:', status);
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('聊天列表页面WebSocket已初始化');
|
||
|
|
} catch (error) {
|
||
|
|
console.error('初始化WebSocket失败:', error);
|
||
|
|
wx.showToast({
|
||
|
|
title: 'WebSocket初始化失败',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 清理WebSocket连接
|
||
|
|
cleanupWebSocket: function() {
|
||
|
|
socketManager.off('message', this.handleWebSocketMessage);
|
||
|
|
},
|
||
|
|
|
||
|
|
// 加载聊天列表数据
|
||
|
|
loadChatList: function() {
|
||
|
|
wx.showLoading({ title: '加载中' });
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 从服务器获取真实的聊天列表数据
|
||
|
|
const app = getApp();
|
||
|
|
const token = app.globalData.token || wx.getStorageSync('token');
|
||
|
|
|
||
|
|
// 使用正确的API配置,兼容开发环境
|
||
|
|
const baseUrl = app.globalData.baseUrl || 'http://localhost:3003';
|
||
|
|
const currentUserId = app.globalData.userInfo?.userId || wx.getStorageSync('userId') || 'unknown';
|
||
|
|
console.log('使用API地址:', baseUrl);
|
||
|
|
console.log('当前用户ID:', currentUserId);
|
||
|
|
|
||
|
|
// 使用正确的API端点 - /api/conversations/user/:userId
|
||
|
|
wx.request({
|
||
|
|
url: `${baseUrl}/api/conversations/user/${currentUserId}`,
|
||
|
|
method: 'GET',
|
||
|
|
header: {
|
||
|
|
'Authorization': token ? `Bearer ${token}` : '',
|
||
|
|
'content-type': 'application/json'
|
||
|
|
},
|
||
|
|
success: (res) => {
|
||
|
|
console.log('获取聊天列表成功:', res.data);
|
||
|
|
// 处理不同的API响应格式
|
||
|
|
let chatData = [];
|
||
|
|
|
||
|
|
if (res.data.code === 0 && res.data.data) {
|
||
|
|
chatData = res.data.data;
|
||
|
|
} else if (Array.isArray(res.data)) {
|
||
|
|
// 如果直接返回数组
|
||
|
|
chatData = res.data;
|
||
|
|
} else if (res.data) {
|
||
|
|
// 如果返回的是对象但不是标准格式,尝试直接使用
|
||
|
|
chatData = [res.data];
|
||
|
|
}
|
||
|
|
|
||
|
|
if (chatData.length > 0) {
|
||
|
|
// 格式化聊天列表数据
|
||
|
|
const formattedMessages = chatData.map(item => {
|
||
|
|
// 格式化时间
|
||
|
|
const now = new Date();
|
||
|
|
const messageDate = new Date(item.lastMessageTime || item.createdAt || Date.now());
|
||
|
|
let displayTime = '';
|
||
|
|
|
||
|
|
if (messageDate.toDateString() === now.toDateString()) {
|
||
|
|
// 今天的消息只显示时间
|
||
|
|
displayTime = messageDate.getHours().toString().padStart(2, '0') + ':' +
|
||
|
|
messageDate.getMinutes().toString().padStart(2, '0');
|
||
|
|
} else if (messageDate.toDateString() === new Date(now.getTime() - 86400000).toDateString()) {
|
||
|
|
// 昨天的消息
|
||
|
|
displayTime = '昨天';
|
||
|
|
} else {
|
||
|
|
// 其他日期显示月日
|
||
|
|
displayTime = (messageDate.getMonth() + 1) + '月' + messageDate.getDate() + '日';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取当前用户ID,确定显示哪个用户
|
||
|
|
const displayUserId = item.userId === currentUserId ? item.managerId : item.userId || item.id;
|
||
|
|
const displayName = `用户${displayUserId}`;
|
||
|
|
|
||
|
|
return {
|
||
|
|
id: displayUserId,
|
||
|
|
name: item.userName || item.name || displayName,
|
||
|
|
avatar: item.avatar || (item.userName || displayName).charAt(0) || '用',
|
||
|
|
content: item.lastMessage || item.content || '',
|
||
|
|
time: displayTime,
|
||
|
|
isRead: item.isRead || false,
|
||
|
|
unreadCount: item.unreadCount || 0
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
this.setData({ messages: formattedMessages });
|
||
|
|
} else {
|
||
|
|
console.log('暂无聊天消息');
|
||
|
|
this.setData({ messages: [] });
|
||
|
|
}
|
||
|
|
},
|
||
|
|
fail: (err) => {
|
||
|
|
console.error('网络请求失败:', err);
|
||
|
|
wx.showToast({
|
||
|
|
title: '网络请求失败,使用本地数据',
|
||
|
|
icon: 'none',
|
||
|
|
duration: 3000
|
||
|
|
});
|
||
|
|
|
||
|
|
// 失败时使用模拟数据,确保页面能够正常显示
|
||
|
|
this.setData({
|
||
|
|
messages: [
|
||
|
|
{
|
||
|
|
id: '1',
|
||
|
|
name: '系统消息',
|
||
|
|
avatar: '系',
|
||
|
|
content: '欢迎使用聊天功能',
|
||
|
|
time: '刚刚',
|
||
|
|
isRead: false
|
||
|
|
}
|
||
|
|
]
|
||
|
|
});
|
||
|
|
},
|
||
|
|
complete: () => {
|
||
|
|
wx.hideLoading();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('加载聊天列表异常:', error);
|
||
|
|
wx.hideLoading();
|
||
|
|
wx.showToast({ title: '加载异常', icon: 'none' });
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 返回上一页
|
||
|
|
onBack: function() {
|
||
|
|
wx.navigateBack({
|
||
|
|
delta: 1
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 生命周期函数--监听页面加载
|
||
|
|
*/
|
||
|
|
onLoad(options) {
|
||
|
|
this.loadChatList();
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 生命周期函数--监听页面初次渲染完成
|
||
|
|
*/
|
||
|
|
onReady() {
|
||
|
|
// 设置导航栏右侧按钮的点击事件
|
||
|
|
wx.showNavigationBarLoading();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 导航栏左侧按钮点击事件
|
||
|
|
onNavigationBarButtonTap(e) {
|
||
|
|
if (e.type === 'left') {
|
||
|
|
this.onBack();
|
||
|
|
} else if (e.type === 'right') {
|
||
|
|
// 处理管理按钮点击
|
||
|
|
wx.showToast({
|
||
|
|
title: '管理功能待开发',
|
||
|
|
icon: 'none'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 生命周期函数--监听页面显示
|
||
|
|
*/
|
||
|
|
onShow() {
|
||
|
|
// 页面显示时初始化WebSocket连接
|
||
|
|
this.initWebSocket();
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 生命周期函数--监听页面隐藏
|
||
|
|
*/
|
||
|
|
onHide() {
|
||
|
|
// 页面隐藏时清理WebSocket连接
|
||
|
|
this.cleanupWebSocket();
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 生命周期函数--监听页面卸载
|
||
|
|
*/
|
||
|
|
onUnload() {
|
||
|
|
// 页面卸载时清理WebSocket连接
|
||
|
|
this.cleanupWebSocket();
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 页面相关事件处理函数--监听用户下拉动作
|
||
|
|
*/
|
||
|
|
onPullDownRefresh() {
|
||
|
|
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 页面上拉触底事件的处理函数
|
||
|
|
*/
|
||
|
|
onReachBottom() {
|
||
|
|
|
||
|
|
},
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 用户点击右上角分享
|
||
|
|
*/
|
||
|
|
onShareAppMessage: function () {
|
||
|
|
// 页面分享配置
|
||
|
|
return {
|
||
|
|
title: '聊天列表',
|
||
|
|
path: '/pages/chat/index'
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 跳转到对话详情页面
|
||
|
|
navigateToChatDetail: function(e) {
|
||
|
|
const userId = e.currentTarget.dataset.userId;
|
||
|
|
const userName = e.currentTarget.dataset.userName;
|
||
|
|
|
||
|
|
// 将该聊天标记为已读
|
||
|
|
const messages = [...this.data.messages];
|
||
|
|
const messageIndex = messages.findIndex(item => item.id === userId);
|
||
|
|
|
||
|
|
if (messageIndex >= 0) {
|
||
|
|
messages[messageIndex].isRead = true;
|
||
|
|
messages[messageIndex].unreadCount = 0;
|
||
|
|
this.setData({ messages });
|
||
|
|
|
||
|
|
// 更新TabBar未读消息数
|
||
|
|
this.updateTabBarBadge();
|
||
|
|
|
||
|
|
// 通知服务器已读状态(可选)
|
||
|
|
this.markAsRead(userId);
|
||
|
|
}
|
||
|
|
|
||
|
|
wx.navigateTo({
|
||
|
|
url: `/pages/chat-detail/index?userId=${userId}&userName=${encodeURIComponent(userName)}`
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 通知服务器消息已读
|
||
|
|
markAsRead: function(userId) {
|
||
|
|
const app = getApp();
|
||
|
|
const token = app.globalData.token || wx.getStorageSync('token');
|
||
|
|
|
||
|
|
wx.request({
|
||
|
|
url: `${app.globalData.baseUrl || 'https://your-server.com'}/api/chat/read`,
|
||
|
|
method: 'POST',
|
||
|
|
header: {
|
||
|
|
'Authorization': `Bearer ${token}`,
|
||
|
|
'content-type': 'application/json'
|
||
|
|
},
|
||
|
|
data: {
|
||
|
|
userId: userId
|
||
|
|
},
|
||
|
|
success: (res) => {
|
||
|
|
console.log('标记消息已读成功:', res.data);
|
||
|
|
},
|
||
|
|
fail: (err) => {
|
||
|
|
console.error('标记消息已读失败:', err);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
})
|