feat: 老板驾驶舱增加门店利润排名 - TOP5赚钱/BOTTOM5亏损,可点击跳转门店详情

This commit is contained in:
freedakgmail
2026-07-30 14:37:38 +08:00
parent 10c64a2ef2
commit 2df4f0799b
2 changed files with 78 additions and 0 deletions
+53
View File
@@ -45,6 +45,10 @@ export function BossPage() {
queryKey: ['store-expense/expense-structure'],
queryFn: () => api.get('/store-expense/expense-structure'),
})
const { data: profitRankingData } = useQuery({
queryKey: ['overview/store-profit-ranking'],
queryFn: () => api.get('/overview/store-profit-ranking'),
})
const pageLoading = odLoading || dailyLoading || exLoading || wfLoading || riskLoading || priorityLoading
@@ -56,6 +60,7 @@ export function BossPage() {
const dailyRows = (daily as any)?.data || []
const costOv = (costOverview as any)?.data || {}
const structRows = (expenseStructure as any)?.data || []
const profitRanking = (profitRankingData as any)?.data || []
// 风险分布
const riskSummary = riskRows.reduce((acc: any, r: any) => {
@@ -351,6 +356,54 @@ export function BossPage() {
</ResponsiveContainer>
</CollapsibleSection>
{/* ⑤b 门店利润排名 */}
{profitRanking.length > 0 && (
<CollapsibleSection title="门店利润排名" subtitle="TOP5 最赚钱 · BOTTOM5 亏损最多 · 公式:实收 - 食材成本 - 经营费用">
<div className="grid gap-4 lg:grid-cols-2">
{/* TOP5 */}
<div>
<p className="mb-2 text-sm font-bold text-green-600"> TOP5</p>
<div className="space-y-2">
{profitRanking.slice(0, 5).map((s: any, i: number) => (
<div
key={s.store_code}
className="flex cursor-pointer items-center gap-3 rounded-lg border border-green-200 bg-green-50/30 p-3 hover:bg-green-50/60"
onClick={() => navigate(`/stores/${s.store_code}`)}
>
<span className="text-lg font-bold text-green-600">{i + 1}</span>
<div className="flex-1">
<p className="text-sm font-medium">{s.store_name}</p>
<p className="text-xs text-muted-foreground"> {formatCurrency(s.received)} · {formatPercent(s.net_margin_pct)}</p>
</div>
<span className="text-lg font-bold text-green-700">{formatCurrency(s.net_profit)}</span>
</div>
))}
</div>
</div>
{/* BOTTOM5 */}
<div>
<p className="mb-2 text-sm font-bold text-red-600"> BOTTOM5</p>
<div className="space-y-2">
{profitRanking.filter((s: any) => Number(s.net_profit) < 0).slice(-5).reverse().map((s: any, i: number) => (
<div
key={s.store_code}
className="flex cursor-pointer items-center gap-3 rounded-lg border border-red-200 bg-red-50/30 p-3 hover:bg-red-50/60"
onClick={() => navigate(`/stores/${s.store_code}`)}
>
<span className="text-lg font-bold text-red-600">{i + 1}</span>
<div className="flex-1">
<p className="text-sm font-medium">{s.store_name}</p>
<p className="text-xs text-muted-foreground"> {formatCurrency(s.received)} · {formatPercent(s.net_margin_pct)}</p>
</div>
<span className="text-lg font-bold text-red-700">{formatCurrency(s.net_profit)}</span>
</div>
))}
</div>
</div>
</div>
</CollapsibleSection>
)}
{/* ⑥ 日度实收趋势 */}
<CollapsibleSection title="日度实收趋势" subtitle={`近30天 · 环比上周 ${trendReceived > 0 ? '↑' : '↓'} ${Math.abs(trendReceived)}%`}>
<ResponsiveContainer width="100%" height={220}>