@@ -189,7 +206,7 @@ export function AdjustmentTab() {
{subTab === 'adjustment' && (
<>
-
+
+ |
+ {[
+ { key: 'effective_date', label: '执行日期' },
+ { key: 'dish_name', label: '菜品名' },
+ { key: 'adjustment_type', label: '调整类型' },
+ ].map(s => (
+
+ ))}
+ |
+
{la ?
: (
<>
diff --git a/client/src/components/cost-analysis/BomTab.tsx b/client/src/components/cost-analysis/BomTab.tsx
index 15689c9..67dc5a6 100644
--- a/client/src/components/cost-analysis/BomTab.tsx
+++ b/client/src/components/cost-analysis/BomTab.tsx
@@ -15,11 +15,16 @@ export function BomTab() {
const [complexPage, setComplexPage] = useState(1)
const [missingPage, setMissingPage] = useState(1)
const [selectedSku, setSelectedSku] = useState('')
+ const [complexSort, setComplexSort] = useState('material_count')
+ const [complexOrder, setComplexOrder] = useState('desc')
+ const [complexFilter, setComplexFilter] = useState('')
+ const [missSort, setMissSort] = useState('sales_amount')
+ const [missOrder, setMissOrder] = useState('desc')
const { data: bomOv, isLoading: l1 } = useQuery({ queryKey: ['ca/bom-overview'], queryFn: () => api.get('/cost-analysis/bom-overview') })
- const { data: complexity, isLoading: l2 } = useQuery({ queryKey: ['ca/bom-complexity', complexPage], queryFn: () => api.get(`/cost-analysis/bom-complexity?page=${complexPage}&page_size=${PAGE_SIZE}`) })
+ const { data: complexity, isLoading: l2 } = useQuery({ queryKey: ['ca/bom-complexity', complexPage, complexSort, complexOrder, complexFilter], queryFn: () => api.get(`/cost-analysis/bom-complexity?page=${complexPage}&page_size=${PAGE_SIZE}&sort=${complexSort}&order=${complexOrder}&filter=${complexFilter}`) })
const { data: highLoss, isLoading: l3 } = useQuery({ queryKey: ['ca/bom-high-loss'], queryFn: () => api.get('/cost-analysis/bom-high-loss?threshold=20') })
- const { data: missing, isLoading: l4 } = useQuery({ queryKey: ['ca/bom-missing', missingPage], queryFn: () => api.get(`/cost-analysis/bom-missing?page=${missingPage}&page_size=${PAGE_SIZE}`) })
+ const { data: missing, isLoading: l4 } = useQuery({ queryKey: ['ca/bom-missing', missingPage, missSort, missOrder], queryFn: () => api.get(`/cost-analysis/bom-missing?page=${missingPage}&page_size=${PAGE_SIZE}&sort=${missSort}&order=${missOrder}`) })
const { data: composition, isLoading: l5 } = useQuery({
queryKey: ['ca/bom-composition', selectedSku],
queryFn: () => api.get(`/cost-analysis/bom-composition?sku_code=${selectedSku}`),
@@ -79,7 +84,28 @@ export function BomTab() {
-
+
+
+ {[
+ { key: '', label: '全部' },
+ { key: 'high_loss', label: '含高损耗' },
+ { key: 'unique_heavy', label: '独有原料多' },
+ ].map(f => (
+
+ ))}
+ |
+ {[
+ { key: 'material_count', label: '原料数' },
+ { key: 'unique_material_count', label: '独有原料' },
+ { key: 'high_loss_count', label: '高损耗' },
+ ].map(s => (
+
+ ))}
+ |
+
+
+
+ {[
+ { key: 'sales_amount', label: '销售额' },
+ { key: 'sales_quantity', label: '销量' },
+ { key: 'standard_name', label: '菜品名' },
+ ].map(s => (
+
+ ))}
+ |
+
+
{
+ const av = parseFloat(a[topSort]) || 0
+ const bv = parseFloat(b[topSort]) || 0
+ return topOrder === 'asc' ? av - bv : bv - av
+ })
const typeData = (typeLoss as any)?.data || []
const reasonColor = (reason: string) => {
@@ -129,8 +136,22 @@ export function MaterialTab() {
-
-
+
+
+ {[
+ { key: 'total_loss_qty', label: '总损耗量' },
+ { key: 'avg_loss_rate', label: '平均损耗率' },
+ { key: 'total_loss_amount', label: '损耗金额' },
+ { key: 'dish_count', label: '涉及菜品数' },
+ ].map(s => (
+
+ ))}
+ |
+
+
+
formatPercent(r.max_loss_rate) },
{ key: 'total_loss_amount', label: '损耗金额', align: 'right', render: (r) => formatCurrency(r.total_loss_amount) },
]}
- data={topData.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)}
+ data={sortedTopData.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)}
/>
diff --git a/client/src/components/cost-analysis/PackagingTab.tsx b/client/src/components/cost-analysis/PackagingTab.tsx
index 6a6058b..a7cebb3 100644
--- a/client/src/components/cost-analysis/PackagingTab.tsx
+++ b/client/src/components/cost-analysis/PackagingTab.tsx
@@ -12,9 +12,12 @@ const PAGE_SIZE = 15
export function PackagingTab() {
const [page, setPage] = useState(1)
+ const [sort, setSort] = useState('total_cost')
+ const [order, setOrder] = useState('desc')
+ const [filter, setFilter] = useState('')
const { data: ov, isLoading: l1 } = useQuery({ queryKey: ['ca/packaging-overview'], queryFn: () => api.get('/cost-analysis/packaging-overview') })
- const { data: detail, isLoading: l2 } = useQuery({ queryKey: ['ca/packaging-detail', page], queryFn: () => api.get(`/cost-analysis/packaging-detail?page=${page}&page_size=${PAGE_SIZE}`) })
+ const { data: detail, isLoading: l2 } = useQuery({ queryKey: ['ca/packaging-detail', page, sort, order, filter], queryFn: () => api.get(`/cost-analysis/packaging-detail?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&order=${order}&filter=${filter}`) })
if (l1 || l2) return
@@ -38,6 +41,28 @@ export function PackagingTab() {
+
+ {[
+ { key: '', label: '全部' },
+ { key: 'high_loss', label: '高损耗' },
+ { key: 'normal', label: '正常' },
+ ].map(f => (
+
+ ))}
+ |
+ {[
+ { key: 'total_cost', label: '总成本' },
+ { key: 'loss_qty', label: '损耗量' },
+ { key: 'avg_loss_rate', label: '损耗率' },
+ { key: 'dish_count', label: '涉及菜品数' },
+ ].map(s => (
+
+ ))}
+ |
+
+
= {
export function ProfitabilityTab() {
const [page, setPage] = useState(1)
const [category, setCategory] = useState('')
+ const [sort, setSort] = useState('sales_amount')
+ const [order, setOrder] = useState('desc')
+ const [filter, setFilter] = useState('')
const { data: matrix, isLoading: l1 } = useQuery({ queryKey: ['ca/menu-engineering'], queryFn: () => api.get('/cost-analysis/menu-engineering') })
- const { data: profit, isLoading: l2 } = useQuery({ queryKey: ['ca/profitability', page, category], queryFn: () => api.get(`/cost-analysis/profitability?page=${page}&page_size=${PAGE_SIZE}${category ? `&category=${category}` : ''}`) })
+ const { data: profit, isLoading: l2 } = useQuery({ queryKey: ['ca/profitability', page, category, sort, order, filter], queryFn: () => api.get(`/cost-analysis/profitability?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&order=${order}&filter=${filter}${category ? `&category=${category}` : ''}`) })
const { data: pricing, isLoading: l3 } = useQuery({ queryKey: ['ca/pricing'], queryFn: () => api.get('/cost-analysis/pricing?threshold=50') })
if (l1 || l2 || l3) return
@@ -81,9 +84,32 @@ export function ProfitabilityTab() {
-
-
-
+
+
+ {[
+ { key: '', label: '全部' },
+ { key: 'profitable', label: '盈利菜品' },
+ { key: 'loss', label: '亏损菜品' },
+ { key: 'high_variance', label: '成本超耗' },
+ ].map(f => (
+
+ ))}
+
|
+ {[
+ { key: 'sales_amount', label: '销售额' },
+ { key: 'sales_quantity', label: '销量' },
+ { key: 'actual_margin_rate_pct', label: '实际毛利率' },
+ { key: 'theoretical_margin_rate_pct', label: '理论毛利率' },
+ { key: 'cost_variance_amount', label: '成本差异' },
+ ].map(s => (
+
+ ))}
+
|
+
+
|
+