feat: 增加顺运宝批量 AI 规格匹配 (#202)

This commit is contained in:
chengma
2026-08-14 10:30:08 +08:00
parent 0201dab98c
commit d64579e9b3
26 changed files with 1289 additions and 26 deletions
+16 -2
View File
@@ -208,8 +208,14 @@ body.syb-page-body > .page {
min-width: 0;
}
.syb-toolbar .syb-filter-form select {
width: 138px;
min-width: 138px;
width: 125px;
min-width: 125px;
}
.syb-toolbar input[type="text"].syb-search-input,
.syb-toolbar textarea.syb-search-input {
flex-basis: 110px;
width: 110px;
min-width: 110px;
}
}
/* 蝦皮工具条同时放商品和店铺两个搜索框,各占剩余工具条的约 20%。
@@ -609,6 +615,14 @@ input.wide { width: 100%; }
}
.status-active { color: #1a7f37; background: #f0fff4; }
.status-disabled { color: #777; background: #f3f4f6; }
.mapping-source { white-space: nowrap; font-size: 12px; }
.source-ai { color: #6f42c1; background: #f7f0ff; }
.source-rule { color: #0969da; background: #eef6ff; }
.source-manual { color: #57606a; background: #f6f8fa; }
.ai-match-result-modal progress { width: 100%; height: 16px; }
.ai-match-summary { margin: 0; }
.ai-match-items-wrap { max-height: min(420px, 52vh); overflow: auto; }
.ai-match-items-wrap th { position: sticky; top: 0; z-index: 1; }
select {
padding: 5px 8px;
+120
View File
@@ -626,6 +626,124 @@
});
}
function setupAIMatchCreate() {
var openButton = document.querySelector("[data-ai-match-open]");
var form = document.getElementById("syb-ai-match-form");
var modal = document.getElementById("syb-ai-match-create-modal");
if (!openButton || !form || !modal) return;
var count = modal.querySelector("[data-ai-match-count]");
var submit = modal.querySelector("[data-ai-match-submit]");
openButton.addEventListener("click", function () {
form.querySelectorAll("input[data-ai-match-dynamic]").forEach(function (input) { input.remove(); });
var selected = [];
document.querySelectorAll("tbody input[name=ids]:checked:not(:disabled)").forEach(function (box) {
if (supportsSelectAction(box, "ai")) selected.push(box.value);
});
selected.forEach(function (id) {
var input = document.createElement("input");
input.type = "hidden";
input.name = "ids";
input.value = id;
input.setAttribute("data-ai-match-dynamic", "true");
form.appendChild(input);
});
if (count) count.textContent = String(selected.length);
if (submit) submit.disabled = selected.length === 0 || selected.length > 100;
});
form.addEventListener("submit", function () {
if (!submit) return;
submit.disabled = true;
submit.setAttribute("aria-busy", "true");
submit.textContent = "正在创建批次…";
});
}
function setupAIMatchBatch() {
var modal = document.querySelector("[data-ai-match-batch]");
if (!modal) return;
var fetchURL = modal.getAttribute("data-ai-match-fetch-url");
var refresh = modal.querySelector("[data-ai-match-refresh]");
var pageRefresh = modal.querySelector("[data-ai-match-page-refresh]");
var status = modal.querySelector("[data-ai-batch-status]");
var progress = modal.querySelector("[data-ai-batch-progress]");
var itemBody = modal.querySelector("[data-ai-batch-items]");
var errorBox = modal.querySelector("[data-ai-batch-error]");
var timer = null;
function setCount(name, value) {
var target = modal.querySelector('[data-ai-count="' + name + '"]');
if (target) target.textContent = String(value);
}
function render(data) {
if (status) status.textContent = data.status_text || data.status || "未知状态";
if (progress) {
progress.max = Math.max(1, Number(data.total) || 1);
progress.value = Number(data.processed) || 0;
progress.textContent = String(data.processed || 0) + " / " + String(data.total || 0);
}
["total", "processed", "success", "reused", "manual", "failed"].forEach(function (name) {
setCount(name, Number(data[name]) || 0);
});
if (errorBox) {
errorBox.textContent = data.error_message || "";
errorBox.hidden = !data.error_message;
}
if (itemBody) {
itemBody.textContent = "";
(data.items || []).forEach(function (item) {
var row = document.createElement("tr");
[item.syb_id, item.status_text, item.source_text || "—", item.confidence_text || "—", item.message || "—"].forEach(function (value) {
var cell = document.createElement("td");
cell.textContent = value || "—";
row.appendChild(cell);
});
itemBody.appendChild(row);
});
}
if (data.running) {
timer = window.setTimeout(load, 1500);
}
}
function load() {
if (!fetchURL || modal.getAttribute("aria-busy") === "true") return;
modal.setAttribute("aria-busy", "true");
if (refresh) {
refresh.disabled = true;
refresh.textContent = "刷新中…";
}
fetch(fetchURL, { cache: "no-store", headers: { "Accept": "application/json" } })
.then(function (response) {
if (!response.ok) throw new Error("HTTP " + response.status);
return response.json();
})
.then(render)
.catch(function (error) {
if (errorBox) {
errorBox.textContent = "刷新 AI 匹配进度失败:" + error.message + "。可以稍后重试。";
errorBox.hidden = false;
}
})
.finally(function () {
modal.removeAttribute("aria-busy");
if (refresh) {
refresh.disabled = false;
refresh.textContent = "刷新进度";
}
});
}
if (refresh) refresh.addEventListener("click", function () {
if (timer !== null) window.clearTimeout(timer);
load();
});
if (pageRefresh) pageRefresh.addEventListener("click", function () { window.location.reload(); });
load();
}
document.addEventListener("DOMContentLoaded", function () {
setupModuleNavigationState();
document.querySelectorAll("table").forEach(setupCheckAll);
@@ -644,6 +762,8 @@
setupSybSyncFeedback();
setupSybHistoryRefresh();
setupSybOrderSearch();
setupAIMatchCreate();
setupAIMatchBatch();
syncButtons();
});