V10.606 正名 PChome 業績成長作戰系統
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{% extends 'ewoooc_base.html' %}
|
||||
{% block title %}AI 競情中樞 · EwoooC{% endblock %}
|
||||
{% block title %}PChome 業績成長自動化作戰系統 · EwoooC{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
@@ -233,6 +233,97 @@
|
||||
background: rgba(250, 247, 240, 0.54);
|
||||
}
|
||||
|
||||
.growth-ops-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.7fr);
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.growth-metric-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.growth-metric {
|
||||
border: 1px solid var(--momo-border-subtle);
|
||||
border-radius: 8px;
|
||||
background: rgba(250, 247, 240, 0.62);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.growth-metric strong {
|
||||
display: block;
|
||||
color: var(--momo-text-strong);
|
||||
font-family: var(--momo-font-mono);
|
||||
font-size: 1.35rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.growth-metric span {
|
||||
color: var(--momo-text-muted);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.growth-source-note {
|
||||
margin-top: 10px;
|
||||
color: var(--momo-text-muted);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.growth-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
max-height: 292px;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.growth-item {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
border: 1px solid rgba(42, 37, 32, 0.1);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.growth-item-title {
|
||||
margin: 0;
|
||||
color: var(--momo-text-strong);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.growth-item-meta,
|
||||
.growth-item-reason {
|
||||
margin: 4px 0 0;
|
||||
color: var(--momo-text-muted);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.growth-action-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 92px;
|
||||
border: 1px solid rgba(42, 37, 32, 0.12);
|
||||
border-radius: 999px;
|
||||
background: rgba(242, 178, 90, 0.2);
|
||||
color: var(--momo-text-strong);
|
||||
font-size: 0.74rem;
|
||||
font-weight: 900;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.ai-intel-hero {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -332,8 +423,8 @@
|
||||
#competitorTable td:nth-child(2)::before { content: "MOMO"; }
|
||||
#competitorTable td:nth-child(3)::before { content: "PChome"; }
|
||||
#competitorTable td:nth-child(4)::before { content: "價差"; }
|
||||
#competitorTable td:nth-child(5)::before { content: "標籤"; }
|
||||
#competitorTable td:nth-child(6)::before { content: "分數"; }
|
||||
#competitorTable td:nth-child(5)::before { content: "狀態"; }
|
||||
#competitorTable td:nth-child(6)::before { content: "可信度"; }
|
||||
#competitorTable td:nth-child(7)::before { content: "更新"; }
|
||||
|
||||
.ai-panel .card-footer {
|
||||
@@ -341,6 +432,15 @@
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.growth-ops-grid,
|
||||
.growth-metric-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.growth-item {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@@ -353,23 +453,23 @@
|
||||
<div>
|
||||
<h1 class="ai-intel-title">
|
||||
<i class="fas fa-brain"></i>
|
||||
AI 競情中樞
|
||||
<span class="ai-intel-badge">ICAIM</span>
|
||||
PChome 業績成長自動化作戰系統
|
||||
<span class="ai-intel-badge">AI 競情中樞</span>
|
||||
</h1>
|
||||
<p class="ai-intel-subtitle">Hermes 3 分析師 × NemoTron 派發器,使用資料庫內的競品價格、AI 決策與 PChome 比對記錄進行監控。</p>
|
||||
<p class="ai-intel-subtitle">把 PChome 後台業績、MOMO 外部價格參考與商品對應狀態整理成每天可處理的作戰清單。</p>
|
||||
</div>
|
||||
<div class="ai-intel-actions">
|
||||
<span id="lastUpdateBadge" class="ai-status-badge">
|
||||
<i class="fas fa-sync me-1"></i>載入中...
|
||||
</span>
|
||||
<button class="btn btn-outline-danger btn-sm ai-action-btn" id="btnTrigger" onclick="triggerAnalysis()">
|
||||
<i class="fas fa-bolt me-1"></i>立即分析
|
||||
<i class="fas fa-bolt me-1"></i>整理建議
|
||||
</button>
|
||||
<button class="btn btn-outline-primary btn-sm ai-action-btn" id="btnPickList" onclick="generatePickList()">
|
||||
<i class="fas fa-wand-magic-sparkles me-1"></i>產生挑品清單
|
||||
<i class="fas fa-wand-magic-sparkles me-1"></i>產生作戰商品
|
||||
</button>
|
||||
<button class="btn btn-outline-warning btn-sm ai-action-btn" id="btnBackfill" onclick="backfillPchomeMatches()">
|
||||
<i class="fas fa-magnifying-glass-chart me-1"></i>補抓未搜尋
|
||||
<i class="fas fa-magnifying-glass-chart me-1"></i>補商品對應
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm ai-action-btn" onclick="loadDashboard()">
|
||||
<i class="fas fa-redo me-1"></i>重新整理
|
||||
@@ -377,6 +477,47 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── PChome 成長作戰清單 ── -->
|
||||
<section class="card shadow-sm ai-panel" id="growthOpsPanel">
|
||||
<div class="card-header d-flex justify-content-between align-items-center py-2 bg-white border-bottom">
|
||||
<span class="ai-panel-title">
|
||||
<i class="fas fa-compass"></i>PChome 成長作戰
|
||||
<small class="text-muted fw-normal ms-2">先處理最可能影響業績的商品</small>
|
||||
</span>
|
||||
<button class="btn btn-outline-secondary btn-sm ai-action-btn" onclick="loadGrowthOps(true)">
|
||||
<i class="fas fa-redo me-1"></i>更新清單
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="growth-ops-grid">
|
||||
<div>
|
||||
<div class="growth-metric-row">
|
||||
<div class="growth-metric">
|
||||
<strong id="growthCandidateCount">—</strong>
|
||||
<span>作戰商品</span>
|
||||
</div>
|
||||
<div class="growth-metric">
|
||||
<strong id="growthMappedCount">—</strong>
|
||||
<span>可直接比價</span>
|
||||
</div>
|
||||
<div class="growth-metric">
|
||||
<strong id="growthNeedsMapping">—</strong>
|
||||
<span>待補對應</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="growth-source-note" id="growthSourceNote">來源整理中...</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="growth-list" id="growthOpsList">
|
||||
<div class="text-center py-4 text-muted">
|
||||
<div class="spinner-border spinner-border-sm me-2"></div>整理作戰清單中...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── KPI 卡片 ── -->
|
||||
<div class="row g-3 mb-4" id="kpiRow">
|
||||
<div class="col-6 col-md-3">
|
||||
@@ -392,7 +533,7 @@
|
||||
<div class="card-body text-center py-3">
|
||||
<div class="fs-2 fw-bold text-success" id="kpiCompetitors">—</div>
|
||||
<div class="small text-muted mt-1">
|
||||
<i class="fas fa-store me-1"></i>有效競品比價
|
||||
<i class="fas fa-store me-1"></i>可直接比價
|
||||
<span id="kpiMatchRate" class="text-muted" style="font-size:0.7rem"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -404,8 +545,7 @@
|
||||
<div class="card-body text-center py-3">
|
||||
<div class="fs-2 fw-bold text-danger" id="kpiHighRisk">—</div>
|
||||
<div class="small text-muted mt-1">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>高風險商品
|
||||
<span class="text-muted" style="font-size:0.7rem">(貴>15%)</span>
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>需檢查價格
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -414,7 +554,7 @@
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center py-3">
|
||||
<div class="fs-2 fw-bold text-info" id="kpiAiRecs">—</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-robot me-1"></i>AI 挑品/決策記錄</div>
|
||||
<div class="small text-muted mt-1"><i class="fas fa-robot me-1"></i>作戰建議紀錄</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -423,12 +563,12 @@
|
||||
<!-- ── 主體分兩欄(競品比價 + AI 決策) ── -->
|
||||
<div class="row g-3">
|
||||
|
||||
<!-- ── 左:PChome 競品比價 ── -->
|
||||
<!-- ── 左:外部價格參考 ── -->
|
||||
<div class="col-xl-7">
|
||||
<div class="card shadow-sm h-100 ai-panel">
|
||||
<div class="card-header d-flex justify-content-between align-items-center py-2 bg-white border-bottom">
|
||||
<span class="ai-panel-title">
|
||||
<i class="fas fa-balance-scale text-warning me-2"></i>PChome 競品比價監控
|
||||
<i class="fas fa-balance-scale text-warning me-2"></i>MOMO 外部價格參考
|
||||
</span>
|
||||
<div class="d-flex gap-2 align-items-center">
|
||||
<select class="form-select form-select-sm" id="riskFilter" onchange="filterTable()" style="width:100px">
|
||||
@@ -455,8 +595,8 @@
|
||||
<th class="text-end" style="min-width:75px">MOMO</th>
|
||||
<th class="text-end" style="min-width:75px">PChome</th>
|
||||
<th class="text-end" style="min-width:70px">價差</th>
|
||||
<th style="min-width:90px">競品標籤</th>
|
||||
<th class="text-center" style="min-width:55px">分數</th>
|
||||
<th style="min-width:90px">比價狀態</th>
|
||||
<th class="text-center" style="min-width:55px">可信度</th>
|
||||
<th class="text-muted" style="min-width:80px">更新</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -469,18 +609,18 @@
|
||||
</div>
|
||||
<div class="card-footer bg-white py-2 d-flex justify-content-between small text-muted">
|
||||
<span id="compCount">—</span>
|
||||
<span>僅顯示已通過身份比對的競品</span>
|
||||
<span>僅顯示已確認同款的商品</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 右:AI 決策日誌 ── -->
|
||||
<!-- ── 右:作戰建議紀錄 ── -->
|
||||
<div class="col-xl-5">
|
||||
<div class="card shadow-sm h-100 ai-panel">
|
||||
<div class="card-header py-2 bg-white border-bottom">
|
||||
<span class="ai-panel-title">
|
||||
<i class="fas fa-robot text-danger me-2"></i>AI 決策日誌
|
||||
<small class="text-muted fw-normal ms-2">挑品 Agent × Hermes × NemoTron</small>
|
||||
<i class="fas fa-robot text-danger me-2"></i>作戰建議紀錄
|
||||
<small class="text-muted fw-normal ms-2">挑品、比價與人工覆核</small>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body p-0 ai-recs-scroll">
|
||||
@@ -492,7 +632,7 @@
|
||||
</div>
|
||||
<div class="card-footer bg-white py-2 d-flex justify-content-between small text-muted">
|
||||
<span id="aiRecsCount">—</span>
|
||||
<span>可手動產生挑品清單</span>
|
||||
<span>可手動產生作戰商品</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -518,7 +658,10 @@
|
||||
let allCompetitors = [];
|
||||
|
||||
// ── 頁面載入 ────────────────────────────────────────
|
||||
document.addEventListener('DOMContentLoaded', loadDashboard);
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadDashboard();
|
||||
loadGrowthOps();
|
||||
});
|
||||
|
||||
async function loadDashboard() {
|
||||
try {
|
||||
@@ -558,6 +701,86 @@ function renderKPIs(stats) {
|
||||
: 'card border-0 shadow-sm h-100';
|
||||
}
|
||||
|
||||
function formatMoney(value) {
|
||||
const num = Number(value || 0);
|
||||
return 'NT$ ' + Math.round(num).toLocaleString();
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? '').replace(/[&<>"']/g, (ch) => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
}[ch]));
|
||||
}
|
||||
|
||||
async function loadGrowthOps(forceRefresh = false) {
|
||||
const list = document.getElementById('growthOpsList');
|
||||
if (forceRefresh) {
|
||||
list.innerHTML = `<div class="text-center py-4 text-muted">
|
||||
<div class="spinner-border spinner-border-sm me-2"></div>更新作戰清單中...
|
||||
</div>`;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = '/api/ai/pchome-growth/opportunities?limit=8' + (forceRefresh ? '&refresh=1' : '');
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
if (!data.success) throw new Error(data.error || '讀取失敗');
|
||||
|
||||
const stats = data.stats || {};
|
||||
document.getElementById('growthCandidateCount').textContent = (stats.candidate_count || 0).toLocaleString();
|
||||
document.getElementById('growthMappedCount').textContent = (stats.mapped_count || 0).toLocaleString();
|
||||
document.getElementById('growthNeedsMapping').textContent = (stats.needs_mapping_count || 0).toLocaleString();
|
||||
|
||||
const scope = data.source_scope || {};
|
||||
const active = (scope.active_external_sources || []).join('、') || '尚未接入';
|
||||
const paused = (scope.paused_external_sources || []).join('、') || '無';
|
||||
document.getElementById('growthSourceNote').textContent =
|
||||
`業績來源:${scope.primary_sales_source || 'PChome 後台業績'}。外部價格先看:${active}。暫停來源:${paused}。`;
|
||||
|
||||
renderGrowthOps(data.opportunities || []);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
list.innerHTML = `<div class="text-center py-4 text-muted">
|
||||
<i class="fas fa-circle-exclamation d-block mb-2"></i>
|
||||
PChome 成長作戰清單暫時無法讀取,請稍後再試。
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderGrowthOps(rows) {
|
||||
const list = document.getElementById('growthOpsList');
|
||||
if (!rows.length) {
|
||||
list.innerHTML = `<div class="text-center py-4 text-muted">
|
||||
<i class="fas fa-circle-info d-block mb-2"></i>
|
||||
目前沒有足夠資料,請先確認 PChome 業績檔已匯入。
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
list.innerHTML = rows.map((row) => {
|
||||
const action = row.recommended_action || {};
|
||||
const reason = (row.reason_lines || []).slice(0, 2).join(' ');
|
||||
const price = row.external_price;
|
||||
const priceText = price && price.gap_pct !== null && price.gap_pct !== undefined
|
||||
? `外部價差 ${price.gap_pct > 0 ? '+' : ''}${price.gap_pct}%`
|
||||
: '尚未可比價';
|
||||
return `<article class="growth-item">
|
||||
<div>
|
||||
<h3 class="growth-item-title">${escapeHtml(row.product_name)}</h3>
|
||||
<p class="growth-item-meta">
|
||||
${formatMoney(row.sales_7d)} · 近 7 天業績 · ${escapeHtml(priceText)}
|
||||
</p>
|
||||
<p class="growth-item-reason">${escapeHtml(reason)}</p>
|
||||
</div>
|
||||
<span class="growth-action-pill">${escapeHtml(action.label || '待判斷')}</span>
|
||||
</article>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// ── 競品比價表格(熱力圖底色)──────────────────────
|
||||
function renderCompetitorTable(rows) {
|
||||
const tbody = document.getElementById('competitorTbody');
|
||||
@@ -570,7 +793,7 @@ function renderCompetitorTable(rows) {
|
||||
}
|
||||
|
||||
tbody.innerHTML = rows.map(r => {
|
||||
// 熱力圖底色(行層級)
|
||||
// 行底色
|
||||
let rowBg = '';
|
||||
if (r.gap_pct > 20) rowBg = 'background:#fee2e2'; // 淺紅 — 嚴重貴
|
||||
else if (r.gap_pct > 10) rowBg = 'background:#fef9c3'; // 淺黃 — 有風險
|
||||
@@ -595,7 +818,7 @@ function renderCompetitorTable(rows) {
|
||||
'match_type_exact':['bg-success text-white', '同款確認'],
|
||||
'price_alert_exact':['bg-danger text-white', '價差告警'],
|
||||
'evidence_brand': ['bg-light text-dark', '品牌一致'],
|
||||
'evidence_identity':['bg-light text-dark', '身份證據'],
|
||||
'evidence_identity':['bg-light text-dark', '同款證據'],
|
||||
'match_shared_model_token':['bg-light text-dark','型號一致'],
|
||||
'match_product_line':['bg-light text-dark', '品線一致'],
|
||||
'alert_tier_price':['bg-warning text-dark', '優先追蹤'],
|
||||
@@ -640,23 +863,22 @@ function filterTable() {
|
||||
renderCompetitorTable(filtered);
|
||||
}
|
||||
|
||||
// ── AI 決策日誌(含推理足跡)───────────────────────
|
||||
// ── 作戰建議紀錄 ────────────────────────
|
||||
function renderAiRecs(recs) {
|
||||
const container = document.getElementById('aiRecsList');
|
||||
document.getElementById('aiRecsCount').textContent =
|
||||
recs.length ? `共 ${recs.length} 筆決策記錄` : '尚無決策記錄';
|
||||
recs.length ? `共 ${recs.length} 筆建議` : '尚無建議';
|
||||
|
||||
if (!recs.length) {
|
||||
container.innerHTML = `
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-brain fa-3x text-muted mb-3 d-block"></i>
|
||||
<p class="text-muted mb-1">AI 決策日誌尚為空</p>
|
||||
<p class="text-muted mb-1">目前還沒有作戰建議</p>
|
||||
<p class="small text-muted mb-3">
|
||||
排程每 6 小時執行一次 Hermes 分析 + NemoTron 派發<br>
|
||||
或點擊「立即分析」手動觸發
|
||||
系統會定期整理,也可以手動更新。
|
||||
</p>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="triggerAnalysis()">
|
||||
<i class="fas fa-bolt me-1"></i>立即觸發分析
|
||||
<i class="fas fa-bolt me-1"></i>整理建議
|
||||
</button>
|
||||
</div>`;
|
||||
return;
|
||||
@@ -676,14 +898,6 @@ function renderAiRecs(recs) {
|
||||
const confColor = confPct >= 80 ? 'bg-success' : confPct >= 60 ? 'bg-warning' : 'bg-danger';
|
||||
const gapSign = r.gap_pct > 0 ? '+' : '';
|
||||
|
||||
// 推理足跡
|
||||
const hermesInfo = r.hermes_duration
|
||||
? `${r.analyst} ${r.hermes_duration}s${r.hermes_tokens ? ' / ' + r.hermes_tokens + 'tok' : ''}`
|
||||
: r.analyst;
|
||||
const nimInfo = r.nim_tokens
|
||||
? `${r.dispatcher} ${r.nim_tokens}tok`
|
||||
: r.dispatcher;
|
||||
|
||||
return `<div class="border rounded mb-2 p-2" style="font-size:0.83rem">
|
||||
<div class="d-flex justify-content-between align-items-start mb-1">
|
||||
<span class="fw-bold text-truncate me-2" style="max-width:200px" title="${r.name}">${r.name}</span>
|
||||
@@ -704,9 +918,8 @@ function renderAiRecs(recs) {
|
||||
</div>
|
||||
<small class="text-muted">${confPct}%</small>
|
||||
</div>
|
||||
<small class="text-muted" title="推理足跡">
|
||||
<i class="fas fa-microchip me-1" style="font-size:0.68rem"></i>${hermesInfo} → ${nimInfo}
|
||||
· ${r.created_at}
|
||||
<small class="text-muted">
|
||||
${r.created_at}
|
||||
</small>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -720,7 +933,7 @@ async function generatePickList() {
|
||||
const msg = document.getElementById('triggerToastMsg');
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>挑品中...';
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>整理中...';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/ai/product-picks/generate', {
|
||||
@@ -739,12 +952,12 @@ async function generatePickList() {
|
||||
|
||||
if (data.success) loadDashboard();
|
||||
} catch (e) {
|
||||
msg.innerHTML = '<i class="fas fa-times-circle me-1"></i>挑品失敗:' + e.message;
|
||||
msg.innerHTML = '<i class="fas fa-times-circle me-1"></i>整理失敗:' + e.message;
|
||||
toast.className = 'toast align-items-center text-white border-0 bg-danger';
|
||||
new bootstrap.Toast(toast, { delay: 4000 }).show();
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-wand-magic-sparkles me-1"></i>產生挑品清單';
|
||||
btn.innerHTML = '<i class="fas fa-wand-magic-sparkles me-1"></i>產生作戰商品';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,7 +968,7 @@ async function backfillPchomeMatches() {
|
||||
const msg = document.getElementById('triggerToastMsg');
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>補抓中...';
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>整理中...';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/ai/pchome-match/backfill', {
|
||||
@@ -767,19 +980,19 @@ async function backfillPchomeMatches() {
|
||||
|
||||
msg.innerHTML = data.success
|
||||
? `<i class="fas fa-check-circle me-1"></i>${data.message}`
|
||||
: `<i class="fas fa-times-circle me-1"></i>${data.error || '補抓啟動失敗'}`;
|
||||
: `<i class="fas fa-times-circle me-1"></i>${data.error || '商品對應啟動失敗'}`;
|
||||
toast.className = 'toast align-items-center text-white border-0 ' +
|
||||
(data.success ? 'bg-success' : 'bg-danger');
|
||||
new bootstrap.Toast(toast, { delay: 6000 }).show();
|
||||
|
||||
if (data.success) setTimeout(loadDashboard, 90000);
|
||||
} catch (e) {
|
||||
msg.innerHTML = '<i class="fas fa-times-circle me-1"></i>補抓失敗:' + e.message;
|
||||
msg.innerHTML = '<i class="fas fa-times-circle me-1"></i>商品對應失敗:' + e.message;
|
||||
toast.className = 'toast align-items-center text-white border-0 bg-danger';
|
||||
new bootstrap.Toast(toast, { delay: 4000 }).show();
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-magnifying-glass-chart me-1"></i>補抓未搜尋';
|
||||
btn.innerHTML = '<i class="fas fa-magnifying-glass-chart me-1"></i>補商品對應';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,7 +1003,7 @@ async function triggerAnalysis() {
|
||||
const msg = document.getElementById('triggerToastMsg');
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>啟動中...';
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>整理中...';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/ai/icaim/trigger', { method: 'POST' });
|
||||
@@ -809,12 +1022,12 @@ async function triggerAnalysis() {
|
||||
setTimeout(loadDashboard, 60000);
|
||||
}
|
||||
} catch (e) {
|
||||
msg.innerHTML = '<i class="fas fa-times-circle me-1"></i>觸發失敗:' + e.message;
|
||||
msg.innerHTML = '<i class="fas fa-times-circle me-1"></i>整理失敗:' + e.message;
|
||||
toast.className = 'toast align-items-center text-white border-0 bg-danger';
|
||||
new bootstrap.Toast(toast, { delay: 4000 }).show();
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-bolt me-1"></i>立即分析';
|
||||
btn.innerHTML = '<i class="fas fa-bolt me-1"></i>整理建議';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user