fix: refine growth dashboard summary data
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s
This commit is contained in:
@@ -3414,11 +3414,12 @@ function renderGrowthCommandCenter(stats = {}, rows = []) {
|
||||
const salesDeltaPct = Number(stats.overall_sales_delta_pct || 0);
|
||||
const matchRate = Number(stats.mapping_rate || (candidateCount ? (mappedCount / candidateCount) * 100 : 0));
|
||||
const latestSalesDate = String(stats.overall_latest_sales_date || stats.latest_sales_date || '').slice(0, 10);
|
||||
const pressureRows = safeRows.filter((row) => row?.recommended_action?.code === 'review_price_or_promo' || Number(row?.external_price?.gap_pct || 0) > 0);
|
||||
const advantageRows = safeRows.filter((row) => row?.recommended_action?.code === 'amplify_price_advantage' || Number(row?.external_price?.gap_pct || 0) < 0);
|
||||
const actionCounts = stats.action_code_counts || {};
|
||||
const pressureCount = Number(actionCounts.review_price_or_promo || 0);
|
||||
const advantageCount = Number(actionCounts.amplify_price_advantage || 0);
|
||||
const pendingCount = Math.max(0, needsMapping + reviewCandidateCount);
|
||||
const readyShare = candidateCount ? (advantageRows.length / candidateCount) * 100 : 0;
|
||||
const pressureShare = candidateCount ? (pressureRows.length / candidateCount) * 100 : 0;
|
||||
const readyShare = candidateCount ? (advantageCount / candidateCount) * 100 : 0;
|
||||
const pressureShare = candidateCount ? (pressureCount / candidateCount) * 100 : 0;
|
||||
|
||||
setText('commandSales7d', sales7d ? formatMoney(sales7d) : '—');
|
||||
setText('commandSalesDelta', prevSales7d
|
||||
@@ -3436,8 +3437,8 @@ function renderGrowthCommandCenter(stats = {}, rows = []) {
|
||||
setText('commandStatusReview', formatCount(reviewCandidateCount));
|
||||
setText('commandStatusNeeds', formatCount(needsMapping));
|
||||
setText('commandLatestSalesDate', latestSalesDate || '—');
|
||||
setText('commandPriceAdvantage', `${formatCount(advantageRows.length)} 件`);
|
||||
setText('commandPricePressure', `${formatCount(pressureRows.length)} 件`);
|
||||
setText('commandPriceAdvantage', `${formatCount(advantageCount)} 件`);
|
||||
setText('commandPricePressure', `${formatCount(pressureCount)} 件`);
|
||||
setText('commandPricePending', `${formatCount(pendingCount)} 件`);
|
||||
setText('commandDonutText', `${Math.round(matchRate)}%`);
|
||||
|
||||
@@ -3491,7 +3492,8 @@ function renderGrowthCommandTopDecliners(rows) {
|
||||
const tbody = document.getElementById('commandTopDecliners');
|
||||
if (!tbody) return;
|
||||
const sortedRows = [...rows]
|
||||
.sort((a, b) => Math.abs(Number(b.sales_delta_pct || 0)) - Math.abs(Number(a.sales_delta_pct || 0)))
|
||||
.filter((row) => Number(row.sales_7d || 0) > 0)
|
||||
.sort((a, b) => Number(b.sales_7d || 0) - Number(a.sales_7d || 0))
|
||||
.slice(0, 5);
|
||||
|
||||
if (!sortedRows.length) {
|
||||
@@ -3502,6 +3504,9 @@ function renderGrowthCommandTopDecliners(rows) {
|
||||
tbody.innerHTML = sortedRows.map((row, index) => {
|
||||
const delta = Number(row.sales_delta_pct || 0);
|
||||
const action = row.recommended_action?.label || '查看商品';
|
||||
const trendText = delta
|
||||
? `${delta > 0 ? '+' : ''}${delta.toFixed(1)}%`
|
||||
: '趨勢待補';
|
||||
return `<tr>
|
||||
<td>
|
||||
<span class="growth-command-meta momo-mono">#${index + 1} · ${escapeHtml(row.category || '未分類')}</span>
|
||||
@@ -3511,7 +3516,7 @@ function renderGrowthCommandTopDecliners(rows) {
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<strong class="momo-mono">${escapeHtml(formatMoney(row.sales_7d || 0))}</strong>
|
||||
<span class="growth-command-meta">${delta ? `${delta > 0 ? '+' : ''}${delta.toFixed(1)}%` : '趨勢待補'}</span>
|
||||
<span class="growth-command-meta">${escapeHtml(trendText)}</span>
|
||||
</td>
|
||||
<td class="text-end"><span class="growth-command-badge">${escapeHtml(action)}</span></td>
|
||||
</tr>`;
|
||||
|
||||
Reference in New Issue
Block a user