import React, { useState, useEffect } from 'react'; import { ArrowLeft, Package, ChevronDown, ChevronUp, List } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { supabase } from '../../supabase/client'; import { motion, AnimatePresence } from 'framer-motion'; import { InventoryRecordsModal } from '../../components/InventoryRecordsModal'; interface FabricInventory { id: string; plan_id: string; fabric_code: string; plan_code: string; weight: number; target_quantity: number; completed_quantity: number; in_records: { time: string; quantity: number; rolls: number }[]; } // 示例数据 - 用于展示参考 const demoInventory: FabricInventory[] = [ { id: 'demo-1', plan_id: 'plan-1', fabric_code: 'SX1', plan_code: 'PLAN-2026-199465', weight: 280, target_quantity: 10000, completed_quantity: 7400, in_records: [ { time: '2026-05-23T15:30:00Z', quantity: 1800, rolls: 20 }, { time: '2026-05-22T09:15:00Z', quantity: 1350, rolls: 15 }, { time: '2026-05-21T14:20:00Z', quantity: 1250, rolls: 14 }, { time: '2026-05-20T11:00:00Z', quantity: 1500, rolls: 17 }, { time: '2026-05-19T16:30:00Z', quantity: 1500, rolls: 16 } ] }, { id: 'demo-2', plan_id: 'plan-2', fabric_code: 'SX1', plan_code: 'PLAN-2026-199466', weight: 280, target_quantity: 8000, completed_quantity: 5760, in_records: [ { time: '2026-05-22T16:00:00Z', quantity: 1620, rolls: 18 }, { time: '2026-05-20T11:30:00Z', quantity: 1260, rolls: 14 }, { time: '2026-05-18T09:00:00Z', quantity: 1440, rolls: 16 }, { time: '2026-05-16T14:00:00Z', quantity: 1440, rolls: 16 } ] }, { id: 'demo-3', plan_id: 'plan-3', fabric_code: 'ZT320', plan_code: 'PLAN-2026-199460', weight: 320, target_quantity: 5000, completed_quantity: 4500, in_records: [ { time: '2026-05-21T10:00:00Z', quantity: 2250, rolls: 25 }, { time: '2026-05-19T15:30:00Z', quantity: 1125, rolls: 12 }, { time: '2026-05-17T11:00:00Z', quantity: 1125, rolls: 13 } ] }, { id: 'demo-4', plan_id: 'plan-4', fabric_code: 'CM180', plan_code: 'PLAN-2026-199458', weight: 180, target_quantity: 6000, completed_quantity: 3600, in_records: [ { time: '2026-05-20T09:00:00Z', quantity: 1800, rolls: 20 }, { time: '2026-05-18T14:30:00Z', quantity: 900, rolls: 10 }, { time: '2026-05-16T10:00:00Z', quantity: 900, rolls: 10 } ] } ]; export function FabricWarehouse() { const navigate = useNavigate(); const { auth } = useAuth(); const [inventory, setInventory] = useState([]); const [loading, setLoading] = useState(true); const [expandedItems, setExpandedItems] = useState>({}); const [recordsModalOpen, setRecordsModalOpen] = useState(false); const [selectedRecords, setSelectedRecords] = useState([]); const [selectedPlanCode, setSelectedPlanCode] = useState(''); useEffect(() => { if (!auth.company) return; fetchFabricInventory(); // 订阅入库记录变化,实现实时更新 const subscription = supabase .channel('inventory_records_changes') .on( 'postgres_changes', { event: '*', schema: 'public', table: 'inventory_records' }, () => { // 有入库记录变化时刷新数据 fetchFabricInventory(); } ) .subscribe(); return () => { subscription.unsubscribe(); }; }, [auth.company]); const fetchFabricInventory = async () => { setLoading(true); // 获取纺织厂关联的所有计划 const { data: planFactories } = await supabase .from('plan_factories') .select('plan_id') .eq('factory_id', auth.company!.id) .eq('factory_type', 'textile'); if (!planFactories || planFactories.length === 0) { setInventory([]); setLoading(false); return; } const planIds = planFactories.map(pf => pf.plan_id); // 获取计划对应的产品信息 const { data: plansData } = await supabase .from('production_plans') .select('id, plan_code, fabric_code, target_quantity, completed_quantity, purchaser_id') .in('id', planIds); if (!plansData || plansData.length === 0) { setInventory([]); setLoading(false); return; } // 获取采购商的产品信息(克重) const purchaserIds = [...new Set(plansData.map(p => p.purchaser_id))]; const fabricCodes = plansData.map(p => p.fabric_code); const { data: productsData } = await supabase .from('products') .select('fabric_code, weight, company_id') .in('company_id', purchaserIds) .in('fabric_code', fabricCodes); const productWeightMap: Record = {}; productsData?.forEach(p => { productWeightMap[p.fabric_code] = p.weight; }); // 获取入库记录 - 从 inventory_records 表查询 const { data: inventoryRecords } = await supabase .from('inventory_records') .select('plan_id, quantity, rolls, created_at') .in('plan_id', planIds) .order('created_at', { ascending: false }); // 按 plan_id 分组入库记录 const recordsByPlan: Record = {}; inventoryRecords?.forEach(record => { if (!recordsByPlan[record.plan_id]) { recordsByPlan[record.plan_id] = []; } recordsByPlan[record.plan_id].push({ time: record.created_at, quantity: Number(record.quantity) || 0, rolls: Number(record.rolls) || 0 }); }); // 构建库存数据 - 每个计划一条记录 // 以 inventory_records 实际入库记录为准,计算准确的已完成数量 const inventoryList: FabricInventory[] = plansData.map(plan => { const records = recordsByPlan[plan.id] || []; // 从实际入库记录计算总米数和总匹/件数 const totalInFromRecords = records.reduce((sum, r) => sum + r.quantity, 0); const totalRollsFromRecords = records.reduce((sum, r) => sum + r.rolls, 0); // 使用实际入库记录的总和作为已完成数量(更准确) const completedQty = totalInFromRecords > 0 ? totalInFromRecords : (plan.completed_quantity || 0); return { id: plan.id, plan_id: plan.id, fabric_code: plan.fabric_code, plan_code: plan.plan_code, weight: productWeightMap[plan.fabric_code] || 0, target_quantity: plan.target_quantity, completed_quantity: completedQty, // 使用实际入库记录的总和 in_records: records }; }); setInventory(inventoryList); // 如果没有真实数据,使用示例数据 if (inventoryList.length === 0) { setInventory(demoInventory); } setLoading(false); }; const toggleExpand = (planId: string) => { setExpandedItems(prev => ({ ...prev, [planId]: !prev[planId] })); }; // 按坯布识别码分组 const groupByFabricCode = () => { const groups: Record = {}; inventory.forEach(item => { const key = item.fabric_code; if (!groups[key]) { groups[key] = []; } groups[key].push(item); }); return groups; }; const fabricGroups = groupByFabricCode(); return (
{/* Header */}
navigate('/textile')} className="p-2 hover:bg-gray-100 rounded-lg transition-colors will-change-transform" >

