'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); });