Files
LWLT-AIBOT/chrome-extension/ltjt-order-assistant/popup.js
2026-08-29 15:02:51 +08:00

229 lines
8.0 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.

'use strict';
const BUSINESS_SOURCE = 'LIANSYN_PLATFORM';
const EXTENSION_SOURCE = 'LTJT_ORDER_ASSISTANT_EXTENSION';
const PRODUCTION_PLATFORM_ORIGIN = 'https://lwlt.nianxx.cn';
const $ = (selector) => document.querySelector(selector);
function setText(selector, text) {
const el = $(selector);
if (el) el.textContent = text;
}
function setBadge(text, mode = '') {
const badge = $('#stateBadge');
if (!badge) return;
badge.textContent = text;
badge.classList.toggle('bad', mode === 'bad');
badge.classList.toggle('paused', mode === 'paused');
}
function isBusinessSystemUrl(url) {
try {
const parsed = new URL(String(url || ''));
if (parsed.origin === PRODUCTION_PLATFORM_ORIGIN) return true;
return parsed.protocol === 'http:'
&& ['localhost', '127.0.0.1'].includes(parsed.hostname)
&& ['', '8765', '8786'].includes(parsed.port);
} catch (error) {
return false;
}
}
function friendlyError(error) {
const message = error?.message || String(error || '');
if (/Extension context invalidated/i.test(message)) {
return '插件刚重新加载,旧页面桥接已失效。请刷新业务系统页面后重新打开本面板。';
}
return message;
}
async function findBusinessTab() {
const tabs = await chrome.tabs.query({});
return tabs.find((tab) => isBusinessSystemUrl(tab.url)) || null;
}
async function pingBusinessBridge(tabId) {
await chrome.scripting.executeScript({
target: { tabId },
files: ['business-bridge.js']
});
const [probe] = await chrome.scripting.executeScript({
target: { tabId },
func: async (businessSource, extensionSource) => {
const requestId = `POPUP-PING-${Date.now()}-${Math.random().toString(16).slice(2)}`;
return new Promise((resolve) => {
let staleError = null;
const timer = window.setTimeout(() => {
window.removeEventListener('message', onMessage);
resolve(staleError || {
ok: false,
message: '业务系统页面未收到插件桥接响应。'
});
}, 1500);
function onMessage(event) {
if (event.source !== window) return;
const message = event.data || {};
if (message.source !== extensionSource || message.requestId !== requestId) return;
if (message.type === 'ERROR') {
const errorMessage = String(message.payload?.message || '');
if (/Extension context invalidated/i.test(errorMessage)) {
// An extension reload can leave old content-script handlers in the
// page. Keep waiting for the current bridge's valid PONG.
staleError = { ok: false, message: errorMessage };
return;
}
}
if (message.type !== 'PONG' || message.payload?.ok === false) return;
window.clearTimeout(timer);
window.removeEventListener('message', onMessage);
resolve(message.payload || { ok: false, message: '插件桥接返回为空。' });
}
window.addEventListener('message', onMessage);
window.postMessage({
source: businessSource,
requestId,
type: 'PING',
payload: {}
}, window.location.origin);
});
},
args: [BUSINESS_SOURCE, EXTENSION_SOURCE]
});
return probe?.result || {
ok: false,
message: '无法读取业务系统桥接检测结果。'
};
}
async function detectBusinessConnection() {
const tab = await findBusinessTab();
if (!tab?.id) {
return {
connected: false,
bridge: null,
text: '未打开业务系统页面',
hint: '请打开生产控制平面或 http://127.0.0.1:8786/,然后刷新本面板。'
};
}
try {
const bridge = await pingBusinessBridge(tab.id);
if (!bridge.ok) {
return {
connected: false,
bridge,
text: '业务系统未联通',
hint: bridge.message || '插件桥接未响应。'
};
}
return {
connected: true,
bridge,
text: '已联通',
hint: '业务系统页面已收到插件桥接响应。'
};
} catch (error) {
return {
connected: false,
bridge: null,
text: '业务系统未联通',
hint: friendlyError(error)
};
}
}
async function readAutomationEnabled() {
const saved = await chrome.storage.local.get('erpAutomationEnabled');
return saved.erpAutomationEnabled !== false;
}
async function readKeepaliveEnabled() {
const saved = await chrome.storage.local.get('erpKeepaliveEnabled');
return saved.erpKeepaliveEnabled !== false;
}
function keepaliveStatusText(connection, automationEnabled, userEnabled) {
if (!automationEnabled) return '随“允许插件操作 ERP”关闭。';
if (!userEnabled) return '已关闭;插件不会发送待机保活请求。';
const keepalive = connection.bridge?.erp_session?.erp_keepalive;
if (['scheduled', 'already_running', 'running'].includes(keepalive?.recovery_status)) {
return '检测到 ERP 链路异常,正在刷新现有 ERP 页面并进行只读复测。';
}
if (keepalive?.recovery_status === 'success' && keepalive.last_recovered_at) {
return `已自动刷新并恢复 ERP 链路:${new Date(keepalive.last_recovered_at).toLocaleString()}`;
}
if (keepalive?.recovery_status === 'failed' && keepalive.recovery_error) {
return `ERP 链路异常,已尝试刷新但仍未恢复:${keepalive.recovery_error}`;
}
if (keepalive?.last_status === 'success' && keepalive.last_success_at) {
return `已开启;最近一次成功:${new Date(keepalive.last_success_at).toLocaleString()}`;
}
if (keepalive?.last_status === 'session_expired') return '已开启,但最近一次检测发现 ERP 会话已失效,请重新登录。';
if (keepalive?.last_status === 'failed') return `已开启;最近一次检测失败:${keepalive.last_error || '网络或页面不可用'}`;
return '已开启;约每 5 分钟发送一次只读请求,不刷新当前页面。';
}
async function updateStatusPanel() {
setBadge('检测中');
setText('#versionLabel', `v${chrome.runtime.getManifest().version}`);
const [enabled, keepaliveEnabled, connection] = await Promise.all([
readAutomationEnabled(),
readKeepaliveEnabled(),
detectBusinessConnection()
]);
const toggle = $('#erpAutomationToggle');
if (toggle) toggle.checked = enabled;
const keepaliveToggle = $('#erpKeepaliveToggle');
if (keepaliveToggle) {
keepaliveToggle.checked = keepaliveEnabled;
keepaliveToggle.disabled = !enabled;
}
setText('#keepaliveText', keepaliveStatusText(connection, enabled, keepaliveEnabled));
if (!enabled) {
setBadge('已关闭', 'paused');
setText('#connectionText', connection.connected ? '已联通ERP 操作已关闭' : '未联通ERP 操作已关闭');
setText('#connectionHint', connection.connected
? '业务系统已连接;插件不会打开、填写或提交 ERP。'
: connection.hint);
return;
}
setBadge(connection.connected ? '已联通' : '未联通', connection.connected ? '' : 'bad');
setText('#connectionText', connection.connected ? '已联通ERP 操作已开启' : connection.text);
setText('#connectionHint', connection.connected
? '插件允许操作 ERP。任务创建和进度跟踪请在业务系统中完成。'
: connection.hint);
}
document.addEventListener('DOMContentLoaded', async () => {
const toggle = $('#erpAutomationToggle');
if (toggle) {
toggle.checked = await readAutomationEnabled();
toggle.addEventListener('change', async (event) => {
await chrome.storage.local.set({ erpAutomationEnabled: event.currentTarget.checked });
await updateStatusPanel();
});
}
const keepaliveToggle = $('#erpKeepaliveToggle');
if (keepaliveToggle) {
keepaliveToggle.checked = await readKeepaliveEnabled();
keepaliveToggle.addEventListener('change', async (event) => {
await chrome.storage.local.set({ erpKeepaliveEnabled: event.currentTarget.checked });
await updateStatusPanel();
});
}
await updateStatusPanel();
window.setTimeout(() => updateStatusPanel().catch(() => {}), 800);
});