Files
a_long/pages/xiaoxi/xiaoxi.js

409 lines
16 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';
const { jianquanxian } = require('../../utils/imAuth/jianquanxian');
import { enrichGroupConversation, isOrderGroupConversation } from '../../utils/group-chat.js';
Page({
data: {
allConversations: [],
filteredConversations: [],
activeTab: 0,
tabUnread: [0, 0, 0],
displayCount: 4,
searchKeyword: '',
actionPopup: { visible: false, conversation: null },
currentUser: null,
notificationMuted: false,
scrollHeight: 0
},
_permissionSeq: 0,
onLoad() {
this.calculateScrollHeight();
const muted = wx.getStorageSync('notificationMuted') || false;
this.setData({ notificationMuted: muted });
},
onShow() {
if (!this.checkLoginStatus()) return;
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: uid,
name: '用户' + uid.substring(0, 6),
avatar: app.globalData.ossImageUrl + app.globalData.morentouxiang
};
app.globalData.currentUser = currentUser;
}
this.setData({ currentUser });
return true;
},
// ========== 自动连接(复用全局连接,避免反复 disconnect ==========
autoConnect() {
const role = app.globalData.currentRole || 'normal';
const uid = wx.getStorageSync('uid');
const prefixMap = { normal: 'Boss', dashou: 'Ds', shangjia: 'Sj', guanshi: 'Gs', zuzhang: 'Zz' };
const targetUserId = (prefixMap[role] || 'Boss') + uid;
const onReady = () => {
this.setupConversationListener();
this.loadConversations();
};
const status = wx.goEasy?.getConnectionStatus?.() || 'disconnected';
const currentUserId = wx.goEasy?.im?.userId;
if ((status === 'connected' || status === 'reconnected') && currentUserId === targetUserId) {
onReady();
return;
}
if (currentUserId && currentUserId !== targetUserId && app.disconnectGoEasy) {
app.disconnectGoEasy().then(() => {
app.connectWithIdentity(role, targetUserId, true).then(onReady).catch(() => {});
});
return;
}
if (!app.connectWithIdentity) {
this.connectGoEasy(targetUserId);
return;
}
app.connectWithIdentity(role, targetUserId, true).then(onReady).catch(() => {
const s = wx.goEasy?.getConnectionStatus?.();
if (s === 'connected' || s === 'reconnected') onReady();
});
},
connectGoEasy(userId) {
const { currentUser } = this.data;
if (!currentUser) return;
wx.goEasy.connect({
id: userId,
data: {
name: currentUser.name,
avatar: currentUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
},
onSuccess: () => {
try {
const connection = {
userId: userId,
identityType: app.globalData.currentRole,
lastConnectTime: Date.now(),
autoReconnect: true,
status: 'connected'
};
wx.setStorageSync('savedGoEasyConnection', JSON.stringify(connection));
wx.setStorageSync('goEasyUserId', userId);
} catch (e) {}
this.setupConversationListener();
this.loadConversations();
},
onFailed: (error) => {
if (error.code === 408) {
this.setupConversationListener();
this.loadConversations();
} else {
wx.showToast({ title: '连接失败,请重试', icon: 'none' });
}
}
});
},
// ========== 会话监听(永不清理) ==========
setupConversationListener() {
if (this.conversationsUpdatedListener) {
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, this.conversationsUpdatedListener);
}
this.conversationsUpdatedListener = (result) => {
this.renderConversations(result);
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, this.conversationsUpdatedListener);
},
loadConversations() {
wx.goEasy.im.latestConversations({
onSuccess: (result) => {
this.renderConversations(result.content);
},
onFailed: () => {
wx.showToast({ title: '获取会话失败', icon: 'none' });
}
});
},
// ========== 会话渲染与排序 ==========
renderConversations(content) {
if (!content || !content.conversations) {
this.setData({ allConversations: [] }, () => this.applyFilter());
return;
}
let list = content.conversations;
const defaultAvatar = app.globalData.ossImageUrl + app.globalData.morentouxiang;
const myGoEasyId = wx.goEasy?.im?.userId || '';
const ossBase = app.globalData.ossImageUrl || '';
const groupMeta = app.globalData.groupInfoMap || {};
list.forEach(item => {
if (item.type === 'group') {
const meta = groupMeta[item.groupId];
if (meta) {
if (!item.data) item.data = {};
if (meta.name) item.data.name = meta.name;
if (meta.avatar) item.data.avatar = meta.avatar;
if (meta.orderDesc) item.data.orderDesc = meta.orderDesc;
if (meta.orderId) item.data.orderId = meta.orderId;
if (meta.orderZhuangtai != null) item.data.orderZhuangtai = meta.orderZhuangtai;
}
enrichGroupConversation(item, myGoEasyId, defaultAvatar, ossBase);
} else if (item.data && item.data.avatar) {
if (!item.data.avatar.startsWith('http')) {
item.data.avatar = ossBase + item.data.avatar;
}
} else if (item.data) {
item.data.avatar = defaultAvatar;
}
if (item.lastMessage && 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;
return (b.lastMessage.timestamp || 0) - (a.lastMessage.timestamp || 0);
});
this.setData({ allConversations: list, displayCount: 4 }, () => {
this.applyFilter();
});
const unreadTotal = content.unreadTotal !== undefined ? content.unreadTotal : list.reduce((sum, c) => sum + (c.unread || 0), 0);
if (app && 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 => 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: 4, searchKeyword: '' }, () => this.applyFilter());
},
onSearchInput(e) {
this.setData({ searchKeyword: e.detail.value, displayCount: 4 }, () => this.applyFilter());
},
onScrollToLower() {
const { displayCount, filteredConversations } = this.data;
if (displayCount < filteredConversations.length) {
this.setData({ displayCount: Math.min(displayCount + 4, 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: conversation.data.avatar
};
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 });
}
});