import { useState } from 'react' import { useQuery } from '@tanstack/react-query' import api from '@/lib/api' import { CollapsibleSection } from '@/components/CollapsibleSection' import { FilterableTable } from '@/components/FilterableTable' import { LoadingSpinner } from '@/components/LoadingSpinner' import { MetricCard } from '@/components/MetricCard' import { formatCurrency, formatPercent, formatNumber } from '@/lib/utils' const PAGE_SIZE = 15 const PRIORITY_COLORS: Record = { 'P0': 'bg-red-100 text-red-700', 'P1': 'bg-orange-100 text-orange-700', 'P2': 'bg-yellow-100 text-yellow-700', 'P3': 'bg-gray-100 text-gray-700', } const LOSS_TYPE_COLORS: Record = { '理论即亏损': 'text-red-600 font-medium', '实际超耗导致亏损': 'text-orange-600 font-medium', '费用过高导致亏损': 'text-yellow-600 font-medium', } const STATUS_COLORS: Record = { '收入极低': 'text-red-600 font-medium', '收入偏低': 'text-orange-600', '食材成本率过高': 'text-red-600 font-medium', '食材成本率偏高': 'text-orange-600', '人工成本率严重过高': 'text-red-600 font-medium', '人工成本率过高': 'text-red-600', '人工成本率偏高': 'text-orange-600', '租金占比严重过高': 'text-red-600 font-medium', '租金占比过高': 'text-red-600', '租金占比偏高': 'text-orange-600', '水电燃气率过高': 'text-red-600', '水电燃气率偏高': 'text-orange-600', '外卖依赖度过高': 'text-red-600 font-medium', '外卖占比较高': 'text-orange-600', '坪效极低': 'text-red-600 font-medium', '坪效偏低': 'text-orange-600', '租约即将到期': 'text-red-600 font-medium', } interface StoreDetail { sales_store_name: string received: string actual_food_cost: string operating_expense: string actual_store_contribution: string actual_store_contribution_rate_pct: string wage_expense: string wage_rate_pct: string rent_expense: string rent_rate_pct: string utility_expense: string utility_rate_pct: string dorm_expense: string delivery_commission_expense: string delivery_received: string delivery_sales_share_pct: string area_sqm: string received_per_sqm: string bill_count: string lease_expiry_date: string theoretical_cost: string theoretical_store_contribution: string theoretical_store_contribution_rate_pct: string revenue_status: string food_cost_status: string wage_status: string rent_status: string utility_status: string delivery_status: string efficiency_status: string lease_status: string loss_type: string diagnosis_reasons: string diagnosis_suggestions: string suggested_actions: string priority: string } function StoreDetailModal({ store, onClose }: { store: StoreDetail | null, onClose: () => void }) { if (!store) return null const statusItems = [ { label: '收入状态', value: store.revenue_status }, { label: '食材成本', value: store.food_cost_status }, { label: '人工成本', value: store.wage_status }, { label: '租金占比', value: store.rent_status }, { label: '水电燃气', value: store.utility_status }, { label: '外卖依赖', value: store.delivery_status }, { label: '坪效', value: store.efficiency_status }, { label: '租约', value: store.lease_status }, ] const metrics = [ { label: '实收', value: formatCurrency(store.received) }, { label: '食材成本', value: formatCurrency(store.actual_food_cost) }, { label: '营业费用', value: formatCurrency(store.operating_expense) }, { label: '贡献利润', value: formatCurrency(store.actual_store_contribution), red: true }, { label: '贡献率', value: formatPercent(store.actual_store_contribution_rate_pct), red: true }, { label: '理论贡献', value: formatCurrency(store.theoretical_store_contribution) }, { label: '理论贡献率', value: formatPercent(store.theoretical_store_contribution_rate_pct) }, { label: '人工成本', value: formatCurrency(store.wage_expense) }, { label: '人工成本率', value: formatPercent(store.wage_rate_pct) }, { label: '租金', value: formatCurrency(store.rent_expense) }, { label: '租金率', value: formatPercent(store.rent_rate_pct) }, { label: '水电燃气', value: formatCurrency(store.utility_expense) }, { label: '水电率', value: formatPercent(store.utility_rate_pct) }, { label: '宿舍费用', value: formatCurrency(store.dorm_expense) }, { label: '外卖佣金', value: formatCurrency(store.delivery_commission_expense) }, { label: '外卖收入', value: formatCurrency(store.delivery_received) }, { label: '外卖占比', value: formatPercent(store.delivery_sales_share_pct) }, { label: '面积(㎡)', value: formatNumber(store.area_sqm) }, { label: '坪效', value: formatNumber(store.received_per_sqm) }, { label: '账单数', value: formatNumber(store.bill_count) }, { label: '租约到期', value: store.lease_expiry_date ? String(store.lease_expiry_date).substring(0, 10) : '-' }, ] return (
e.stopPropagation()}>

{store.sales_store_name}

{store.priority} {store.loss_type}

关键指标

{metrics.map((m, i) => (

{m.label}

{m.value}

))}

诊断状态

{statusItems.map((s, i) => (

{s.label}

{s.value}

))}

亏损原因分析

{store.diagnosis_reasons}

改进建议

{store.diagnosis_suggestions}

建议动作

{store.suggested_actions.split('、').map((action, i) => ( {action} ))}
) } export function LossDiagnosisTab({ month }: { month: string }) { const [page, setPage] = useState(1) const [sort, setSort] = useState('actual_store_contribution') const [filter, setFilter] = useState('') const [order, setOrder] = useState<'asc' | 'desc'>('asc') const [selectedStore, setSelectedStore] = useState(null) const { data, isLoading } = useQuery({ queryKey: ['se/loss-diagnosis', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/loss-diagnosis?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) }) if (isLoading) return const rows = (data as any)?.data || [] const meta = (data as any)?.meta || { page, pageSize: PAGE_SIZE, total: 0 } const p0Count = meta.evalStats ? Number(meta.evalStats.p0_count) : rows.filter((r: any) => r.priority === 'P0').length const p1Count = meta.evalStats ? Number(meta.evalStats.p1_count) : rows.filter((r: any) => r.priority === 'P1').length const totalLoss = meta.evalStats ? Number(meta.evalStats.total_loss) : rows.reduce((sum: number, r: any) => sum + parseFloat(r.actual_store_contribution || 0), 0) return (
setSelectedStore(r as StoreDetail)} columns={[ { key: 'sales_store_name', label: '门店', render: (r) => }, { key: 'priority', label: '优先级', render: (r) => {r.priority} }, { key: 'loss_type', label: '亏损类型', render: (r) => {r.loss_type} }, { key: 'received', label: '实收', align: 'right', render: (r) => formatCurrency(r.received) }, { key: 'actual_store_contribution', label: '贡献利润', align: 'right', render: (r) => {formatCurrency(r.actual_store_contribution)} }, { key: 'actual_store_contribution_rate_pct', label: '贡献率', align: 'right', render: (r) => {formatPercent(r.actual_store_contribution_rate_pct)} }, { key: 'wage_rate_pct', label: '人工率', align: 'right', render: (r) => 35 ? 'text-red-600' : ''}>{formatPercent(r.wage_rate_pct)} }, { key: 'rent_rate_pct', label: '租金率', align: 'right', render: (r) => 20 ? 'text-red-600' : ''}>{formatPercent(r.rent_rate_pct)} }, { key: 'received_per_sqm', label: '坪效', align: 'right', render: (r) => {formatNumber(r.received_per_sqm)} }, { key: 'diagnosis_reasons', label: '亏损原因', render: (r) => {r.diagnosis_reasons} }, { key: 'suggested_actions', label: '建议动作', render: (r) => {r.suggested_actions} }, ]} /> setSelectedStore(null)} />
) }