/* ============================================================ * 微信工作台 — 登录 / 在线检测 / 登出模块 * 依赖:core.js(window.Core)、wx.js(window.WxN) * ============================================================ */ (function (global) { "use strict"; var Core = global.Core; var WxN = global.WxN; var $ = function (sel, root) { return (root || document).querySelector(sel); }; function state() { return WxN.state; } // 登录地区列表 var REGION_LIST = [ { id: "110000", name: "北京市" }, { id: "120000", name: "天津市" }, { id: "130000", name: "河北省" }, { id: "140000", name: "山西省" }, { id: "210000", name: "辽宁省" }, { id: "220000", name: "吉林省" }, { id: "230000", name: "黑龙江省" }, { id: "310000", name: "上海市" }, { id: "320000", name: "江苏省" }, { id: "330000", name: "浙江省" }, { id: "340000", name: "安徽省" }, { id: "350000", name: "福建省" }, { id: "360000", name: "江西省" }, { id: "370000", name: "山东省" }, { id: "410000", name: "河南省" }, { id: "420000", name: "湖北省" }, { id: "430000", name: "湖南省" }, { id: "440000", name: "广东省" }, { id: "450000", name: "广西省" }, { id: "460000", name: "海南省" }, { id: "500000", name: "重庆市" }, { id: "510000", name: "四川省" }, { id: "520000", name: "贵州省" }, { id: "530000", name: "云南省" }, { id: "540000", name: "西藏" }, { id: "610000", name: "陕西省" }, { id: "620000", name: "甘肃省" }, { id: "630000", name: "青海省" }, { id: "640000", name: "宁夏" } ]; /* ==================== 登录卡片 ==================== */ function renderLoginCard() { var s = state(); var box = $("#wxLoginCard", s.container); var app = s.app; if ((!app || !app.appId) || s.qr) { var qr = s.qr; var qrImg = qr && qr.img ? '微信登录二维码' : '
▣
点击下方按钮获取登录二维码
'; var statusText = "等待获取二维码"; if (qr) { statusText = qr.status === 0 ? "请使用微信扫码" : qr.status === 1 ? "已扫码,请在手机上确认登录" : "登录成功"; if (qr.expired) statusText = "二维码已过期,请重新获取"; } box.innerHTML = '
' + '
微信未登录
' + '
' + qrImg + '
' + statusText + "
" + '" + "
"; $("#wxQrBtn", box).onclick = getQrCode; return; } box.innerHTML = '
' + '
' + (app.headImgUrl ? '' : '
' + Core.esc(Core.initial(app.nickName || app.wxid)) + "
") + '
' + '
' + Core.esc(app.nickName || "微信用户") + "
" + '
' + Core.esc(app.wxid || app.appId) + "
" + "
" + (s.online ? '在线' : '离线') + "
" + '
' + '' + '' + "
" + "
"; $("#wxOnlineBtn", box).onclick = function () { checkOnline().then(function () { renderLoginCard(); }); }; $("#wxLogoutBtn", box).onclick = doLogout; } /* ---------- 扫码登录 ---------- */ function showRegionDialog() { return new Promise(function (resolve) { var optionsHtml = REGION_LIST.map(function (r) { return ''; }).join(''); var m = Core.modal({ title: '选择登录地区', body: '
请选择您当前所在的省市地区,用于微信登录接入。
' + '
' + '
' + '
' + '· 请选择最近的省市,若没有您的地区可自行采购 socks5 代理 IP
' + '· 平台代理仅做测试,长用建议自己采购独占代理
' + '
', footer: '' }); $('.rg-cancel', m.root).onclick = function () { m.close(); resolve(null); }; $('.rg-ok', m.root).onclick = function () { var regionId = $('#wx-region-select', m.root).value; m.close(); resolve(regionId); }; var geoHint = $('#wx-geo-hint', m.root); if (navigator.geolocation && geoHint) { geoHint.textContent = '正在尝试定位…'; navigator.geolocation.getCurrentPosition( function (pos) { var lat = pos.coords.latitude; var lng = pos.coords.longitude; var guessed = guessRegionByCoords(lat, lng); if (guessed) { $('#wx-region-select', m.root).value = guessed; geoHint.innerHTML = '已自动定位到 ' + Core.esc(REGION_LIST.filter(function(r){return r.id===guessed;})[0].name) + ',可手动修改'; } else { geoHint.textContent = '定位成功但无法匹配地区,请手动选择'; } }, function () { geoHint.textContent = '无法获取定位,请手动选择地区'; }, { timeout: 5000 } ); } }); } function guessRegionByCoords(lat, lng) { var coords = [ { id: "110000", lat: 39.9, lng: 116.4 }, { id: "120000", lat: 39.1, lng: 117.2 }, { id: "310000", lat: 31.2, lng: 121.5 }, { id: "440000", lat: 23.1, lng: 113.3 }, { id: "330000", lat: 30.3, lng: 120.2 }, { id: "320000", lat: 32.1, lng: 118.8 }, { id: "510000", lat: 30.6, lng: 104.1 }, { id: "420000", lat: 30.6, lng: 114.3 }, { id: "430000", lat: 28.2, lng: 112.9 }, { id: "370000", lat: 36.7, lng: 117.0 }, { id: "410000", lat: 34.8, lng: 113.7 }, { id: "130000", lat: 38.0, lng: 114.5 }, { id: "210000", lat: 41.8, lng: 123.4 }, { id: "610000", lat: 34.3, lng: 108.9 }, { id: "340000", lat: 31.8, lng: 117.3 }, { id: "350000", lat: 26.1, lng: 119.3 }, { id: "360000", lat: 28.7, lng: 115.9 }, { id: "500000", lat: 29.6, lng: 106.5 }, { id: "530000", lat: 25.0, lng: 102.7 }, { id: "520000", lat: 26.6, lng: 106.7 }, { id: "450000", lat: 22.8, lng: 108.3 }, { id: "460000", lat: 20.0, lng: 110.3 }, { id: "140000", lat: 37.9, lng: 112.5 }, { id: "220000", lat: 43.9, lng: 125.3 }, { id: "230000", lat: 45.8, lng: 126.5 }, { id: "620000", lat: 36.1, lng: 103.8 }, { id: "630000", lat: 36.6, lng: 101.8 }, { id: "640000", lat: 38.5, lng: 106.3 }, { id: "540000", lat: 29.6, lng: 91.1 } ]; var minDist = Infinity, matched = null; for (var i = 0; i < coords.length; i++) { var d = Math.pow(lat - coords[i].lat, 2) + Math.pow(lng - coords[i].lng, 2); if (d < minDist) { minDist = d; matched = coords[i].id; } } return matched; } function getQrCode() { stopLoginPolling(); showRegionDialog().then(function (regionId) { if (!regionId) return; Core.loading(true, "正在获取登录二维码…"); Core.wxApi("login/getLoginQrCode", { appId: "", regionId: regionId, type: "mac" }) .then(function (data) { Core.loading(false); if (!data || !data.uuid) { Core.toast("获取二维码失败", "err"); return; } state().qr = { uuid: data.uuid, img: data.qrImgBase64, status: 0, expired: false, appId: data.appId || "", regionId: regionId }; // 将接口返回的 appId 更新到销售的 wx_device_id var hashMatch = (location.hash || '').match(/wx-advicer=(\d+)/); var advicerId = hashMatch ? Number(hashMatch[1]) : 0; if (data.appId && advicerId) { Core.api("advicer/wx_device/update", { advicerId: advicerId, wxDeviceId: data.appId }) .then(function () { console.log("[getQrCode] wx_device_id updated: " + data.appId); }) .catch(function (e) { console.warn("[getQrCode] update wx_device_id failed:", e); }); } renderLoginCard(); startLoginPolling(); }) .catch(function (e) { Core.loading(false); Core.toast("获取二维码失败:" + e.message, "err"); }); }); } function startLoginPolling() { stopLoginPolling(); var s = state(); s.loginTimer = setInterval(function () { s = state(); if (!s || !s.qr) return; Core.wxApi("login/checkLogin", { appId: s.qr.appId || "", uuid: s.qr.uuid, autoSliding: true, proxyIp: "", captchCode: "" }).then(function (data) { if (!data) return; if (s.qr) { s.qr.status = data.status; if (typeof data.expiredTime === "number" && data.status !== 2 && data.expiredTime <= 0) { s.qr.expired = true; } } if (data.status === 2) { stopLoginPolling(); var app = { appId: s.qr.appId || "", wxid: data.loginInfo ? data.loginInfo.wxid : "", nickName: data.nickName || (data.loginInfo && data.loginInfo.nickName) || "", headImgUrl: data.headImgUrl || "" }; s.app = app; Core.store(Core.KEYS.wxApp, app); s.qr = null; Core.toast("微信登录成功:" + (app.nickName || app.wxid), "ok"); renderLoginCard(); checkOnlineLoop(); // 登录成功后回写 wx_id 到销售记录 var hashMatch2 = (location.hash || '').match(/wx-advicer=(\d+)/); var advicerId2 = hashMatch2 ? Number(hashMatch2[1]) : 0; if (advicerId2 && app.wxid) { Core.api("advicer/wx_id/update", { advicerId: advicerId2, wxId: app.wxid }) .then(function () { console.log("[login] wx_id updated: " + app.wxid); }) .catch(function (e) { console.warn("[login] update wx_id failed:", e); }); } WxN.loadProfile().then(function () { WxN.loadConversations(); }); WxN.loadContacts(); WxN.loadLabels(); } else { renderLoginCard(); } }).catch(function () { }); }, 10000); } function stopLoginPolling() { var s = state(); if (s && s.loginTimer) { clearInterval(s.loginTimer); s.loginTimer = null; } } function checkOnlineLoop() { WxN.connectWs(); checkOnline(); } function checkOnline() { var s = state(); if (!s || !s.app || !s.app.appId) { console.log("[checkOnline] skipped: state=" + !!s + ", app=" + (s && s.app ? s.app.appId : "null")); return Promise.resolve(false); } console.log("[checkOnline] calling with appId=" + s.app.appId); return Core.wxApi("login/checkOnline", { appId: s.app.appId }) .then(function (v) { s.online = v === true; renderLoginCard(); return s.online; }) .catch(function () { state().online = false; renderLoginCard(); return false; }); } function resetToOffline() { WxN.disconnectWs(); var s = state(); s.app = null; s.online = false; s.contacts = { friends: [], chatrooms: [], ghs: [] }; s.conversations = []; s.convMsgs = {}; s.brief = {}; s.msgs = {}; s.active = null; Core.store(Core.KEYS.wxApp, null); if (location.hash) history.replaceState(null, '', location.pathname + location.search); WxN.clearBriefCache(); renderLoginCard(); WxN.renderContacts(); WxN.renderChat(); } /* ---------- 在线状态 WebSocket 事件 ---------- */ function handleOnlineStatus(data) { if (!data) return; var s = state(); var wasOnline = s.online; s.online = data.online === true; if (!s.online && wasOnline) { Core.toast("微信已离线", "warn"); renderLoginCard(); } else if (s.online && !wasOnline) { Core.toast("微信已恢复在线", "ok"); renderLoginCard(); } else { renderLoginCard(); } } function doLogout() { var s = state(); Core.confirm("确定退出微信登录?退出后需重新扫码。").then(function (ok) { if (!ok) return; Core.wxApi("login/logout", { appId: state().app.appId }) .then(function () { Core.toast("已退出微信登录", "ok"); resetToOffline(); }) .catch(function (e) { Core.wxApi("login/checkOnline", { appId: state().app.appId }) .then(function (online) { if (online !== true) { Core.toast("微信已离线,自动回到未登录状态", "warn"); resetToOffline(); } else { Core.toast("退出登录失败:" + e.message, "err"); } }) .catch(function () { Core.toast("退出登录失败:" + e.message, "err"); }); }); }); } /* ---------- 消息回调设置 ---------- */ function openCallbackDialog() { Core.loading(true, "获取回调配置…"); Core.api("wx/callback/info", {}).then(function (info) { Core.loading(false); var suggested = (info && info.callbackUrl) || (location.origin + "/api/v1/advicer/wx/callback"); var m = Core.modal({ title: "设置消息回调", body: '
' + '
' + '
' + "· 需为公网可访问地址,微信协议服务会把客户与销售的消息推送到该地址;
" + "· 配置成功后消息自动入库,用于客户画像迭代、每晚等级评估与 AI 主动对话;
" + "· 若服务器部署在反向代理后,请把域名替换为实际公网访问地址。" + "
", footer: '' }); $(".mf-cancel", m.root).onclick = m.close; $(".mf-save", m.root).onclick = function () { var url = $("#wcb-url", m.root).value.trim(); if (!url) { Core.toast("请输入回调地址", "err"); return; } Core.loading(true, "正在设置回调…"); Core.api("wx/callback/set", { appId: (state().app && state().app.appId) || "", callbackUrl: url }) .then(function () { Core.loading(false); m.close(); Core.toast("回调地址已设置并生效", "ok"); }) .catch(function (e) { Core.loading(false); Core.toast("设置失败:" + e.message, "err"); }); }; }).catch(function (e) { Core.loading(false); Core.toast(e.message, "err"); }); } /* ==================== 导出 ==================== */ WxN.renderLoginCard = renderLoginCard; WxN.getQrCode = getQrCode; WxN.checkOnline = checkOnline; WxN.checkOnlineLoop = checkOnlineLoop; WxN.resetToOffline = resetToOffline; WxN.handleOnlineStatus = handleOnlineStatus; WxN.doLogout = doLogout; WxN.openCallbackDialog = openCallbackDialog; WxN.stopLoginPolling = stopLoginPolling; })(window);