/* ============================================= bridge.js — WebView2 消息通信桥 统一封装所有与易语言后端的 postMessage 通信 消息格式:JSON { "命令": "...", ...参数 } 后端回调格式:JSON { "命令": "...", ...结果 } ============================================= */ window.Bridge = (function () { // ── 待回调队列(命令 → 回调列表)───────────── const _listeners = {}; /** * 向后端发送 WebView2 消息 * @param {object} payload 纯 JS 对象,会被 JSON.stringify 后发送 */ function send(payload) { try { const msg = JSON.stringify(payload); if (window.chrome && window.chrome.webview) { window.chrome.webview.postMessage(msg); } else { console.warn('[Bridge] WebView2 不可用,消息未发送:', msg); } } catch (e) { console.error('[Bridge] send 异常:', e); } } /** * 订阅来自后端的命令消息(可注册多个) * @param {string} cmd 命令名称,如 "登录结果" * @param {function} callback 回调函数,参数为完整消息对象 * @returns {function} 调用后可取消订阅 */ function on(cmd, callback) { if (!_listeners[cmd]) _listeners[cmd] = []; _listeners[cmd].push(callback); return function off() { _listeners[cmd] = (_listeners[cmd] || []).filter(fn => fn !== callback); }; } /** * 一次性订阅(触发一次后自动移除) * @param {string} cmd * @param {function} callback */ function once(cmd, callback) { const off = on(cmd, function wrapper(data) { off(); callback(data); }); } /** * 发送消息并等待指定命令回调(Promise 形式,超时 10s) * @param {object} payload 发送内容 * @param {string} replyCmd 期待后端回复的命令名 * @param {number} timeout 超时毫秒,默认 10000 */ function call(payload, replyCmd, timeout) { timeout = timeout || 10000; return new Promise(function (resolve, reject) { const timer = setTimeout(function () { _listeners[replyCmd] = (_listeners[replyCmd] || []).filter(fn => fn !== handler); reject(new Error('请求超时,请检查网络连接')); }, timeout); function handler(data) { clearTimeout(timer); resolve(data); } once(replyCmd, handler); send(payload); }); } // ── 监听后端推送的 WebView2 消息 ───────────── if (window.chrome && window.chrome.webview) { window.chrome.webview.addEventListener('message', function (e) { try { const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data; const cmd = data['命令'] || data.cmd || ''; const cbs = _listeners[cmd]; if (cbs && cbs.length) { // 复制一份,避免回调中修改 _listeners 影响遍历 cbs.slice().forEach(function (fn) { fn(data); }); } } catch (err) { console.warn('[Bridge] 解析后端消息失败:', e.data, err); } }); } else { // 非 WebView2 环境(普通浏览器调试):提供模拟接口,避免报错 console.warn('[Bridge] 未检测到 WebView2,通信桥降级为模拟模式'); window._bridgeSimReceive = function (jsonStr) { try { const data = JSON.parse(jsonStr); const cmd = data['命令'] || ''; const cbs = _listeners[cmd]; if (cbs && cbs.length) cbs.slice().forEach(fn => fn(data)); } catch (e) {} }; } // ── 窗口控制快捷方法 ───────────────────────── const WinCtrl = { /** * 最小化窗口 */ minimize: function () { send({ '命令': '窗口控制', '动作': '最小化', '手柄': 0 }); }, /** * 最大化 / 还原切换 */ toggleMaximize: function () { send({ '命令': '窗口控制', '动作': '最大化切换', '手柄': 0 }); }, /** * 关闭窗口 */ close: function () { send({ '命令': '窗口控制', '动作': '关闭', '手柄': 0 }); }, /** * 置顶 / 取消置顶切换 */ toggleTopmost: function () { send({ '命令': '窗口控制', '动作': '置顶切换', '手柄': 0 }); }, /** * 拖拽移动窗口(在 mousedown 事件中调用) */ startMove: function () { send({ '命令': '窗口控制', '动作': '移动窗口', '手柄': 0 }); }, /** * 拖拽调整窗口尺寸 * @param {number} dir 方位:1上 2下 3右 4左 5左上 6右上 7右下 8左下 */ resize: function (dir) { send({ '命令': '窗口控制', '动作': '调整尺寸', '手柄': dir }); }, }; // ── 登录相关快捷方法 ───────────────────────── const Auth = { /** * 请求自动填卡(后端回复 "自动填卡" 命令) */ requestAutoFill: function () { send({ '命令': '请求自动填卡' }); }, /** * 账号登录 * @param {string} user * @param {string} pass * @returns {Promise} 解析为 { 成功: bool, 消息: string, ... } */ login: function (user, pass) { return call( { '命令': '账号登录', '账号': user, '密码': pass }, '登录结果' ); }, /** * 注册账号 */ register: function (user, pass, sec, card) { return call( { '命令': '注册账号', '账号': user, '密码': pass, '密保': sec, '卡密': card }, '注册结果' ); }, /** * 续费账号 */ renew: function (user, card) { return call( { '命令': '续费账号', '账号': user, '卡密': card }, '续费结果' ); }, /** * 修改密码 */ changePwd: function (user, sec, newPass) { return call( { '命令': '修改密码', '账号': user, '密保': sec, '新密码': newPass }, '改密结果' ); }, }; return { send: send, on: on, once: once, call: call, WinCtrl: WinCtrl, Auth: Auth, }; })();