// pages/messages/messages.js const app = getApp(); import { formatDate } from '../../static/lib/utils'; import { resolveAvatarUrl, getDefaultAvatarUrl, resolveAvatarFromStorage } from '../../utils/avatar.js'; import { getLocalImUserId, getLocalImIdentity, getImUserIdForRole, loadGroupMetaCache, getDefaultAvatar } from '../../utils/im-user.js'; import { enrichGroupConversation, isOrderGroupConversation, sortConversations, recordConversationOpen } from '../../utils/group-chat.js'; import { reconnectForRole } from '../../utils/role-tab-bar.js'; import { getPrimaryRole } from '../../utils/primary-role.js'; import { isStaffMode } from '../../utils/staff-api.js'; import { createPage } from '../../utils/base-page.js'; const { jianquanxian } = require('../../utils/imAuth/jianquanxian'); Page(createPage({ data: { allConversations: [], filteredConversations: [], activeTab: 0, tabUnread: [0, 0, 0], displayCount: 10, searchKeyword: '', actionPopup: { visible: false, conversation: null }, currentUser: null, notificationMuted: false, scrollHeight: 0 }, _permissionSeq: 0, _peerProfileCache: {}, onLoad() { this.calculateScrollHeight(); const muted = wx.getStorageSync('notificationMuted') || false; this.setData({ notificationMuted: muted }); if (app.globalData.messageManager) { app.globalData.messageManager.notificationMuted = muted; } loadGroupMetaCache(app); this._onGlobalConvUpdated = (content) => { if (content && content.conversations) { this.renderConversations(content); } else if (app.loadConversations) { app.loadConversations(); } }; app.on('conversationsUpdated', this._onGlobalConvUpdated); }, onUnload() { if (this._onGlobalConvUpdated) { app.off('conversationsUpdated', this._onGlobalConvUpdated); } if (this.conversationsUpdatedListener && wx.goEasy?.im) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, this.conversationsUpdatedListener); this.conversationsUpdatedListener = null; } }, onShow() { if (!this.checkLoginStatus()) return; loadGroupMetaCache(app); this.registerNotificationComponent(); if (app.globalData.messageManager?.unreadTotal != null && app.emitEvent) { app.emitEvent('tabBarBadgeChanged', { badgeText: app.globalData.messageManager.unreadTotal > 0 ? String(app.globalData.messageManager.unreadTotal) : '', }); } this.checkPermissionAndAutoConnect(); if (app.ensureConnection) app.ensureConnection(); this.setupConversationListener(); setTimeout(() => { if (app.loadConversations) app.loadConversations(); else if (wx.goEasy?.im) this.loadConversations(); }, 200); }, onHide() {}, // ========== ==== ================ ==== ================ 鉴权检查 ========== async checkPermissionAndAutoConnect() { const seq = ++this._permissionSeq; let quanxian; try { quanxian = await jianquanxian(app); } catch (e) { return; } if (seq !== this._permissionSeq) return; if (!quanxian.allowed) { const currentRole = app.globalData.currentRole || 'normal'; let content = '您没有权限使用消息功能'; if (currentRole === 'dashou') content = '您需要先充值会员才能使用消息功能'; wx.showModal({ title: '权限不足', content, showCancel: false, confirmText: '我知道了' }); return; } this.autoConnect(); }, // 登录状态检查 checkLoginStatus() { const uid = wx.getStorageSync('uid'); if (!uid) { wx.showToast({ title: '请先登录', icon: 'none' }); setTimeout(() => { wx.redirectTo({ url: '/pages/mine/mine' }); }, 1500); return false; } let currentUser = app.globalData.currentUser; if (!currentUser) { currentUser = { id: getLocalImUserId(app) || uid, name: wx.getStorageSync('nicheng') || app.globalData.dashouNicheng || app.globalData.nicheng || ('用户' + uid.substring(0, 6)), avatar: resolveAvatarFromStorage(app), }; app.globalData.currentUser = currentUser; } this.setData({ currentUser }); return true; }, autoConnect() { const role = isStaffMode() ? 'shangjia' : (getLocalImIdentity(app) || app.globalData.currentRole || 'normal'); const uid = wx.getStorageSync('uid'); const targetUserId = getImUserIdForRole(role, uid); if (!targetUserId) return; const onReady = () => { this.setupConversationListener(); if (app.loadConversations) app.loadConversations(); else this.loadConversations(); }; const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected'; const currentUserId = wx.goEasy?.im?.userId; if ((status === 'connected' || status === 'reconnected') && currentUserId === targetUserId) { onReady(); return; } const tryConnect = async () => { try { if (app.ensureImForRole) { await app.ensureImForRole(role); } else if (app.connectWithIdentity) { await app.connectWithIdentity(role, targetUserId, true); } else if (app.ensureConnection) { app.ensureConnection(); } onReady(); } catch (e) { const s = wx.goEasy?.getConnectionStatus?.(); if (s === 'connected' || s === 'reconnected') onReady(); } }; tryConnect(); }, connectGoEasy(userId) { const { currentUser } = this.data; if (!currentUser) return; wx.goEasy.connect({ id: userId, data: { name: currentUser.name, avatar: resolveAvatarUrl(currentUser.avatar, app), }, onSuccess: () => { try { wx.setStorageSync('savedGoEasyConnection', JSON.stringify({ userId, identityType: app.globalData.currentRole, lastConnectTime: Date.now(), status: 'connected', })); } catch (e) {} this.setupConversationListener(); this.loadConversations(); if (app.registerNotification) { } }, onFailed: (error) => { if (error.code === 408) { this.setupConversationListener(); this.loadConversations(); } else { wx.showToast({ title: '连接失败,请重试', icon: 'none' }); } }, }); }, setupConversationListener() { if (!wx.goEasy || !wx.goEasy.im) return; if (this.conversationsUpdatedListener) { wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, this.conversationsUpdatedListener); } this.conversationsUpdatedListener = (result) => { const content = result?.content || result; this.renderConversations(content); }; wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, this.conversationsUpdatedListener); }, loadConversations() { if (!wx.goEasy || !wx.goEasy.im) return; wx.goEasy.im.latestConversations({ onSuccess: (result) => { const content = result?.content || result; this.renderConversations(content); }, onFailed: () => { wx.showToast({ title: '获取会话失败', icon: 'none' }); }, }); }, // 会话渲染与排序 renderConversations(content) { const conversations = (content && content.conversations) ? content.conversations : []; if (!conversations.length) { this.setData({ allConversations: [] }, () => this.applyFilter()); return; } const myGoEasyId = wx.goEasy?.im?.userId || getLocalImUserId(app); const defaultAvatar = getDefaultAvatar(app); const ossBase = app.globalData.ossImageUrl || ''; let list = conversations.map((item) => { if (item.type === 'group') { return enrichGroupConversation(item, myGoEasyId, defaultAvatar, ossBase); } if (!item.data) item.data = {}; if (item.data.avatar) { item.data.avatar = resolveAvatarUrl(item.data.avatar, app); } return item; }); list.forEach(item => { if (!item.lastMessage) { item.lastMessage = { type: 'text', payload: { text: '' }, timestamp: 0 }; } if (item.lastMessage.timestamp) { item.lastMessage.date = formatDate(item.lastMessage.timestamp); } if (item.type === 'group' && item.displayLastMsg) { item.lastMessage.preview = item.displayLastMsg; } }); const openTimes = wx.getStorageSync('conversationOpenTimes') || {}; list = sortConversations(list, openTimes); this.setData({ allConversations: list, displayCount: 10 }, () => { this.applyFilter(); }); const unreadTotal = content.unreadTotal !== undefined ? content.unreadTotal : list.reduce((sum, c) => sum + (c.unread || 0), 0); if (app.updateUnreadCount) { app.updateUnreadCount(unreadTotal); } else if (app.emitEvent) { app.emitEvent('tabBarBadgeChanged', { badgeText: unreadTotal > 0 ? String(unreadTotal) : '' }); } // 重新订阅群组 this.subscribeGroupsIfNeeded(list); }, /** * 群组订阅,每次获取群组列表时直接订阅保证消息不丢失 */ subscribeGroupsIfNeeded(conversations) { if (!wx.goEasy?.im || typeof wx.goEasy.im.subscribeGroup !== 'function') return; const groupIds = conversations .filter(c => c.type === 'group' && c.groupId) .map(c => c.groupId); if (groupIds.length === 0) return; // 不再判断缓存,直接订阅当前所有群组 wx.goEasy.im.subscribeGroup({ groupIds: groupIds, onSuccess: () => { // 可选的日志输出,不影响功能 // console.log('群组订阅成功', groupIds); }, onFailed: (error) => { console.error('群组订阅失败:', error); } }); }, applyFilter() { const { allConversations, activeTab, searchKeyword } = this.data; let filtered = []; if (activeTab === 0) { filtered = allConversations.filter(c => isOrderGroupConversation(c)); } else if (activeTab === 1) { filtered = allConversations.filter(c => c.type === 'private'); } else if (activeTab === 2) { filtered = allConversations.filter(c => c.type === 'cs'); } if (searchKeyword.trim()) { const kw = searchKeyword.trim().toLowerCase(); filtered = filtered.filter(c => { const name = (c.data && c.data.name || '').toLowerCase(); const orderId = (c.data && c.data.orderId || '').toLowerCase(); const id = (c.userId || c.groupId || '').toLowerCase(); return name.includes(kw) || id.includes(kw) || orderId.includes(kw); }); } const tabUnread = [0, 0, 0]; allConversations.forEach(c => { if (isOrderGroupConversation(c)) tabUnread[0] += c.unread || 0; else if (c.type === 'private') tabUnread[1] += c.unread || 0; else if (c.type === 'cs') tabUnread[2] += c.unread || 0; }); this.setData({ filteredConversations: filtered, tabUnread }); }, switchTab(e) { const index = parseInt(e.currentTarget.dataset.index); this.setData({ activeTab: index, displayCount: 10, searchKeyword: '' }, () => this.applyFilter()); }, onSearchInput(e) { this.setData({ searchKeyword: e.detail.value, displayCount: 10 }, () => this.applyFilter()); }, onScrollToLower() { const { displayCount, filteredConversations } = this.data; if (displayCount < filteredConversations.length) { this.setData({ displayCount: Math.min(displayCount + 10, filteredConversations.length) }); } }, onRefresh() { this.loadConversations(); }, chat(e) { const conversation = e.currentTarget.dataset.conversation; if (!conversation) return; recordConversationOpen(conversation); if (conversation.type === 'private') { const param = { toUserId: conversation.userId, toName: conversation.data.name || '用户', toAvatar: resolveAvatarUrl(conversation.data.avatar, app), }; wx.navigateTo({ url: '/pages/chat/chat?data=' + encodeURIComponent(JSON.stringify(param)) }); } else if (conversation.type === 'group') { const d = conversation.data || {}; const param = { groupId: conversation.groupId, orderId: d.orderId || '', groupName: d.name || '订单群聊', groupAvatar: d.avatar, isCross: d.isCross || 0, orderZhuangtai: d.orderZhuangtai, orderJine: d.orderJine, orderDesc: d.orderDesc || d.orderJieshao || '', }; wx.navigateTo({ url: '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param)) }); } else if (conversation.type === 'cs') { const param = { teamId: conversation.teamId || conversation.id }; wx.navigateTo({ url: '/pages/cs-chat/cs-chat?data=' + encodeURIComponent(JSON.stringify(param)) }); } }, chatKefu() { const param = { teamId: 'support_team' }; wx.navigateTo({ url: '/pages/cs-chat/cs-chat?data=' + encodeURIComponent(JSON.stringify(param)) }); }, showAction(e) { const conversation = e.currentTarget.dataset.conversation; this.setData({ ['actionPopup.conversation']: conversation, ['actionPopup.visible']: true }); }, closeMask() { this.setData({ ['actionPopup.visible']: false }); }, topConversation() { let conversation = this.data.actionPopup.conversation; if (!conversation) return; const top = !conversation.top; wx.showLoading({ title: '处理中...' }); if (conversation.type === 'private') { wx.goEasy.im.topPrivateConversation({ userId: conversation.userId, top, onSuccess: () => { wx.hideLoading(); this.loadConversations(); wx.showToast({ title: top ? '已置顶' : '已取消置顶' }); }, onFailed: () => { wx.hideLoading(); wx.showToast({ title: '操作失败', icon: 'none' }); } }); } else { wx.goEasy.im.topGroupConversation({ groupId: conversation.groupId, top, onSuccess: () => { wx.hideLoading(); this.loadConversations(); wx.showToast({ title: top ? '已置顶' : '已取消置顶' }); }, onFailed: () => { wx.hideLoading(); wx.showToast({ title: '操作失败', icon: 'none' }); } }); } this.closeMask(); }, removeConversation() { let conversation = this.data.actionPopup.conversation; if (!conversation) return; wx.showLoading({ title: '删除中...' }); wx.goEasy.im.removeConversation({ conversation: conversation, onSuccess: () => { wx.hideLoading(); this.loadConversations(); wx.showToast({ title: '已删除' }); }, onFailed: () => { wx.hideLoading(); wx.showToast({ title: '删除失败', icon: 'none' }); } }); this.closeMask(); }, toggleNotification() { const newMuted = !this.data.notificationMuted; this.setData({ notificationMuted: newMuted }); wx.setStorageSync('notificationMuted', newMuted); wx.showToast({ title: newMuted ? '已静音' : '已开启通知', icon: 'none' }); }, calculateScrollHeight() { const systemInfo = wx.getSystemInfoSync(); const windowHeight = systemInfo.windowHeight; const windowWidth = systemInfo.windowWidth; const rpxRatio = 750 / windowWidth; const headerHeight = 150 / rpxRatio; const tabBarHeight = 130 / rpxRatio; const safeAreaBottom = systemInfo.safeArea ? (windowHeight - systemInfo.safeArea.bottom) : 0; this.setData({ scrollHeight: windowHeight - headerHeight - tabBarHeight - safeAreaBottom }); }, onAvatarError(e) { const index = e.currentTarget.dataset.index; const def = getDefaultAvatarUrl(app); const key = `filteredConversations[${index}].data.avatar`; this.setData({ [key]: def }); } }))