feat: 门店详情风险/象限卡片增加偏差指标明细对比
This commit is contained in:
@@ -107,6 +107,25 @@ export function StoreDetailPage() {
|
||||
const quadrant = action?.management_quadrant || ''
|
||||
const quadrantInfo = quadrantDesc[quadrant]
|
||||
|
||||
// 公司均值
|
||||
const companyAvg = { daily_rev: 20144, bill: 36.8, discount: 20.5, margin: 71.5, member: 15.9, repeat: 37.0, delivery: 36.1, combo: 20.6, items: 4.05 }
|
||||
|
||||
// 偏差项明细
|
||||
const deviations: { label: string; store: number; company: number; unit: string; good: 'high' | 'low' }[] = [
|
||||
{ label: '日均营收', store: Number(risk?.avg_daily_received) || 0, company: companyAvg.daily_rev, unit: '元', good: 'high' },
|
||||
{ label: '客单价', store: Number(risk?.avg_bill_value) || 0, company: companyAvg.bill, unit: '元', good: 'high' },
|
||||
{ label: '优惠率', store: Number(risk?.discount_rate_pct) || 0, company: companyAvg.discount, unit: '%', good: 'low' },
|
||||
{ label: '毛利率', store: Number(risk?.theoretical_margin_pct) || 0, company: companyAvg.margin, unit: '%', good: 'high' },
|
||||
{ label: '会员占比', store: Number(risk?.member_bill_share_pct) || 0, company: companyAvg.member, unit: '%', good: 'high' },
|
||||
{ label: '复购率', store: Number(action?.repeat_rate_pct) || 0, company: companyAvg.repeat, unit: '%', good: 'high' },
|
||||
{ label: '外卖占比', store: Number(action?.delivery_bill_share_pct) || 0, company: companyAvg.delivery, unit: '%', good: 'low' },
|
||||
{ label: '成本差异率', store: Number(action?.variance_to_theoretical_pct) || 0, company: 20, unit: '%', good: 'low' },
|
||||
]
|
||||
const problemDeviations = deviations.filter(d => {
|
||||
const diff = d.store - d.company
|
||||
return d.good === 'high' ? diff < 0 : diff > 0
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<button onClick={() => navigate(-1)} className="flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground">
|
||||
@@ -135,9 +154,24 @@ export function StoreDetailPage() {
|
||||
<div className={`rounded-lg border p-3 ${riskInfo.color}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold">风险等级:{risk.risk_level}</span>
|
||||
{risk.primary_issue && <span className="text-xs opacity-80">— {risk.primary_issue}</span>}
|
||||
{action?.problem_combination && <span className="text-xs opacity-80">— {action.problem_combination}</span>}
|
||||
</div>
|
||||
<p className="mt-1 text-sm opacity-90">{riskInfo.desc}</p>
|
||||
{problemDeviations.length > 0 && (
|
||||
<div className="mt-2 space-y-1 border-t border-current/20 pt-2">
|
||||
<p className="text-xs font-medium opacity-80">偏差指标明细(vs 公司均值):</p>
|
||||
{problemDeviations.map(d => {
|
||||
const diff = d.store - d.company
|
||||
const diffStr = diff > 0 ? `+${diff.toFixed(1)}${d.unit}` : `${diff.toFixed(1)}${d.unit}`
|
||||
return (
|
||||
<div key={d.label} className="flex items-center justify-between text-xs">
|
||||
<span className="opacity-80">{d.label}</span>
|
||||
<span className="font-medium">{d.store.toFixed(1)}{d.unit} <span className="opacity-60">vs {d.company}{d.unit}</span> <span className="font-bold">{diffStr}</span></span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{quadrantInfo && (
|
||||
@@ -147,6 +181,23 @@ export function StoreDetailPage() {
|
||||
{action?.scale_tier && <span className="text-xs opacity-80">— {action.scale_tier}</span>}
|
||||
</div>
|
||||
<p className="mt-1 text-sm opacity-90">{quadrantInfo}</p>
|
||||
<div className="mt-2 space-y-1 border-t border-blue-200/50 pt-2">
|
||||
<p className="text-xs font-medium opacity-80">象限定位依据:</p>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="opacity-80">日均营收</span>
|
||||
<span className="font-medium">{Number(risk?.avg_daily_received || 0).toFixed(0)}元 <span className="opacity-60">vs 中位数 18642元</span></span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="opacity-80">毛利率</span>
|
||||
<span className="font-medium">{Number(risk?.theoretical_margin_pct || 0).toFixed(1)}% <span className="opacity-60">vs 中位数 70.0%</span></span>
|
||||
</div>
|
||||
{action?.benchmark_score && (
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<span className="opacity-80">综合基准分</span>
|
||||
<span className="font-medium">{Number(action.benchmark_score).toFixed(1)}分</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -158,6 +209,11 @@ export function StoreDetailPage() {
|
||||
<span className="font-bold text-sm">行动优先级:{action.action_priority}</span>
|
||||
{action.problem_combination && <span className="text-sm text-muted-foreground">问题:{action.problem_combination}</span>}
|
||||
{action.business_type && <span className="text-xs rounded bg-muted px-2 py-0.5">{action.business_type}</span>}
|
||||
{action?.variance_level && (
|
||||
<span className={`text-xs rounded px-2 py-0.5 ${action.variance_level.includes('红色') ? 'bg-red-100 text-red-700' : action.variance_level.includes('黄色') ? 'bg-yellow-100 text-yellow-700' : 'bg-green-100 text-green-700'}`}>
|
||||
成本差异:{action.variance_to_theoretical_pct}% ({action.variance_level})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user