feat: 利润与成本指标卡增加颜色标识 - 绿色正常/黄色关注/红色问题

This commit is contained in:
freedakgmail
2026-07-30 10:10:30 +08:00
parent 90be14a5d6
commit 47a9581500
2 changed files with 24 additions and 11 deletions
+16 -3
View File
@@ -8,9 +8,22 @@ interface MetricCardProps {
trend?: number
description?: string
className?: string
status?: 'good' | 'warn' | 'bad'
}
export function MetricCard({ title, value, unit, format = 'number', trend, description, className }: MetricCardProps) {
const STATUS_STYLES: Record<string, string> = {
good: 'border-green-200 bg-green-50/40',
warn: 'border-yellow-200 bg-yellow-50/40',
bad: 'border-red-200 bg-red-50/40',
}
const STATUS_VALUE_STYLES: Record<string, string> = {
good: 'text-green-700',
warn: 'text-yellow-700',
bad: 'text-red-700',
}
export function MetricCard({ title, value, unit, format = 'number', trend, description, className, status }: MetricCardProps) {
const displayValue = format === 'percent'
? value === null || value === undefined ? '-' : `${Number(value).toFixed(2)}%`
: format === 'currency'
@@ -20,7 +33,7 @@ export function MetricCard({ title, value, unit, format = 'number', trend, descr
: formatNumber(value)
return (
<div className={cn('rounded-lg border bg-card p-4 shadow-sm', className)}>
<div className={cn('rounded-lg border bg-card p-4 shadow-sm', status && STATUS_STYLES[status], className)}>
<div className="flex items-center gap-1">
<p className="text-sm text-muted-foreground">{title}</p>
{description && (
@@ -33,7 +46,7 @@ export function MetricCard({ title, value, unit, format = 'number', trend, descr
)}
</div>
<div className="mt-2 flex items-baseline gap-1">
<span className="text-2xl font-bold">{displayValue}</span>
<span className={cn('text-2xl font-bold', status && STATUS_VALUE_STYLES[status])}>{displayValue}</span>
{unit && <span className="text-sm text-muted-foreground">{unit}</span>}
</div>
{trend !== undefined && (
+8 -8
View File
@@ -192,16 +192,16 @@ export function DashboardPage() {
{ex && (
<CollapsibleSection title="利润与成本" subtitle="食材成本、经营费用、净利润等财务指标">
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
<MetricCard title="理论净利润" value={ex.theoretical_net_profit} format="currency" description={`净利率 ${formatPercent(ex.theoretical_net_margin_pct)} · 按标准成本计算的理论利润`} />
<MetricCard title="实际净利润" value={ex.actual_net_profit} format="currency" description={`净利率 ${formatPercent(ex.actual_net_margin_pct)} · 盈利 ${ex.profitable_stores}家 / 亏损 ${ex.loss_stores}`} />
<MetricCard title="食材成本" value={ex.total_food_cost} format="currency" description={`成本率 ${formatPercent(ex.actual_food_cost_rate_pct)} · 理论成本率 ${formatPercent(ex.theoretical_food_cost_rate_pct)}`} />
<MetricCard title="经营费用" value={ex.total_expense} format="currency" description={`费用率 ${formatPercent(ex.overall_expense_rate_pct)} · 人工 ${formatCurrency(ex.total_wage)} · 房租 ${formatCurrency(ex.total_rent)}`} />
<MetricCard title="理论净利润" value={ex.theoretical_net_profit} format="currency" status="good" description={`净利率 ${formatPercent(ex.theoretical_net_margin_pct)} · 按标准成本计算的理论利润`} />
<MetricCard title="实际净利润" value={ex.actual_net_profit} format="currency" status={Number(ex.actual_net_margin_pct) < 5 ? 'bad' : Number(ex.actual_net_margin_pct) < 10 ? 'warn' : 'good'} description={`净利率 ${formatPercent(ex.actual_net_margin_pct)} · 盈利 ${ex.profitable_stores}家 / 亏损 ${ex.loss_stores}`} />
<MetricCard title="食材成本" value={ex.total_food_cost} format="currency" status={Number(ex.actual_food_cost_rate_pct) - Number(ex.theoretical_food_cost_rate_pct) > 10 ? 'bad' : Number(ex.actual_food_cost_rate_pct) - Number(ex.theoretical_food_cost_rate_pct) > 5 ? 'warn' : 'good'} description={`成本率 ${formatPercent(ex.actual_food_cost_rate_pct)} · 理论成本率 ${formatPercent(ex.theoretical_food_cost_rate_pct)} · 差异 ${formatPercent(Number(ex.actual_food_cost_rate_pct) - Number(ex.theoretical_food_cost_rate_pct))}`} />
<MetricCard title="经营费用" value={ex.total_expense} format="currency" status={Number(ex.overall_expense_rate_pct) > 50 ? 'bad' : Number(ex.overall_expense_rate_pct) > 40 ? 'warn' : 'good'} description={`费用率 ${formatPercent(ex.overall_expense_rate_pct)} · 人工 ${formatCurrency(ex.total_wage)} · 房租 ${formatCurrency(ex.total_rent)}`} />
</div>
<div className="mt-3 grid grid-cols-2 gap-3 md:grid-cols-4">
<MetricCard title="人工费用" value={ex.total_wage} format="currency" description={`占实收 ${formatPercent(ex.overall_wage_rate_pct)}`} />
<MetricCard title="房租费用" value={ex.total_rent} format="currency" description={`占实收 ${formatPercent(ex.overall_rent_rate_pct)}`} />
<MetricCard title="水电费用" value={ex.total_utility} format="currency" description={`占实收 ${formatPercent(ex.overall_utility_rate_pct)}`} />
<MetricCard title="外卖佣金" value={ex.total_commission} format="currency" description="外卖平台佣金合计" />
<MetricCard title="人工费用" value={ex.total_wage} format="currency" status={Number(ex.overall_wage_rate_pct) > 30 ? 'bad' : Number(ex.overall_wage_rate_pct) > 25 ? 'warn' : 'good'} description={`占实收 ${formatPercent(ex.overall_wage_rate_pct)}`} />
<MetricCard title="房租费用" value={ex.total_rent} format="currency" status={Number(ex.overall_rent_rate_pct) > 20 ? 'bad' : Number(ex.overall_rent_rate_pct) > 15 ? 'warn' : 'good'} description={`占实收 ${formatPercent(ex.overall_rent_rate_pct)}`} />
<MetricCard title="水电费用" value={ex.total_utility} format="currency" status={Number(ex.overall_utility_rate_pct) > 8 ? 'warn' : 'good'} description={`占实收 ${formatPercent(ex.overall_utility_rate_pct)}`} />
<MetricCard title="外卖佣金" value={ex.total_commission} format="currency" status={Number(ex.total_commission) > 4000000 ? 'warn' : 'good'} description="外卖平台佣金合计" />
</div>
</CollapsibleSection>
)}