diff --git a/.gitignore b/.gitignore
index 30606c4..66a0d42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,7 @@ dist/
*.xlsx
*.zip
.playwright-mcp/
+backups/
+.playwright-cli/
+.tmp/
+数据/
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 144ed7a..2d74c86 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -26,6 +26,10 @@ import { SituationalAwarenessPage } from '@/pages/SituationalAwarenessPage'
import { BossPage } from '@/pages/BossPage'
import { RevenuePage } from '@/pages/RevenuePage'
import { BankPage } from '@/pages/BankPage'
+import { CentralKitchenPage } from '@/pages/CentralKitchenPage'
+import { DistributionReconciliationPage } from '@/pages/DistributionReconciliationPage'
+import { BomPenetrationPage } from '@/pages/BomPenetrationPage'
+import { ProductionPlanPage } from '@/pages/ProductionPlanPage'
import { LoginPage } from '@/pages/LoginPage'
const queryClient = new QueryClient({
@@ -73,6 +77,10 @@ export default function App() {
| 产品编码 | +产品名称 | +材料数 | +半成品数 | +理论成本 | +领用成本 | +单位理论成本 | +单位领用成本 | +差异金额 | +差异率 | +
|---|---|---|---|---|---|---|---|---|---|
| {b.product_code} | +{b.product_name} | +{b.material_count} | ++ {b.sub_product_count > 0 ? {b.sub_product_count} : '-'} + | +{formatCurrency(b.bom_theoretical_amt)} | +{formatCurrency(b.bom_issue_amt)} | +{formatNumber(b.bom_unit_theoretical_cost)} | +{formatNumber(b.bom_unit_issue_cost)} | += 0 ? 'text-yellow-600' : 'text-red-600'}`}> + {formatCurrency(b.variance_amt)} + | ++ {varianceText(b.variance_pct)} + | +
| 产品编码 | +产品名称 | +品类 | +入库量 | +理论成本 | +实际成本 | +材料数 | +多级BOM | ++ |
|---|---|---|---|---|---|---|---|---|
| {p.product_code} | +{p.product_name} | +{p.category_minor} | +{formatNumber(p.inbound_quantity)} | +{formatCurrency(p.theoretical_cost)} | +{formatCurrency(p.actual_cost)} | +{p.material_count} | ++ {p.has_multi_level_bom ? ✓ : '-'} + | ++ + | +
| 成品编码 | +成品名称 | +成品配方 | ++ | 半成品编码 | +半成品名称 | +半成品配方 | +
|---|---|---|---|---|---|---|
| {c.finished_code} | +{c.finished_name} | +{c.finished_recipe} | +{c.sub_product_code} | +{c.sub_product_finished_name || c.sub_product_name} | +{c.sub_product_recipe || '-'} | +
+ 基线: {typeof o.baseline === 'number' ? (o.baseline < 100 ? formatPercent(o.baseline) : formatNumber(o.baseline)) : o.baseline} + {typeof o.baseline === 'number' && o.baseline < 100 ? '%' : ''} · 责任: {o.owner} · 验收: {o.evidence} +
+{formatCurrency(opp)}
+占比 {formatPercent(pct)}
++ {line || '\u00A0'} +
+ ) + })} +净利润
-{formatCurrency(wf.net_profit)}
+门店贡献利润
+{formatCurrency(wf.store_contribution)}
+ 理论月度机会合计 {formatCurrency(totalOpportunity)},考虑项目重叠和促销弹性,经营承诺值取 ≥ 150万元。 + 标准门店贡献率目标从 7.29% 提升至约 10%。 +
+{s.store_name}
-实收 {formatCurrency(s.received)} · 净利率 {formatPercent(s.net_margin_pct)}
+实收 {formatCurrency(s.received)} · 贡献率 {formatPercent(s.contribution_margin_pct)}
亏损 BOTTOM5
{s.store_name}
-实收 {formatCurrency(s.received)} · 净利率 {formatPercent(s.net_margin_pct)}
+实收 {formatCurrency(s.received)} · 贡献率 {formatPercent(s.contribution_margin_pct)}
| 品类 | +产品数 | +入库量 | +理论成本 | +实际材料 | +制造费用 | +全成本 | +效率差异 | +
|---|---|---|---|---|---|---|---|
| {c.category_minor} | +{c.product_count} | +{formatNumber(c.total_qty)} | +{formatCurrency(c.theoretical_cost)} | +{formatCurrency(c.material_actual_cost)} | +{formatCurrency(c.allocated_mfg_cost)} | +{formatCurrency(c.full_cost)} | ++ {varianceText(c.efficiency_var_pct)} + | +
| 编码 | +产品名 | +品类 | +入库量 | +理论成本 | +标准成本 | +实际材料 | +制造费用 | +全成本 | +单位成本 | +效率差异 | +标准差异 | +入库价值 | +产品毛利 | +
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {p.product_code} | +{p.product_name} | +{p.category_minor} | +{formatNumber(p.inbound_quantity)} | +{formatCurrency(p.theoretical_cost)} | +{formatCurrency(p.standard_cost)} | +{formatCurrency(p.material_actual_cost)} | +{formatCurrency(p.allocated_manufacturing_cost)} | +{formatCurrency(p.full_manufacturing_cost)} | +{formatNumber(p.full_unit_cost)} | ++ {varianceText(p.efficiency_variance_pct)} + | ++ {varianceText(p.standard_variance_pct)} + | +{formatCurrency(p.inbound_value)} | += 0 ? 'text-green-600' : 'text-red-600'}`}> + {formatCurrency(p.product_margin)} + | +
| 配方 | +物料 | +规格 | +理论用量 | +实际用量 | +理论金额 | +实际金额 | +金额差异率 | +实际出成率 | +标准出成率 | +出成差异 | +
|---|---|---|---|---|---|---|---|---|---|---|
| {r.recipe_name} | +{r.item_name} | +{r.specification} | +{formatNumber(r.theoretical_qty)} | +{formatNumber(r.actual_qty)} | +{formatCurrency(r.theoretical_amt)} | +{formatCurrency(r.actual_amt)} | ++ {varianceText(r.variance_pct)} + | +{r.avg_actual_yield ? `${(r.avg_actual_yield * 100).toFixed(2)}%` : '-'} | +{r.avg_recipe_yield ? `${(r.avg_recipe_yield * 100).toFixed(2)}%` : '-'} | +0.03 ? 'text-green-600' : ''}`}> + {r.yield_diff ? `${(r.yield_diff * 100).toFixed(2)}pct` : '-'} + | +
| 配方 | +产品名 | +理论入库量 | +实际入库量 | +达成率 | +预期偏差率 | +入库金额 | +退库量 | +退库率 | +
|---|---|---|---|---|---|---|---|---|
| {y.recipe_name} | +{y.product_name} | +{formatNumber(y.theoretical_inbound_qty)} | +{formatNumber(y.actual_inbound_qty)} | += 0.95 ? 'text-green-600' : y.avg_achievement_rate >= 0.9 ? 'text-yellow-600' : 'text-red-600'}`}> + {y.avg_achievement_rate ? `${(y.avg_achievement_rate * 100).toFixed(2)}%` : '-'} + | +0.03 ? 'text-green-600' : ''}`}> + {y.avg_expected_var_rate ? `${(y.avg_expected_var_rate * 100).toFixed(2)}%` : '-'} + | +{formatCurrency(y.total_inbound_amt)} | +{formatNumber(y.total_return_qty)} | +1 ? 'text-red-600' : ''}`}> + {y.return_rate ? `${y.return_rate.toFixed(2)}%` : '-'} + | +
| 费用类型 | +费用子类 | +来源 | +原始金额 | +分摊比例 | +分摊金额 | +分摊方法 | +计入重建 | +临时 | +备注 | +
|---|---|---|---|---|---|---|---|---|---|
| {m.cost_type} | +{m.cost_subtype} | +{m.source_type} | +{formatCurrency(m.source_amount)} | +{m.share_pct ? `${(m.share_pct * 100).toFixed(2)}%` : '-'} | +{formatCurrency(m.allocated_amount)} | +{m.allocation_method} | +{m.include_in_rebuilt_cost ? '✅' : '❌'} | +
+ {m.is_provisional && |
+ {m.note} | +
全部门店经营总览 · 2026年4月
+全部门店经营总览 · {month}
{item.label}
+账单与菜品明细数据质量检查 · {dq.min_date?.substring(0, 10)} ~ {dq.max_date?.substring(0, 10)}
完整性
+{completenessStatus}
+缺失字段检查
+准确性
+{accuracyStatus}
+负值、零值、越界检查
+一致性
+{consistencyStatus}
+库存负耗用等跨表检查
+利润数据置信等级
+{profitConfidence}
+账单质量状态
-- {billQualityStatus} -
-菜品质量状态
-- {dishQualityStatus} +
+ {dishMissingStore === 0 && dishMissingDish === 0 ? '正常' : '异常'}
+ 提示: + {negativeConsumptionCount} 条库存负耗用记录,金额 {formatCurrency(negativeConsumptionAmount)},可能扭曲成本和库存周转,需排查盘点差异或录入错误 +
+| 品类 | +品项数 | +配送量 | +配送金额 | +不含税成本 | +耗用金额 | +期末库存 | +差异金额 | +差异率 | +
|---|---|---|---|---|---|---|---|---|
| {c.minor_category} | +{c.item_count} | +{formatNumber(c.dist_qty)} | +{formatCurrency(c.dist_amt)} | +{formatCurrency(c.dist_cost_excl_tax)} | +{formatCurrency(c.consumption_amt)} | +{formatCurrency(c.ending_amt)} | += 0 ? 'text-yellow-600' : 'text-red-600'}`}> + {formatCurrency(c.variance_amt)} + | ++ {varianceText(c.variance_pct)} + | +
| 门店编码 | +门店名称 | +配送金额 | +不含税成本 | +期初库存 | +实际耗用 | +期末库存 | +倒挤应耗用 | +差异金额 | +差异率 | +负库存 | +
|---|---|---|---|---|---|---|---|---|---|---|
| {s.store_code} | +{s.store_name || '-'} | +{formatCurrency(s.dist_amt)} | +{formatCurrency(s.dist_cost_excl_tax)} | +{formatCurrency(s.opening_amt)} | +{formatCurrency(s.consumption_amt)} | +{formatCurrency(s.ending_amt)} | +{formatCurrency(s.reverse_consumption_amt)} | += 0 ? 'text-yellow-600' : 'text-red-600'}`}> + {formatCurrency(s.variance_amt)} + | ++ {varianceText(s.variance_pct)} + | +0 ? 'text-red-600 font-medium' : ''}`}> + {s.neg_inventory_count > 0 ? s.neg_inventory_count : '-'} + | +
| 门店 | +品项编码 | +品项名称 | +品类 | +配送量 | +配送金额 | +期初 | +实际耗用 | +期末 | +倒挤应耗用 | +差异金额 | +差异率 | +
|---|---|---|---|---|---|---|---|---|---|---|---|
| {v.store_code} | +{v.item_code} | +{v.item_name} | +{v.minor_category} | +{formatNumber(v.dist_qty)} | +{formatCurrency(v.dist_amt)} | +{formatCurrency(v.opening_amt)} | +{formatCurrency(v.consumption_amt)} | +{formatCurrency(v.ending_amt)} | +{formatCurrency(v.reverse_consumption_amt)} | += 0 ? 'text-yellow-600' : 'text-red-600'}`}> + {formatCurrency(v.variance_amt)} + | ++ {varianceText(v.variance_pct)} + | +
| 品项编码 | +品项名称 | +品类 | +配送量 | +配送金额 | +涉及门店数 | +
|---|---|---|---|---|---|
| {u.item_code} | +{u.item_name} | +{u.minor_category} | +{formatNumber(u.dist_qty)} | +{formatCurrency(u.dist_amt)} | +{u.store_count} | +