findme-miniprogram-frontend/pages/message/message.js

1291 lines
36 KiB
JavaScript
Raw Permalink Normal View History

2025-12-27 17:16:03 +08:00
// 消息页面逻辑 - 完全基于 NIM SDK
const app = getApp();
const apiClient = require('../../utils/api-client.js');
const nimConversationManager = require('../../utils/nim-conversation-manager.js');
const nimPresenceManager = require('../../utils/nim-presence-manager.js');
const { initPageSystemInfo } = require('../../utils/system-info-modern.js');
Page({
data: {
//模拟数据
store:[{
avatar:'../../images/group/testImg.jpg',
nickname:'Jianying Liu',
text:'Heyyy',
tiem:'16:30'
}],
searchKeyword: '',
refreshing: false,
loading: false,
longPressing: false, // 长按标记,防止长按后触发单击
// 会话数据
conversations: [],
pinnedConversations: [],
normalConversations: [],
totalUnreadCount: 0,
filteredConversations: [],
// NIM SDK 状态
nimReady: false,
// 系统适配信息
systemInfo: {},
statusBarHeight: 0,
menuButtonHeight: 0,
menuButtonTop: 0,
navBarHeight: 0,
windowHeight: 0,
safeAreaBottom: 0,
showSearch: false,
// 本地模糊搜索增强
searching: false,
conversationSamples: {},
sampleLoading: {},
lastSearchKeyword: ''
},
// 统一时间戳规范化
normalizeTimestamp(ts) {
if (!ts && ts !== 0) return 0;
if (typeof ts === 'number' && isFinite(ts)) return ts;
if (typeof ts === 'string') {
const s = ts.trim();
if (!s) return 0;
if (/^\d+$/.test(s)) {
const n = parseInt(s, 10);
return s.length === 10 ? n * 1000 : n;
}
const d = Date.parse(s);
return isNaN(d) ? 0 : d;
}
try {
const d = new Date(ts).getTime();
return isNaN(d) ? 0 : d;
} catch (_) {
return 0;
}
},
/**
* 从会话数据中提取真实的用户ID
* @param {Object} conv - 会话对象
* @returns {string} - 用户ID
*/
extractUserIdFromConversation(conv) {
let userId = conv.targetId || conv.userId || '';
// 如果 targetId 是 conversationId 格式(包含|符号),则解析
if (userId && typeof userId === 'string' && userId.includes('|')) {
const parts = userId.split('|');
if (parts.length === 3) {
userId = parts[2]; // 提取目标用户ID
}
}
// 如果还是没有,尝试从 conversationId 解析
if (!userId && conv.conversationId && typeof conv.conversationId === 'string') {
const parts = conv.conversationId.split('|');
if (parts.length === 3) {
userId = parts[2];
}
}
return userId || '';
},
// 页面加载
onLoad: function (options) {
const { store } = this.data;
// 生成10次重复的数组fill+flat 一行实现)
const repeatedArr = Array(20).fill(store).flat();
// 响应式更新:必须用 setData页面才会渲染新数组
this.setData({ repeatedArr });
this.initSystemInfo();
this.checkAuthAndInit();
wx.setNavigationBarTitle({ title: '消息' });
},
// 检查认证状态并初始化
async checkAuthAndInit() {
try {
const currentToken = apiClient.getToken();
const app = getApp();
const isLoggedIn = app?.globalData?.isLoggedIn || false;
console.log('检查认证状态:', { hasToken: !!currentToken, isLoggedIn });
if (!currentToken || !isLoggedIn) {
console.warn('⚠️ 用户未登录,跳转到登录页');
this.redirectToLogin('checkAuthAndInit');
return;
}
// 初始化 NIM 模式
await this.initNIMMode();
} catch (error) {
console.error('认证检查失败:', error);
wx.showToast({
title: `NIM初始化失败:${error}`,
icon: 'none'
});
}
},
// 统一的登录页跳转方法
redirectToLogin(source) {
if (this.redirecting) {
console.log('已在跳转中,跳过');
return;
}
this.redirecting = true;
console.log(`🔄 来自${source}的跳转请求,执行跳转...`);
setTimeout(() => {
wx.reLaunch({
url: '/pages/login/login',
success: () => {
console.log('✅ 跳转登录页成功');
this.redirecting = false;
},
fail: (err) => {
console.error('❌ 跳转登录页失败:', err);
this.redirecting = false;
}
});
}, 1000);
},
// 初始化系统信息
initSystemInfo() {
const pageSystemInfo = initPageSystemInfo();
this.setData({
systemInfo: pageSystemInfo.systemInfo,
statusBarHeight: pageSystemInfo.statusBarHeight,
menuButtonHeight: pageSystemInfo.menuButtonHeight,
menuButtonTop: pageSystemInfo.menuButtonTop,
navBarHeight: pageSystemInfo.navBarHeight,
windowHeight: pageSystemInfo.windowHeight,
safeAreaBottom: pageSystemInfo.safeAreaBottom
});
},
// 初始化 NIM 模式
initNIMMode() {
try {
console.log('📡 初始化 NIM 模式');
const app = getApp();
if (!app.globalData.nim) {
console.error('❌ NIM 实例未初始化');
wx.showToast({
title: 'NIM未初始化',
icon: 'none'
});
return;
}
// 注册 NIM 事件监听器
nimConversationManager.on('conversation_created', this.handleNIMConversationCreated.bind(this));
nimConversationManager.on('conversation_deleted', this.handleNIMConversationDeleted.bind(this));
nimConversationManager.on('conversation_changed', this.handleNIMConversationChanged.bind(this));
nimConversationManager.on('new_message', this.handleNIMNewMessages.bind(this));
nimConversationManager.on('message_revoked', this.handleNIMMessageRevoked.bind(this));
// 🔥 注册在线状态变化监听
nimPresenceManager.on('presence_changed', this.handlePresenceChanged.bind(this));
this.setData({ nimReady: true });
// 加载会话列表
this.loadConversations();
console.log('✅ NIM 模式初始化完成');
} catch (error) {
console.error('❌ 初始化 NIM 模式失败:', error);
wx.showToast({
title: '初始化 NIM 模式失败',
icon: 'none'
});
}
},
// 🔥 检查并重连 NIM页面级别
async checkAndReconnectNim() {
try {
const app = getApp();
// 如果 NIM 未就绪,尝试初始化
if (!this.data.nimReady) {
console.log('⚠️ 页面 NIM 未就绪,尝试初始化');
await this.checkAuthAndInit();
return;
}
// 触发全局 NIM 重连检查
if (app && typeof app.checkAndReconnectNim === 'function') {
await app.checkAndReconnectNim();
}
// 重新初始化会话管理器(确保事件监听器有效)
if (app.globalData.nim) {
const nim = app.globalData.nim;
// 检查会话管理器是否已初始化
if (!nimConversationManager.getInitStatus()) {
console.log('🔄 重新初始化会话管理器');
nimConversationManager.init(nim);
}
// 检查在线状态管理器是否已初始化
if (!nimPresenceManager.getInitStatus()) {
console.log('🔄 重新初始化在线状态管理器');
nimPresenceManager.init(nim);
}
}
} catch (error) {
console.error('❌ 页面级 NIM 重连检查失败:', error);
}
},
// ===== NIM 事件处理方法 =====
// 处理 NIM 会话新增
handleNIMConversationCreated(conversation) {
console.log('📝 处理NIM会话新增:', conversation);
const conversations = [conversation, ...this.data.conversations];
this.processConversations(conversations);
this.playMessageSound();
},
// 处理 NIM 会话删除
handleNIMConversationDeleted(conversationIds) {
console.log('🗑️ 处理NIM会话删除:', conversationIds);
const conversations = this.data.conversations.filter(
conv => !conversationIds.includes(conv.conversationId)
);
this.processConversations(conversations);
},
// 处理 NIM 会话更新
handleNIMConversationChanged(changedConversations) {
console.log('🔄 处理NIM会话更新数量:', changedConversations?.length || 0);
if (!Array.isArray(changedConversations) || changedConversations.length === 0) {
return;
}
// 🔥 增量更新:只更新变化的会话,而不是替换整个列表
let conversations = [...this.data.conversations];
let hasChanges = false;
changedConversations.forEach(changedConv => {
const index = conversations.findIndex(
conv => conv.conversationId === changedConv.conversationId
);
if (index >= 0) {
// 检查是否真的有变化(避免不必要的更新)
const oldConv = conversations[index];
const hasRealChange =
oldConv.unreadCount !== changedConv.unreadCount ||
oldConv.lastMessage !== changedConv.lastMessage ||
oldConv.isPinned !== changedConv.isPinned ||
oldConv.isMuted !== changedConv.isMuted;
if (hasRealChange) {
// 🔥 特殊处理如果旧会话有未读数新会话未读数为0但最后消息没变
// 说明这可能是标记已读导致的更新,不应该覆盖可能正在到来的新消息未读数
if (oldConv.unreadCount > 0 && changedConv.unreadCount === 0 &&
oldConv.lastMessage === changedConv.lastMessage) {
console.log('⚠️ 检测到可能的标记已读事件,保留当前未读数');
changedConv.unreadCount = oldConv.unreadCount;
}
conversations[index] = changedConv;
hasChanges = true;
}
} else {
// 新会话,添加到列表
conversations.unshift(changedConv);
hasChanges = true;
}
});
// 只有真正有变化时才更新 UI
if (hasChanges) {
this.processConversations(conversations);
} else {
console.log('⚠️ 会话数据无实质变化,跳过更新');
}
},
// 处理 NIM 新消息
async handleNIMNewMessages(messages) {
console.log('📨 处理NIM新消息:', messages);
if (!Array.isArray(messages)) return;
let conversations = [...this.data.conversations];
let hasChanges = false;
let needReloadConversations = false;
messages.forEach(message => {
const conversationId = message.conversationId;
if (!conversationId) return;
const index = conversations.findIndex(
conv => conv.conversationId === conversationId
);
if (index >= 0) {
// 更新现有会话
// 🔥 使用消息的 createTime如果没有则保持原时间
const messageTime = message.createTime || conversations[index].lastMsgTime || 0;
conversations[index] = {
...conversations[index],
lastMessage: nimConversationManager.getMessagePreview(message),
lastMessageType: nimConversationManager.getMessageType(message),
lastMessageTime: nimConversationManager.formatMessageTime(messageTime),
lastMsgTime: messageTime,
unreadCount: (conversations[index].unreadCount || 0) + 1
};
hasChanges = true;
} else {
// 新会话,需要重新加载
console.log('⚠️ 收到新会话消息,需要重新加载会话列表');
needReloadConversations = true;
}
});
if (hasChanges) {
this.processConversations(conversations);
this.playMessageSound();
}
if (needReloadConversations) {
this.loadConversations();
}
},
// 处理 NIM 消息撤回
handleNIMMessageRevoked(revokeNotifications) {
console.log('↩️ 处理NIM消息撤回:', revokeNotifications);
if (!Array.isArray(revokeNotifications)) return;
let conversations = [...this.data.conversations];
let hasChanges = false;
revokeNotifications.forEach(notification => {
const conversationId = notification.conversationId;
if (!conversationId) return;
const index = conversations.findIndex(
conv => conv.conversationId === conversationId
);
if (index >= 0) {
conversations[index] = {
...conversations[index],
lastMessage: '已撤回'
};
hasChanges = true;
}
});
if (hasChanges) {
this.processConversations(conversations);
}
},
// 🔥 处理用户在线状态变化
handlePresenceChanged(data) {
const { userId, isOnline } = data;
// 更新会话列表中对应用户的在线状态
let conversations = [...this.data.conversations];
let hasChanges = false;
conversations.forEach(conv => {
// 判断是否为单聊会话
const isSingleChat = conv.type === 'single' || conv.type === 0 || conv.chatType === 0;
if (!isSingleChat) {
return;
}
// 提取真实的用户ID进行匹配
let targetId = conv.targetId;
// 如果 targetId 包含 | 符号,说明是 conversationId 格式,需要解析
if (targetId && typeof targetId === 'string' && targetId.includes('|')) {
const parts = targetId.split('|');
if (parts.length === 3) {
targetId = parts[2];
conv.targetId = targetId; // 更新为纯用户ID
}
}
if (targetId === userId) {
conv.isOnline = isOnline;
hasChanges = true;
}
});
if (hasChanges) {
this.processConversations(conversations);
}
},
// 加载会话列表
async loadConversations() {
if (this.data.loading) return;
const currentToken = apiClient.getToken();
if (!currentToken) {
console.warn('⚠️ Token 丢失,无法加载会话列表');
return;
}
try {
this.setData({ loading: true });
console.log('📋 从 NIM SDK 加载会话列表');
const conversations = await nimConversationManager.getConversationList(0, 100);
console.log('✅ 加载会话列表成功,数量:', conversations.length);
this.processConversations(conversations);
} catch (error) {
console.error('❌ 加载会话列表失败:', error);
wx.showToast({
title: `加载会话列表失败:${error}`,
icon: 'none'
});
} finally {
this.setData({ loading: false });
}
},
// 播放消息提示音
playMessageSound() {
try {
const innerAudioContext = wx.createInnerAudioContext();
innerAudioContext.src = '/images/message-sound.mp3';
innerAudioContext.play();
} catch (error) {
console.error('播放提示音失败:', error);
}
},
// 更新Tab角标
updateTabBadge() {
const totalUnread = this.data.totalUnreadCount;
if (totalUnread > 0) {
wx.setTabBarBadge({
index: 1,
text: totalUnread > 99 ? '99+' : totalUnread.toString()
});
} else {
wx.removeTabBarBadge({ index: 1 });
}
// 自定义TabBar同步
try {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setMessagesBadge(totalUnread);
}
} catch (_) { }
},
// 处理会话数据
async processConversations(conversations) {
// 过滤已删除项,并统一关键字段
const validList = (conversations || [])
.filter(it => it && !it.deleted && !it._deleted)
.map(it => ({
...it,
isPinned: !!(it.isPinned || it.isTop),
_ts: this.normalizeTimestamp(it.lastMsgTime || it.lastMessageTimeTs || it.lastActiveAt || it.updatedAt)
}));
// 🔥 订阅会话中单聊用户的在线状态
try {
await nimPresenceManager.subscribeConversations(validList);
} catch (error) {
console.error('订阅在线状态失败:', error);
}
// 🔥 从缓存中更新在线状态
validList.forEach(conv => {
// 判断是否为单聊
const isSingleChat = conv.type === 'single' || conv.type === 0 || conv.chatType === 0;
if (isSingleChat) {
// 提取真实的用户ID处理可能的 conversationId 格式)
let targetId = conv.targetId;
// 如果 targetId 包含 | 符号,说明是 conversationId 格式,需要解析
if (targetId && typeof targetId === 'string' && targetId.includes('|')) {
const parts = targetId.split('|');
if (parts.length === 3) {
targetId = parts[2];
conv.targetId = targetId; // 更新为纯用户ID
}
}
if (!targetId) {
return;
}
const presence = nimPresenceManager.getUserPresence(targetId);
if (presence) {
conv.isOnline = presence.online;
} else {
conv.isOnline = false; // 默认离线
}
}
});
// 置顶/非置顶分组并组内按时间降序
const pinnedConversations = validList
.filter(item => item.isPinned)
.sort((a, b) => b._ts - a._ts);
const normalConversations = validList
.filter(item => !item.isPinned)
.sort((a, b) => b._ts - a._ts);
let sortedAll = [...pinnedConversations, ...normalConversations];
// 应用来自聊天页的撤回提示
sortedAll = this.applyRecallHints(sortedAll);
// 基于隐藏集合过滤呈现列表
const hiddenSet = this._getHiddenConversationSet();
const filteredSorted = (sortedAll || []).filter(item =>
!hiddenSet.has(String(item.conversationId || item.id || ''))
);
const totalUnreadCount = filteredSorted.reduce((sum, item) => sum + (item.unreadCount || 0), 0);
this.setData({
conversations: sortedAll,
pinnedConversations: filteredSorted.filter(i => i.isPinned),
normalConversations: filteredSorted.filter(i => !i.isPinned),
totalUnreadCount,
filteredConversations: filteredSorted
});
// 更新角标
this.updateTabBadge();
},
// 应用撤回提示(来自 chat 页存储TTL=2分钟
applyRecallHints(list) {
try {
const store = wx.getStorageSync('recallHints') || {};
const now = Date.now();
const TTL = 2 * 60 * 1000; // 2分钟
return list.map(conv => {
const cid = conv.conversationId || conv.id;
if (!cid) return conv;
const hint = store[cid];
if (hint && (now - hint.timestamp < TTL)) {
return {
...conv,
lastMessage: '已撤回',
lastMessageType: 'system'
};
}
return conv;
});
} catch (e) {
console.error('应用撤回提示失败:', e);
return list;
}
},
// 刷新会话列表
async refreshConversations() {
this.setData({ refreshing: true });
await this.loadConversations();
this.setData({ refreshing: false });
},
// 下拉刷新
onRefresh() {
this.refreshConversations();
},
// 搜索输入
onSearchInput(e) {
const keyword = (e.detail.value || '').trim();
this.setData({
searchKeyword: keyword,
lastSearchKeyword: keyword,
searching: !!keyword
});
if (!keyword) {
// 清空搜索结果
this.processConversations(this.data.conversations);
return;
}
// 🔥 本地会话列表过滤(快速响应)
this.fuzzyFilterConversations(keyword);
},
// 清除搜索
clearSearch() {
this.setData({
searchKeyword: '',
lastSearchKeyword: '',
searching: false
});
this.processConversations(this.data.conversations);
},
// 显示/隐藏搜索栏
showSearchBar() {
this.setData({ showSearch: true });
},
hideSearchBar() {
this.setData({
showSearch: false,
searchKeyword: '',
lastSearchKeyword: '',
searching: false
});
this.processConversations(this.data.conversations);
},
// 🔥 带搜索关键词打开聊天
openChatWithSearchKeyword(conversation, searchKeyword) {
if (!conversation.targetId) {
wx.showToast({
title: '会话信息不完整',
icon: 'none'
});
return;
}
const chatType = conversation.chatType !== undefined ? conversation.chatType : 0;
const params = {
conversationId: conversation.conversationId,
targetId: conversation.targetId,
name: encodeURIComponent(conversation.name),
chatType: chatType
};
const kw = (searchKeyword || '').trim();
if (kw) {
params.searchKeyword = encodeURIComponent(kw);
}
const queryString = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&');
wx.navigateTo({
url: `/pages/message/chat/chat?${queryString}`
});
},
// 本地模糊过滤
fuzzyFilterConversations(keyword) {
if (!keyword) {
this.processConversations(this.data.conversations);
return;
}
const q = (keyword || '').toLowerCase();
const subseqScore = (text, query) => {
if (!text || !query) return 0;
text = text.toLowerCase();
let ti = 0, qi = 0, matches = 0;
while (ti < text.length && qi < query.length) {
if (text[ti] === query[qi]) {
matches++;
qi++;
}
ti++;
}
return qi === query.length ? matches / text.length : 0;
};
const samples = this.data.conversationSamples || {};
const list = this.data.conversations || [];
const withScore = list.map(item => {
const nameScore = subseqScore(item.name || '', q) * 3;
const lastMsgScore = subseqScore(item.lastMessage || '', q) * 2;
const sampleTexts = samples[item.conversationId] || [];
const sampleScore = sampleTexts.reduce((acc, txt) => Math.max(acc, subseqScore(txt, q)), 0);
const totalScore = nameScore + lastMsgScore + sampleScore;
return { item, score: totalScore };
}).filter(x => x.score > 0);
withScore.sort((a, b) => {
if (Math.abs(a.score - b.score) < 0.01) {
return b.item._ts - a.item._ts;
}
return b.score - a.score;
});
let filtered = withScore.map(x => x.item);
if (filtered.length === 0) {
filtered = list.filter(item => {
const n = (item.name || '').toLowerCase();
const m = (item.lastMessage || '').toLowerCase();
return n.includes(q) || m.includes(q);
});
}
const pinnedFiltered = filtered.filter(i => i.isPinned);
const normalFiltered = filtered.filter(i => !i.isPinned);
this.setData({
pinnedConversations: pinnedFiltered,
normalConversations: normalFiltered,
filteredConversations: filtered
});
},
// 预取会话样本
async prefetchTopConversationSamples(keyword) {
const q = (keyword || '').toLowerCase();
const list = this.data.conversations || [];
const rough = list
.map(item => ({
item,
s: (String(item.name || '').toLowerCase().includes(q) ? 2 : 0) +
(String(item.lastMessage || '').toLowerCase().includes(q) ? 1 : 0)
}))
.sort((a, b) => b.s - a.s)
.slice(0, 8)
.map(x => x.item);
const tasks = [];
for (const conv of rough) {
const cid = conv.conversationId;
const loading = this.data.sampleLoading[cid];
const existing = this.data.conversationSamples[cid];
if (!loading && !existing) {
tasks.push(this.fetchConversationSample(conv));
}
}
if (tasks.length) {
await Promise.allSettled(tasks);
this.fuzzyFilterConversations(this.data.lastSearchKeyword);
}
},
async fetchConversationSample(conv) {
const cid = conv.conversationId;
try {
this.setData({
[`sampleLoading.${cid}`]: true
});
// 这里可以调用 NIM SDK 获取历史消息
// 暂时留空,等待实现
this.setData({
[`conversationSamples.${cid}`]: [],
[`sampleLoading.${cid}`]: false
});
} catch (e) {
console.error('抓取会话样本失败:', e);
this.setData({
[`sampleLoading.${cid}`]: false
});
}
},
// 打开聊天
openChat(e) {
// 如果正在长按,忽略单击事件
if (this.data.longPressing) {
console.log('长按中,忽略单击事件');
this.setData({ longPressing: false });
return;
}
const conversation = e.currentTarget.dataset.conversation;
if (!conversation.targetId) {
wx.showToast({
title: '会话信息不完整',
icon: 'none'
});
return;
}
const chatType = conversation.chatType !== undefined ? conversation.chatType : 0;
const params = {
conversationId: conversation.conversationId,
targetId: conversation.targetId,
name: encodeURIComponent(conversation.name),
chatType: chatType
};
const kw = (this.data.searchKeyword || '').trim();
if (kw) {
params.searchKeyword = encodeURIComponent(kw);
}
const queryString = Object.keys(params)
.map(key => `${key}=${params[key]}`)
.join('&');
wx.navigateTo({
url: `/pages/message/chat/chat?${queryString}`
});
},
// 显示会话选项
showConversationOptions(e) {
// 设置长按标记,防止长按后触发单击
this.setData({ longPressing: true });
const conversation = e.currentTarget.dataset.conversation;
const itemList = [
(conversation.isPinned || conversation.isTop) ? '取消置顶' : '置顶会话',
conversation.isMuted ? '取消免打扰' : '免打扰',
'不显示该聊天',
'删除会话'
];
wx.showActionSheet({
itemList: itemList,
success: (res) => {
switch (res.tapIndex) {
case 0:
this.toggleConversationTop(conversation.conversationId);
break;
case 1:
this.toggleConversationMute(conversation.conversationId);
break;
case 2:
this.hideConversation(conversation.conversationId);
break;
case 3:
this.deleteConversation(conversation.conversationId);
break;
}
}
});
},
// 标记会话已读
async markConversationRead(conversationId) {
try {
await nimConversationManager.markConversationRead(conversationId);
const conversations = [...this.data.conversations];
const index = conversations.findIndex(c => c.conversationId === conversationId);
if (index >= 0) {
conversations[index].unreadCount = 0;
this.processConversations(conversations);
}
} catch (error) {
console.error('标记已读失败:', error);
}
},
// 全部标记为已读
markAllRead() {
if (this._markingAll) return;
const unreadIds = (this.data.conversations || [])
.filter(c => (c.unreadCount || 0) > 0)
.map(c => c.conversationId)
.filter(Boolean);
if (!unreadIds.length) {
wx.showToast({ title: '没有未读消息', icon: 'none' });
return;
}
wx.showModal({
title: '确认操作',
content: '将所有会话标记为已读?',
success: async (res) => {
if (!res.confirm) return;
this._markingAll = true;
wx.showLoading({ title: '处理中...' });
try {
const tasks = unreadIds.map(id => this.markConversationRead(id));
await Promise.allSettled(tasks);
wx.hideLoading();
wx.showToast({ title: '已全部标记为已读', icon: 'success' });
} catch (err) {
console.error('批量标记已读失败:', err);
wx.hideLoading();
wx.showToast({ title: '操作失败', icon: 'none' });
} finally {
this._markingAll = false;
}
}
});
},
// 发起聊天入口
startNewChat() {
this.showMenu();
},
// ===== 隐藏会话 =====
_hiddenStorageKey: 'hiddenConversationIds',
_getHiddenConversationSet() {
try {
const arr = wx.getStorageSync(this._hiddenStorageKey) || [];
return new Set(arr);
} catch (_) {
return new Set();
}
},
_saveHiddenConversationSet(set) {
try {
wx.setStorageSync(this._hiddenStorageKey, Array.from(set));
} catch (_) { }
},
hideConversation(conversationId) {
const cid = String(conversationId || '');
if (!cid) return;
const set = this._getHiddenConversationSet();
if (!set.has(cid)) set.add(cid);
this._saveHiddenConversationSet(set);
this.processConversations(this.data.conversations || []);
wx.showToast({ title: '已隐藏该聊天', icon: 'none' });
},
_unhideConversationId(conversationId) {
const cid = String(conversationId || '');
if (!cid) return;
const set = this._getHiddenConversationSet();
if (set.has(cid)) {
set.delete(cid);
this._saveHiddenConversationSet(set);
}
},
// ===== 会话操作 =====
// 置顶/取消置顶会话
async toggleConversationTop(conversationId) {
const conversations = [...this.data.conversations];
const conv = conversations.find(c => c.conversationId === conversationId);
if (!conv) return;
const willPin = !conv.isPinned;
try {
wx.showLoading({ title: willPin ? '置顶中...' : '取消中...' });
await nimConversationManager.setConversationStickTop(conversationId, willPin);
conv.isPinned = willPin;
conv.isTop = willPin;
this.processConversations(conversations);
wx.hideLoading();
wx.showToast({
title: willPin ? '已置顶' : '已取消置顶',
icon: 'success'
});
} catch (error) {
console.error('置顶操作失败:', error);
wx.hideLoading();
wx.showToast({
title: '操作失败',
icon: 'none'
});
}
},
// 静音/取消静音会话
async toggleConversationMute(conversationId) {
const conversations = [...this.data.conversations];
const conv = conversations.find(c => c.conversationId === conversationId);
if (!conv) return;
const willMute = !conv.isMuted;
conv.isMuted = willMute;
this.processConversations(conversations);
wx.showToast({
title: willMute ? '已设置免打扰' : '已取消免打扰',
icon: 'success'
});
},
// 删除会话
async deleteConversation(conversationId) {
try {
wx.showLoading({ title: '删除中...' });
await nimConversationManager.deleteConversation(conversationId);
const conversations = this.data.conversations.filter(
c => c.conversationId !== conversationId
);
this.processConversations(conversations);
wx.hideLoading();
wx.showToast({ title: '已删除', icon: 'success' });
} catch (error) {
console.error('删除会话失败:', error);
wx.hideLoading();
wx.showToast({ title: '删除失败', icon: 'none' });
}
},
// 显示菜单
showMenu() {
wx.showActionSheet({
itemList: ['创建群聊', '添加好友', '扫一扫', '设置'],
success: (res) => {
switch (res.tapIndex) {
case 0: this.createGroup(); break;
case 1: this.addFriend(); break;
case 2: this.scanQRCode(); break;
case 3: this.openSettings(); break;
}
}
});
},
// 创建群聊
createGroup() {
wx.navigateTo({ url: '/subpackages/group/create-group/create-group' });
},
// 添加好友
addFriend() {
wx.navigateTo({ url: '/subpackages/social/search/search' });
},
// 扫一扫
scanQRCode() {
wx.scanCode({
success: (res) => {
this.handleScanResult(res.result);
},
fail: (err) => {
console.error('扫码失败:', err);
if (err.errMsg && err.errMsg.includes('cancel')) {
// 用户取消扫码,不显示错误提示
return;
}
wx.showToast({
title: '扫码失败',
icon: 'none'
});
}
});
},
// 处理扫码结果
handleScanResult(result) {
console.log('📱 扫码结果:', result);
try {
// 处理 FindMe 用户二维码格式: FINDME:{customId}
if (result && result.startsWith('FINDME:')) {
const customId = result.split(':')[1];
if (!customId) {
wx.showToast({
title: '无效的用户二维码',
icon: 'none'
});
return;
}
// 检查是否是自己
const app = getApp();
const currentUser = app.globalData.userInfo?.user;
if (currentUser && currentUser.customId === customId) {
wx.showToast({
title: '这是你自己的二维码',
icon: 'none'
});
return;
}
// 跳转到用户预览页(该页面会自动处理好友/非好友状态)
wx.navigateTo({
url: `/subpackages/social/user-preview/user-preview?customId=${customId}`,
fail: (err) => {
console.error('跳转用户预览页失败:', err);
wx.showToast({
title: '打开用户资料失败',
icon: 'none'
});
}
});
return;
}
// 尝试解析URL格式的二维码兼容旧格式
try {
const url = new URL(result);
const type = url.searchParams.get('type');
const id = url.searchParams.get('id');
if (type === 'user' && id) {
// 检查是否是自己
const app = getApp();
const currentUser = app.globalData.userInfo?.user;
if (currentUser && currentUser.customId === id) {
wx.showToast({
title: '这是你自己的二维码',
icon: 'none'
});
return;
}
wx.navigateTo({
url: `/subpackages/social/user-preview/user-preview?customId=${id}`
});
} else if (type === 'group' && id) {
this.joinGroupByQR(id);
} else {
wx.showToast({
title: '无效的二维码',
icon: 'none'
});
}
} catch (urlError) {
// 不是URL格式显示无效二维码提示
wx.showToast({
title: '无法识别的二维码格式',
icon: 'none'
});
}
} catch (error) {
console.error('处理扫码结果失败:', error);
wx.showToast({
title: '处理二维码失败',
icon: 'none'
});
}
},
// 通过二维码加入群聊
async joinGroupByQR(groupId) {
try {
wx.showLoading({ title: '加入中...' });
// 调用加入群组API
// await groupAPI.joinGroup(groupId);
wx.hideLoading();
wx.showToast({ title: '已加入群聊', icon: 'success' });
setTimeout(() => {
wx.navigateTo({
url: `/subpackages/group/group-info/group-info?groupId=${groupId}`
});
}, 1500);
} catch (error) {
console.error('加入群聊失败:', error);
wx.hideLoading();
wx.showToast({
title: '加入失败',
icon: 'none'
});
}
},
// 打开设置
openSettings() {
wx.navigateTo({ url: '/subpackages/settings/settingss/settingss' });
},
// 页面显示
onShow: function () {
console.log('消息页面显示');
// 设置tabBar选中状态为"聊天"索引2
try {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 2 });
}
} catch (_) {}
const currentToken = apiClient.getToken();
const app = getApp();
const isLoggedIn = app?.globalData?.isLoggedIn || false;
if (!currentToken) {
console.warn('⚠️ onShow检测到用户未登录');
if (isLoggedIn) {
this.redirectToLogin('onShow');
}
return;
}
// 🔥 检查并重连 NIM防止从后台恢复后 NIM 断开)
this.checkAndReconnectNim();
// 刷新会话列表
this.refreshConversations();
},
// 页面隐藏
onHide: function () {
console.log('消息页面隐藏');
},
// 页面卸载
onUnload: function () {
console.log('消息页面卸载');
// 移除 NIM 监听器
if (this.data.nimReady) {
nimConversationManager.off('conversation_created');
nimConversationManager.off('conversation_deleted');
nimConversationManager.off('conversation_changed');
nimConversationManager.off('new_message');
nimConversationManager.off('message_revoked');
}
}
});