import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import api from '@/lib/api' import { CollapsibleSection } from '@/components/CollapsibleSection' import { MetricCard } from '@/components/MetricCard' import { FilterableTable } from '@/components/FilterableTable' import { LoadingSpinner } from '@/components/LoadingSpinner' import { formatCurrency, formatPercent, formatNumber, priorityColor } from '@/lib/utils' import { useState } from 'react' import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine, Legend } from 'recharts' const PAGE_SIZE = 15 const ACTION_LABELS: Record = { 'price_up': '涨价', 'price_down': '降价', 'recipe_optimize': '优化配方', 'portion_reduce': '减份量', 'delist': '下架', 'evaluate_delist': '评估下架', 'fix_data': '修复数据', 'monitor': '监控', 'keep': '保持', } const STATUS_LABELS: Record = { 'planned': '待执行', 'executing': '执行中', 'completed': '已完成', 'cancelled': '已取消', } export function AdjustmentTab({ month }: { month: string }) { const queryClient = useQueryClient() const [subTab, setSubTab] = useState<'diagnosis' | 'adjustment' | 'verify'>('diagnosis') const [diagPage, setDiagPage] = useState(1) const [adjPage, setAdjPage] = useState(1) const [diagPriority, setDiagPriority] = useState('') const [adjStatus, setAdjStatus] = useState('') const [diagSort, setDiagSort] = useState('priority') const [diagOrder, setDiagOrder] = useState<'asc' | 'desc'>('asc') const [adjSort, setAdjSort] = useState('effective_date') const [adjOrder, setAdjOrder] = useState<'asc' | 'desc'>('desc') const [showForm, setShowForm] = useState(false) const [verifyId, setVerifyId] = useState(null) const [formData, setFormData] = useState({}) const { data: diag, isLoading: ld } = useQuery({ queryKey: ['ca/diagnosis', month, diagPage, diagPriority, diagSort, diagOrder], queryFn: () => api.get(`/cost-analysis/diagnosis`, { params: { month, page: diagPage, page_size: PAGE_SIZE, sort: diagSort, order: diagOrder, ...(diagPriority ? { priority: diagPriority } : {}) } }) }) const { data: adj, isLoading: la } = useQuery({ queryKey: ['ca/adjustment', month, adjPage, adjStatus, adjSort, adjOrder], queryFn: () => api.get(`/cost-analysis/adjustment`, { params: { month, page: adjPage, page_size: PAGE_SIZE, sort: adjSort, order: adjOrder, ...(adjStatus ? { status: adjStatus } : {}) } }) }) const { data: verify, isLoading: lv } = useQuery({ queryKey: ['ca/adjustment/verify', verifyId], queryFn: () => api.get(`/cost-analysis/adjustment/${verifyId}/verify`), enabled: verifyId !== null }) const generateDiagnosis = useMutation({ mutationFn: () => api.post('/cost-analysis/diagnosis/generate'), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ca/diagnosis'] }) }, }) const createAdjustment = useMutation({ mutationFn: (data: any) => api.post('/cost-analysis/adjustment', data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['ca/adjustment'] }); setShowForm(false); setFormData({}) }, }) const updateStatus = useMutation({ mutationFn: ({ id, status }: { id: number; status: string }) => api.put(`/cost-analysis/adjustment/${id}`, { status }), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['ca/adjustment'] }), }) const diagData = (diag as any)?.data || [] const diagMeta = (diag as any)?.meta || { total: 0 } const adjData = (adj as any)?.data || [] const adjMeta = (adj as any)?.meta || { total: 0 } const verifyData = (verify as any)?.data || null const p0Count = diagData.filter((d: any) => d.priority === 'P0').length const p1Count = diagData.filter((d: any) => d.priority === 'P1').length const statusColor = (status: string) => { if (status === 'executing') return 'bg-blue-100 text-blue-700' if (status === 'completed') return 'bg-green-100 text-green-700' if (status === 'cancelled') return 'bg-red-100 text-red-700' return 'bg-gray-100 text-gray-700' } const handleCreateAdjustment = (diag: any) => { setFormData({ dish_code: diag.dish_code, dish_name: diag.dish_name, adjustment_type: diag.suggested_action === 'price_up' ? 'price' : diag.suggested_action === 'portion_reduce' ? 'portion' : diag.suggested_action === 'delist' ? 'delisting' : 'recipe', before_theoretical_margin: diag.theoretical_margin_pct, before_cost_variance: diag.cost_variance_amount, reason: diag.diagnosis_detail, diagnosis_id: diag.id, effective_date: new Date().toISOString().slice(0, 10), }) setShowForm(true) } const handleSubmitForm = () => { createAdjustment.mutate(formData) } return (
{subTab === 'diagnosis' && ( <>
{generateDiagnosis.data && 已生成 {(generateDiagnosis.data as any)?.data?.generated} 条诊断}
{ld ? : ( formatPercent(r.theoretical_margin_pct) }, { key: 'actual_margin_pct', label: '实际毛利率', align: 'right', render: (r) => formatPercent(r.actual_margin_pct) }, { key: 'cost_variance_amount', label: '成本差异', align: 'right', render: (r) => formatCurrency(r.cost_variance_amount) }, { key: 'suggested_action', label: '建议动作', render: (r) => ACTION_LABELS[r.suggested_action] || r.suggested_action }, { key: 'priority', label: '优先级', render: (r) => ( {r.priority} )}, { key: 'action', label: '操作', render: (r) => ( )}, ]} /> )} {showForm && (
setFormData({ ...formData, effective_date: e.target.value })} className="mt-1 w-full rounded border px-3 py-1.5 text-sm" />
setFormData({ ...formData, after_target_margin: Number(e.target.value) })} className="mt-1 w-full rounded border px-3 py-1.5 text-sm" />
setFormData({ ...formData, decided_by: e.target.value })} className="mt-1 w-full rounded border px-3 py-1.5 text-sm" />
setFormData({ ...formData, reason: e.target.value })} className="mt-1 w-full rounded border px-3 py-1.5 text-sm" />
)} )} {subTab === 'adjustment' && ( <>
{la ? : ( ACTION_LABELS[r.adjustment_type] || r.adjustment_type }, { key: 'effective_date', label: '执行日期' }, { key: 'before_theoretical_margin', label: '调整前毛利率', align: 'right', render: (r) => formatPercent(r.before_theoretical_margin) }, { key: 'after_target_margin', label: '目标毛利率', align: 'right', render: (r) => formatPercent(r.after_target_margin) }, { key: 'decided_by', label: '决策人' }, { key: 'status', label: '状态', render: (r) => ( {STATUS_LABELS[r.status] || r.status} )}, { key: 'actions', label: '操作', render: (r) => (
{r.status === 'planned' && } {r.status === 'executing' && }
)}, ]} /> )} )} {subTab === 'verify' && ( <>
setVerifyId(e.target.value ? Number(e.target.value) : null)} placeholder="输入ID" className="rounded border px-3 py-1.5 text-sm w-32" />
{verifyId && ( lv ? : verifyData ? ( <>
({ ...d, period: '调整前' })), ...verifyData.after.map((d: any) => ({ ...d, period: '调整后' })), ]}> ACTION_LABELS[r.adjustment_type] || r.adjustment_type }, { key: 'effective_date', label: '执行日期' }, { key: 'reason', label: '调整原因' }, { key: 'status', label: '状态', render: (r) => ( {STATUS_LABELS[r.status] || r.status} )}, ]} /> ) :

输入调整记录ID查看验证数据

)} )}
) }