feat: 全页面增加营业收入/优惠指标 + 修复趋势预测空数据 + 区域对比口径统一 + StorePage最新经营日概览

- 后端: 所有API的discount改为从bill_fact累计discount_total
- 后端: revenue/daily-summary增加consumption字段
- 后端: situational-awareness/forecast修复c175字符串比较为timestamp
- 后端: region/comparison去掉未知区域排除,统一口径
- 后端: daily-card增加营业收入/优惠指标,返回target_date/baseline_date
- 前端: BossPage/DashboardPage/BankPage/StorePage/ExpenseOverviewTab/RegionalPage/RegionComparisonPage/RevenuePage 均增加营业收入和优惠指标卡
- 前端: StorePage日均经营概览改为最新经营日概览,显示具体对比日期
- 前端: 日期格式统一截取YYYY-MM-DD显示
This commit is contained in:
freedakgmail
2026-08-02 14:56:00 +08:00
parent a1a8b7ee5e
commit 271ad8041d
13 changed files with 204 additions and 76 deletions
+35 -24
View File
@@ -266,32 +266,41 @@ export function StorePage() {
{/* KPI达成率 */}
<KPISection month={month} level="store" storeCode={storeCode} />
{/* 最新经营概览 + 本周指标进度 一行 */}
<div className="grid gap-4 lg:grid-cols-2">
{/* 经营概览 */}
<div className="rounded-lg border bg-card p-4">
<div className="mb-3 flex items-center justify-between">
<h2 className="text-sm font-bold">{month}</h2>
{anomalyCount > 0 && (
<span className="rounded-md bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700">{anomalyCount} </span>
)}
{/* 最新经营概览 */}
<div className="rounded-lg border bg-card p-4">
<div className="mb-3 flex items-center justify-between">
<div>
<h2 className="text-sm font-bold"></h2>
<p className="text-xs text-muted-foreground">
{card?.target_date ? `${String(card.target_date).substring(0, 10)} 当日数据 vs ${String(card.baseline_date).substring(0, 10)} 一周前同日对比` : `${month} 月末单日 vs 一周前同日对比`}
</p>
</div>
{allAnomalyItems.length === 0 ? (
<p className="text-sm text-muted-foreground"></p>
) : (
<div className="grid gap-2 md:grid-cols-3">
{allAnomalyItems.map((item, idx) => (
<div key={idx} className={`rounded-md border p-3 ${item.is_anomaly ? 'border-red-200 bg-red-50/50' : ''}`}>
<p className="text-xs text-muted-foreground">{item.metric}</p>
<p className={`mt-1 text-lg font-bold ${item.is_anomaly ? 'text-red-600' : ''}`}>
{item.metric === '实收' ? formatCurrency(item.value) : formatNumber(item.value)}
</p>
<p className="text-xs text-muted-foreground">: {item.metric === '实收' ? formatCurrency(item.baseline) : formatNumber(item.baseline)}{item.is_anomaly && <span className="text-red-500"> </span>}</p>
</div>
))}
</div>
{anomalyCount > 0 && (
<span className="rounded-md bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700">{anomalyCount} </span>
)}
</div>
{allAnomalyItems.length === 0 ? (
<p className="text-sm text-muted-foreground"></p>
) : (
<div className="grid gap-2 grid-cols-5">
{allAnomalyItems.map((item, idx) => {
const isCurrency = item.metric === '实收' || item.metric === '营业收入' || item.metric === '优惠'
return (
<div key={idx} className={`rounded-md border p-3 ${item.is_anomaly ? 'border-red-200 bg-red-50/50' : ''}`}>
<p className="text-xs text-muted-foreground">{item.metric}</p>
<p className={`mt-1 text-lg font-bold ${item.is_anomaly ? 'text-red-600' : ''}`}>
{isCurrency ? formatCurrency(item.value) : formatNumber(item.value)}
</p>
<p className="text-xs text-muted-foreground">: {isCurrency ? formatCurrency(item.baseline) : formatNumber(item.baseline)}{item.is_anomaly && <span className="text-red-500"> </span>}</p>
</div>
)
})}
</div>
)}
</div>
{/* 本周指标进度 */}
<div className="grid gap-4 lg:grid-cols-2">
{/* 本周指标进度条 */}
{followup && (
@@ -515,7 +524,9 @@ export function StorePage() {
{sc && Number(sc.received) > 0 && (
<>
{/* 核心指标 - 月度汇总 */}
<div className="grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-6">
<div className="grid grid-cols-2 gap-3 md:grid-cols-4 lg:grid-cols-8">
<MetricCard title="营业收入" value={sc.consumption} format="currency" />
<MetricCard title="优惠" value={sc.discount} format="currency" />
<MetricCard title="月度实收" value={sc.received} format="currency" />
<MetricCard title="月度账单数" value={sc.bill_count} format="number" />
<MetricCard title="客单价" value={sc.avg_bill_value} format="currency" />