findme-miniprogram-frontend/pages/message/message.js
2025-12-27 17:16:03 +08:00

1290 lines
36 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 消息页面逻辑 - 完全基于 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');
}
}
});