diff --git a/App.vue b/App.vue index 7e31c1b..a03459d 100644 --- a/App.vue +++ b/App.vue @@ -4,44 +4,34 @@ import V2NIM, { V2NIMConst } from "nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK"; import { getMsgContentTipByType } from "./pages/NEUIKit/utils/msg"; import { STORAGE_KEY } from "./pages/NEUIKit/utils/constants"; import { isWxApp } from "./pages/NEUIKit/utils"; -import { V2NIMMessage, V2NIMMessagePushConfig } from "nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK/V2NIMMessageService"; +import { V2NIMMessage, V2NIMMessagePushConfig, V2NIMConversation } from "nim-web-sdk-ng/dist/v2/NIM_UNIAPP_SDK/V2NIMMessageService"; import { unix } from "dayjs"; export default { globalData: { timer: null, // 定时器变量 + reconnectTimer: null, // 重连专用定时器 themeColor: "#00b666", themeBackgroundColor: "#00b66621", }, onLaunch() { let that = this; uni.removeStorageSync("selectedCity"); - // 可能只在小程序生效的监听方法 - // uni.onBeforeAppRoute((res) => { - // console.log("setNavStyle", res); - // try { - // that.G.Post("/common/user/browsing/history/addRecord", { browsingUrl: res.path }, (res) => {}); - // } catch (error) { - // console.log("error", error); - // } - // }); + // 监听路由跳转 uni.addInterceptor("navigateTo", { - // 跳转前拦截 invoke(args) { console.log("navigateTo 跳转前", args); that.G.watchUserPage(args.url); }, }); uni.addInterceptor("reLaunch", { - // 跳转前拦截 invoke(args) { console.log("reLaunch 跳转前", args); that.G.watchUserPage(args.url); }, }); uni.addInterceptor("redirectTo", { - // 跳转前拦截 invoke(args) { console.log("redirectTo 跳转前", args); that.G.watchUserPage(args.url); @@ -51,6 +41,7 @@ export default { onShow: function (options) { let that = this; console.log("show 项目init:", options, decodeURIComponent(options.query.scene)); + // 清除扫码获取的id信息 uni.removeStorageSync("scene"); uni.removeStorageSync("user_scene"); @@ -74,15 +65,21 @@ export default { } else { uni.setStorageSync("apply-invite-code", ""); } + if (uni.getStorageSync("apply-token")) { this.G.checkToken(); } + + // 初始化定时器 - 合并检查任务 if (!that.globalData.timer) { that.checkNum(); + that.checkAndReconnectIM(); // 立即检查一次连接 + + // 缩短检查间隔到15秒,兼顾消息提醒和连接检查 that.globalData.timer = setInterval(() => { - console.log("inner+++++++++++++++++++++++++++++++++++++++++++++++"); that.checkNum(); - }, 30 * 1000); + that.checkAndReconnectIM(); + }, 15 * 1000); } uni.$on("isGlogin", function (data) { @@ -91,19 +88,127 @@ export default { that.initWyyx(); } }); + + // 应用从后台返回时检查连接 if (uni.getStorageSync("apply-token")) { - console.log("app.vue 是否调用"); - that.initWyyx(); + console.log("app.vue 检查IM连接状态"); + that.checkAndReconnectIM(); } }, onHide: function () { + // 应用进入后台时不清除定时器,保持心跳 + console.log("应用进入后台,保持IM连接检测"); + + // 发送一次心跳包 + if (uni.$UIKitNIM && uni.$UIKitNIM.V2NIMLoginService) { + uni.$UIKitNIM.V2NIMLoginService.ping().then(() => { + console.log("后台心跳发送成功"); + }).catch(err => { + console.error("后台心跳发送失败,准备重连", err); + this.reconnectIM(); + }); + } + }, + onUnload() { + // 页面卸载时才清除定时器 if (this.globalData.timer) { clearInterval(this.globalData.timer); this.globalData.timer = null; - console.log("定时任务已清除"); + console.log("定时器已清除"); + } + + if (this.globalData.reconnectTimer) { + clearInterval(this.globalData.reconnectTimer); + this.globalData.reconnectTimer = null; } }, methods: { + // 检查并重新连接IM + checkAndReconnectIM() { + let that = this; + // 延迟执行,确保SDK初始化完成 + setTimeout(() => { + if (uni.$UIKitNIM && uni.$UIKitStore) { + const connectStatus = uni.$UIKitStore.connectStore.connectStatus; + console.log('当前IM连接状态:', connectStatus); + + // 连接状态码: + // 0: 初始状态 + // 1: 已断开 + // 2: 已连接 + // 3: 连接中 + if (connectStatus !== 2 && connectStatus !== 3) { + console.log("IM连接已断开,尝试重连..."); + that.reconnectIM(); + } + } else if (uni.getStorageSync("apply-token") && uni.getStorageSync("apply-uid")) { + console.log("IM SDK未初始化,重新初始化..."); + that.initWyyx(); + } + }, 1000); + }, + + // 重新连接IM + reconnectIM() { + let that = this; + + // 如果已经在重连中,则不再重复发起 + if (this.globalData.reconnectTimer) { + console.log("正在重连中,无需重复操作"); + return; + } + + // 尝试重连 + const attemptReconnect = () => { + if (!uni.getStorageSync("apply-token") || !uni.getStorageSync("apply-uid")) { + console.log("用户未登录,停止重连"); + clearInterval(that.globalData.reconnectTimer); + that.globalData.reconnectTimer = null; + return; + } + + if (uni.$UIKitNIM && uni.$UIKitNIM.V2NIMLoginService) { + const account = uni.getStorageSync("im-accid"); + const token = uni.getStorageSync("im-token"); + + if (account && token) { + console.log(`尝试重连IM (account: ${account})`); + uni.$UIKitNIM.V2NIMLoginService.login(account, token) + .then(() => { + console.log("IM重连成功"); + clearInterval(that.globalData.reconnectTimer); + that.globalData.reconnectTimer = null; + }) + .catch((error) => { + console.error("IM重连失败,将再次尝试", error); + }); + } else { + console.log("重连信息不全,重新初始化IM"); + that.initWyyx(); + } + } else { + console.log("SDK未初始化,重新初始化IM"); + that.initWyyx(); + } + }; + + // 立即尝试一次重连 + attemptReconnect(); + + // 设置重连定时器,每5秒尝试一次,最多尝试10次 + let reconnectAttempts = 0; + this.globalData.reconnectTimer = setInterval(() => { + reconnectAttempts++; + if (reconnectAttempts >= 10) { + console.log("重连尝试次数已达上限,停止重连"); + clearInterval(that.globalData.reconnectTimer); + that.globalData.reconnectTimer = null; + return; + } + attemptReconnect(); + }, 5000); + }, + initWyyx() { let that = this; @@ -114,36 +219,46 @@ export default { }, (res) => { console.log("wyyx_getConfig", res); + + // 检查返回数据有效性 + if (!res || !res.appKey || !res.accid || !res.token) { + console.error("获取IM配置失败,参数不完整"); + return; + } + let resData = { appkey: res.appKey, accid: res.accid, token: res.token, }; + + // 保存IM配置信息,用于重连 + uni.setStorageSync('im-appkey', resData.appkey); + uni.setStorageSync('im-accid', resData.accid); + uni.setStorageSync('im-token', resData.token); + const imOptions = { - appkey: resData.appkey, // 请填写您的 appkey - account: resData.accid, // 请填写您的 account - token: resData.token, // 请填写您的 token - // appkey: "7e19d679085266aa872a8de12f0c1ae5", // 请填写您的 appkey - // account: "9", // 请填写您的 account - // token: "2d4ca36cca6293cf3a81d6ca1b8026f7", // 请填写您的 token + appkey: resData.appkey, + account: resData.accid, + token: resData.token, }; - if (imOptions) { - this.initNim(imOptions); - } else { - // 需要登录, 跳转登录页 - } + + this.initNim(imOptions); } ); }, + async initNim(opts) { let that = this; - // 初始化 nim sdk + // 初始化 nim sdk,增强重连配置 const nim = (uni.$UIKitNIM = V2NIM.getInstance( { appkey: opts.appkey, needReconnect: true, - // "reconnectionAttempts": 5, - debugLevel: "error", + reconnectionAttempts: 10, // 增加重连次数 + reconnectionDelay: 3000, // 重连间隔3秒 + heartbeatInterval: 20000, // 缩短心跳间隔为20秒 + debugLevel: "warn", // 提高日志级别便于调试 apiVersion: "v2", enableV2CloudConversation: true, }, @@ -151,10 +266,8 @@ export default { V2NIMLoginServiceConfig: { lbsUrls: isWxApp ? ["https://lbs.netease.im/lbs/wxwebconf.jsp"] : ["https://lbs.netease.im/lbs/webconf.jsp"], linkUrl: isWxApp ? "wlnimsc0.netease.im" : "weblink.netease.im", - /** - * 使用固定设备 ID, - */ - isFixedDeviceId: true, + isFixedDeviceId: true, // 使用固定设备ID + autoLogin: true, // 允许自动登录 }, } )); @@ -166,9 +279,7 @@ export default { nim, { addFriendNeedVerify: false, - // 是否需要显示 p2p 消息、p2p 会话列表消息已读未读,默认 false p2pMsgReceiptVisible: true, - // 是否需要显示群组消息已读未读,默认 false teamMsgReceiptVisible: true, teamAgreeMode: V2NIMConst.V2NIMTeamAgreeMode.V2NIM_TEAM_AGREE_MODE_NO_AUTH, sendMsgBefore: async (options: { msg: V2NIMMessage; conversationId: string; serverExtension?: Record }) => { @@ -178,8 +289,6 @@ export default { }); const yxAitMsg = options.serverExtension ? options.serverExtension.yxAitMsg : { forcePushIDsList: "[]", needForcePush: false }; - // 如果是 at 消息,需要走离线强推 - const { forcePushIDsList, needForcePush } = yxAitMsg ? // @ts-ignore store.msgStore._formatExtAitToPushInfo(yxAitMsg, options.msg.text) @@ -192,26 +301,21 @@ export default { const targetId = nim.V2NIMConversationIdUtil.parseConversationTargetId(conversationId); const pushPayload = JSON.stringify({ - // oppo oppoField: { - click_action_type: 4, // 参考 oppo 官网 - click_action_activity: "", // 各端不一样 TODO + click_action_type: 4, + click_action_activity: "", action_parameters: { sessionId: targetId, sessionType: conversationType, - }, // 自定义 + }, }, - - // vivo vivoField: { - pushMode: 0, //推送模式:0 表示正式推送、1 表示测试推送。不填则默认为 0。 + pushMode: 0, }, - - // huawei hwField: { click_action: { type: 1, - action: "", // 各端不一样 TODO + action: "", }, androidConfig: { category: "IM", @@ -221,8 +325,6 @@ export default { }), }, }, - - // 通用 sessionId: targetId, sessionType: conversationType, }); @@ -243,6 +345,16 @@ export default { "UniApp" )); + // 监听连接状态变化 + nim.V2NIMLoginService.on("onConnectStatusChanged", (status) => { + console.log("IM连接状态变化:", status); + // 状态1表示已断开连接 + if (status === 1) { + console.log("检测到连接断开,触发重连"); + that.reconnectIM(); + } + }); + // #ifdef APP-PLUS const nimPushPlugin = uni.requireNativePlugin("NIMUniPlugin-PluginModule"); nim.V2NIMSettingService.setOfflinePushConfig(nimPushPlugin, { @@ -264,41 +376,56 @@ export default { certificateName: "rongyaoCertificateName", }, apns: { - // certificateName: 'AuthKey_98P8URRXUD_test', //测试用: - certificateName: "AuthKey_98P8URRXUD", //正式用 + certificateName: "AuthKey_98P8URRXUD", }, }); - console.log("是否执行 推送"); + console.log("推送配置已设置"); // #endif - nim.V2NIMLoginService.login(opts.account, opts.token).then(() => {}); + // 登录IM + nim.V2NIMLoginService.login(opts.account, opts.token) + .then(() => { + console.log("IM登录成功"); + // 清除重连定时器 + if (that.globalData.reconnectTimer) { + clearInterval(that.globalData.reconnectTimer); + that.globalData.reconnectTimer = null; + } + }) + .catch((error) => { + console.error("IM登录失败", error); + // 登录失败时启动重连机制 + that.reconnectIM(); + }); + // 监听会话变化 nim.V2NIMConversationService.on("onConversationChanged", function (conversationList: V2NIMConversation[]) { console.log("初始化监听会话:", conversationList); - let _bool = conversationList.filter((item) => { + let unreadConversations = conversationList.filter((item) => { return Number(item.unreadCount) > 0; }); - console.log("初始化监听会话bool:", _bool); - if (_bool && _bool.length > 0) { - that.playAudio(); // 播放音频 + console.log("未读会话数量:", unreadConversations.length); + if (unreadConversations.length > 0) { + that.playAudio(); } }); }, + playAudio() { let that = this; const innerAudioContext = uni.createInnerAudioContext(); innerAudioContext.autoplay = true; innerAudioContext.src = "https://matripe-cms.oss-cn-beijing.aliyuncs.com/ibocai/tip.mp3"; innerAudioContext.onPlay(() => { - console.log("开始播放"); + console.log("开始播放提示音"); }); uni.vibrateShort(); innerAudioContext.onError((res) => { - console.log(res.errMsg); - console.log(res.errCode); + console.log("提示音播放错误:", res.errMsg, res.errCode); }); }, + checkNum() { let that = this; if (uni.getStorageSync("apply-token")) { @@ -308,16 +435,16 @@ export default { if (res.approvePassHasNotRed + applyNum > 0) { uni.setTabBarBadge({ index: 2, - text: String(res.approvePassHasNotRed+ applyNum), + text: String(res.approvePassHasNotRed + applyNum), fail(err) { - console.log("err", err); + console.log("设置tabBar徽章失败:", err); }, }); } else { uni.removeTabBarBadge({ index: 2, fail(err) { - console.log("err", err); + console.log("移除tabBar徽章失败:", err); }, }); } @@ -333,15 +460,14 @@ export default { @import "./static/css/iconfont.css"; @import "./uni_modules/vk-uview-ui/index.scss"; @import "./static/css/base.scss"; -// @import "./static/font/iconfont-weapp-icon.css"; // #ifdef MP-WEIXIN page { width: 100vw; height: 100vh; } - // #endif + // #ifdef H5 || APP-PLUS || MP-TOUTIAO || MP-KUAISHOU page { width: 100vw; @@ -352,7 +478,6 @@ page { --color-027: #ff4400; --color-href: #576b95; } - // #endif // uview默认样式覆盖