Files
Wechat/pages/xiaoxi/xiaoxi.js

407 lines
15 KiB
JavaScript
Raw 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.
// pages/xiaoxi/xiaoxi.js
const app = getApp();
import { formatDate } from '../../static/lib/utils';
import { resolveAvatarUrl, getDefaultAvatarUrl, resolveAvatarFromStorage } from '../../utils/avatar.js';
import { getLocalImUserId } from '../../utils/im-user.js';
import { normalizeConversationsList } from '../../utils/conversation-display.js';
import { reconnectForRole } from '../../utils/role-tab-bar.js';
import { getPrimaryRole } from '../../utils/primary-role.js';
const { jianquanxian } = require('../../utils/imAuth/jianquanxian');
Page({
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 });
},
onShow() {
if (!this.checkLoginStatus()) return;
this.registerNotificationComponent();
this.checkPermissionAndAutoConnect();
},
onHide() {},
onUnload() {},
// ========== 鉴权检查 ==========
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/wode/wode' }); }, 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;
},
registerNotificationComponent() {
const notificationComp = this.selectComponent('#global-notification');
if (notificationComp && notificationComp.showNotification) {
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification(),
};
}
},
autoConnect() {
const targetUserId = getLocalImUserId(app);
if (!targetUserId) return;
if (app.ensureConnection) app.ensureConnection();
reconnectForRole(getPrimaryRole(app));
const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
if (status === 'connected' || status === 'reconnected') {
const currentUserId = wx.goEasy.im ? wx.goEasy.im.userId : null;
if (currentUserId === targetUserId) {
this.setupConversationListener();
this.loadConversations();
return;
}
try {
wx.goEasy.disconnect();
} catch (e) {}
}
this.connectGoEasy(targetUserId);
},
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) {
// 全局通知在 chat-core 注册
}
},
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;
}
let list = normalizeConversationsList(conversations, app, this._peerProfileCache);
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);
}
});
list.sort((a, b) => {
if (a.top && !b.top) return -1;
if (!a.top && b.top) return 1;
if (a.unread > 0 && b.unread <= 0) return -1;
if (a.unread <= 0 && b.unread > 0) return 1;
const ta = (a.lastMessage && a.lastMessage.timestamp) || 0;
const tb = (b.lastMessage && b.lastMessage.timestamp) || 0;
return tb - ta;
});
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);
},
/**
* 群组订阅依赖GoEasy IM
* 根据官方文档,需要主动 subscribeGroup 才能接收群聊消息。
* 为了应对切后台、网络波动等导致的订阅丢失,这里不再缓存订阅状态,
* 每次获取到群组列表时都直接订阅,保证消息不丢失。
*/
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 => c.type === 'group' && c.data && c.data.orderId);
} 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 (c.type === 'group' && c.data && c.data.orderId) 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;
if (conversation.type === 'private') {
const param = {
toUserId: conversation.userId,
toName: conversation.data.name || '用户',
toAvatar: resolveAvatarUrl(conversation.data.avatar, app),
};
wx.navigateTo({ url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param)) });
} else if (conversation.type === 'group') {
const param = {
groupId: conversation.groupId,
orderId: conversation.data.orderId || '',
groupName: conversation.data.name,
groupAvatar: conversation.data.avatar,
isCross: conversation.data.isCross || 0
};
wx.navigateTo({ url: '/pages/qunliaotian/qunliaotian?data=' + encodeURIComponent(JSON.stringify(param)) });
} else if (conversation.type === 'cs') {
const param = { teamId: conversation.teamId || conversation.id };
wx.navigateTo({ url: '/pages/kefuliaotian/kefuliaotian?data=' + encodeURIComponent(JSON.stringify(param)) });
}
},
chatKefu() {
const param = { teamId: 'support_team' };
wx.navigateTo({ url: '/pages/kefuliaotian/kefuliaotian?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 });
}
});