const $ = (sel) => document.querySelector(sel);
// Promise-based sendMessage with timeout (MV3 reliable)
function sendMsg(msg, timeoutMs = 10000) {
return new Promise((resolve) => {
let settled = false;
const timer = setTimeout(() => {
if (!settled) {
settled = true;
resolve({ error: "消息超时,请检查插件是否正常加载" });
}
}, timeoutMs);
chrome.runtime.sendMessage(msg, (res) => {
if (settled) return;
settled = true;
clearTimeout(timer);
if (chrome.runtime.lastError) {
resolve({ error: chrome.runtime.lastError.message });
} else {
resolve(res || { error: "无响应" });
}
});
});
}
function setUI(state) {
const dot = $("#statusDot");
const text = $("#statusText");
const btn = $("#btnToggle");
if (state === "loading") {
dot.className = "dot dot-gray";
text.textContent = "连接中...";
btn.textContent = "启动";
btn.className = "btn btn-primary";
btn.disabled = true;
} else if (state === "error") {
dot.className = "dot dot-red";
text.textContent = "服务未连接";
btn.textContent = "启动";
btn.className = "btn btn-primary";
btn.disabled = true;
$("#statToday").textContent = "-";
$("#statPending").textContent = "-";
$("#statFails").textContent = "-";
$("#todayCount").textContent = "- / -";
$("#connTest").innerHTML = '无法连接到后台脚本,请右键插件→重新加载';
} else if (state === "running") {
dot.className = "dot dot-green";
text.textContent = "运行中";
btn.textContent = "停止";
btn.className = "btn btn-danger";
btn.disabled = false;
} else if (state === "stopped") {
dot.className = "dot dot-gray";
text.textContent = "已停止";
btn.textContent = "启动";
btn.className = "btn btn-primary";
btn.disabled = false;
}
}
async function loadStatus() {
setUI("loading");
const status = await sendMsg({ type: "GET_STATUS" });
if (!status || status.error) {
setUI("error");
return;
}
setUI(status.polling ? "running" : "stopped");
if (status.statusError) {
$("#connTest").innerHTML = `${status.statusError}`;
} else {
$("#connTest").innerHTML = "";
}
$("#statToday").textContent = status.todayCount;
$("#statFails").textContent = status.consecutiveFailures;
const config = await sendMsg({ type: "GET_CONFIG" });
if (config && !config.error) {
$("#shopId").value = config.shopId || "";
$("#serverUrl").value = config.serverUrl || "http://localhost:3000";
const limit = config.dailyLimit || 20;
const pending = status.pendingCount || 0;
$("#statPending").textContent = pending;
$("#todayCount").textContent = `${status.todayCount} / ${limit}`;
}
}
$("#btnToggle").addEventListener("click", async () => {
const btn = $("#btnToggle");
btn.disabled = true;
btn.textContent = "处理中...";
// Save config first
await saveConfig();
// Check current status and toggle
const status = await sendMsg({ type: "GET_STATUS" });
const isRunning = status && status.polling;
if (isRunning) {
await sendMsg({ type: "STOP_POLLING" });
} else {
const res = await sendMsg({ type: "START_POLLING" });
if (res && res.error) {
console.error("启动失败:", res.error);
}
}
// Reload status to update UI
await loadStatus();
});
$("#btnTest").addEventListener("click", async () => {
const testEl = $("#connTest");
testEl.innerHTML = "测试中...";
await saveConfig();
const res = await sendMsg({ type: "TEST_CONNECTION" }, 5000);
if (res && res.ok) {
testEl.innerHTML = '✓ 连接成功';
} else {
testEl.innerHTML = `✗ ${res?.error || "连接失败,请确认本地服务已启动"}`;
}
});
function saveConfig() {
return new Promise((resolve) => {
chrome.storage.local.get(["config"], (data) => {
const existing = data.config || {};
const config = {
...existing,
serverUrl: $("#serverUrl").value.trim() || "http://localhost:3000",
shopId: $("#shopId").value.trim(),
};
chrome.storage.local.set({ config }, resolve);
});
});
}
let refreshTimer = null;
function scheduleSaveAndRefresh() {
clearTimeout(refreshTimer);
refreshTimer = setTimeout(async () => {
await saveConfig();
await loadStatus();
}, 400);
}
// 输入店铺名时要立即保存并刷新数量;change 只有失焦后才触发,容易看起来没有同步。
["shopId", "serverUrl"].forEach((id) => {
const input = $(`#${id}`);
input.addEventListener("input", scheduleSaveAndRefresh);
input.addEventListener("change", async () => {
clearTimeout(refreshTimer);
await saveConfig();
await loadStatus();
});
});
loadStatus();