feat(ai): 建立 PChome 銷售挑品清單
All checks were successful
CD Pipeline / deploy (push) Successful in 2m24s

This commit is contained in:
OoO
2026-05-01 10:05:16 +08:00
parent 55855ef508
commit 82d759d3b1
8 changed files with 541 additions and 15 deletions

View File

@@ -20,6 +20,9 @@
<button class="btn btn-outline-danger btn-sm" id="btnTrigger" onclick="triggerAnalysis()">
<i class="fas fa-bolt me-1"></i>立即分析
</button>
<button class="btn btn-outline-primary btn-sm" id="btnPickList" onclick="generatePickList()">
<i class="fas fa-wand-magic-sparkles me-1"></i>產生挑品清單
</button>
<button class="btn btn-outline-secondary btn-sm" onclick="loadDashboard()">
<i class="fas fa-redo me-1"></i>重新整理
</button>
@@ -40,7 +43,10 @@
<div class="card border-0 shadow-sm h-100">
<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>有效競品比價</div>
<div class="small text-muted mt-1">
<i class="fas fa-store me-1"></i>有效競品比價
<span id="kpiMatchRate" class="text-muted" style="font-size:0.7rem"></span>
</div>
</div>
</div>
</div>
@@ -60,7 +66,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>AI 挑品/決策記錄</div>
</div>
</div>
</div>
@@ -126,7 +132,7 @@
<div class="card-header py-2 bg-white border-bottom">
<span class="fw-bold">
<i class="fas fa-robot text-danger me-2"></i>AI 決策日誌
<small class="text-muted fw-normal ms-2">Hermes × NemoTron</small>
<small class="text-muted fw-normal ms-2">挑品 Agent × Hermes × NemoTron</small>
</span>
</div>
<div class="card-body p-0" style="overflow-y:auto; max-height:548px;">
@@ -138,7 +144,7 @@
</div>
<div class="card-footer bg-white py-2 d-flex justify-content-between small text-muted">
<span id="aiRecsCount"></span>
<span>每 6h 自動更新</span>
<span>可手動產生挑品清單</span>
</div>
</div>
</div>
@@ -193,6 +199,7 @@ function renderKPIs(stats) {
document.getElementById('kpiSkus').textContent = (stats.total_skus || 0).toLocaleString();
document.getElementById('kpiCompetitors').textContent = (stats.valid_competitor_prices || 0).toLocaleString();
document.getElementById('kpiAiRecs').textContent = (stats.total_ai_recs || 0).toLocaleString();
document.getElementById('kpiMatchRate').textContent = stats.match_rate ? `(${stats.match_rate}%)` : '';
const hr = stats.high_risk_count || 0;
document.getElementById('kpiHighRisk').textContent = hr;
@@ -300,6 +307,7 @@ function renderAiRecs(recs) {
const strategyMap = {
'price_cut': ['bg-danger', '降價'],
'promote': ['bg-primary', '主推'],
'product_pick':['bg-success', 'AI挑品'],
'monitor': ['bg-secondary', '觀察'],
'flag': ['bg-warning text-dark', '覆核'],
};
@@ -347,6 +355,41 @@ function renderAiRecs(recs) {
}).join('');
}
// ── 產生 AI 建議挑品清單 ───────────────────────────
async function generatePickList() {
const btn = document.getElementById('btnPickList');
const toast = document.getElementById('triggerToast');
const msg = document.getElementById('triggerToastMsg');
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>挑品中...';
try {
const res = await fetch('/api/ai/product-picks/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ limit: 30 })
});
const data = await res.json();
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 || '產生失敗'}`;
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) loadDashboard();
} catch (e) {
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>產生挑品清單';
}
}
// ── 手動觸發分析 ────────────────────────────────────
async function triggerAnalysis() {
const btn = document.getElementById('btnTrigger');