已生产坯布

{loading ? (

加载中...

) : inventory.length === 0 ? (

暂无原坯布库存

关联的计划暂无入库记录

) : (
{Object.entries(fabricGroups).map(([fabricCode, items], groupIndex) => { const isExpanded = expandedItems[fabricCode]; const totalInMeters = items.reduce((sum, item) => sum + item.completed_quantity, 0); const totalTargetMeters = items.reduce((sum, item) => sum + item.target_quantity, 0); return ( {/* 分组头部 */} toggleExpand(fabricCode)} whileTap={{ scale: 0.995 }} className="flex items-center justify-between p-4 sm:p-5 cursor-pointer hover:bg-gray-50/50 transition-colors will-change-transform" >

{fabricCode}

已入库 {totalInMeters.toLocaleString()} 米 · 计划 {totalTargetMeters.toLocaleString()} 米

{items.length} 个计划
{/* 展开的库存列表 - 移动端卡片式,桌面端表格 */} {isExpanded && ( {/* 桌面端表格 */}
{items.map((item, itemIndex) => ( ))}
计划编号 规格 计划/入库 入库记录
{item.plan_code}

{item.weight}g

{item.target_quantity.toLocaleString()}米 / {item.completed_quantity.toLocaleString()}米
{item.in_records.length > 0 ? (
{new Date(item.in_records[0].time).toLocaleDateString('zh-CN')} {item.in_records[0].quantity}米 / {item.in_records[0].rolls}匹/件 {item.in_records.length > 1 && ( )}
) : ( 暂无入库 )}
{/* 移动端卡片 */}
{items.map((item, itemIndex) => (
{item.plan_code} {item.weight}g
计划 {item.target_quantity.toLocaleString()}米
已入 {item.completed_quantity.toLocaleString()}米
{item.in_records.length > 0 ? (
{new Date(item.in_records[0].time).toLocaleDateString('zh-CN')} {item.in_records[0].quantity}米 / {item.in_records[0].rolls}匹/件
{item.in_records.length > 1 && ( )}
) : (
暂无入库
)}
))}
)}
); })}
)}
{/* 入库记录弹窗 */} setRecordsModalOpen(false)} records={selectedRecords} title={`${selectedPlanCode} - 入库记录`} />
); }