207 lines
6.2 KiB
JavaScript
207 lines
6.2 KiB
JavaScript
/* =============================================
|
||
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,
|
||
};
|
||
|
||
})();
|