feat(ai): 建立 PChome 銷售挑品清單
All checks were successful
CD Pipeline / deploy (push) Successful in 2m24s
All checks were successful
CD Pipeline / deploy (push) Successful in 2m24s
This commit is contained in:
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user