import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { purchaserApi, http } from '../../api'; import { motion, AnimatePresence } from 'framer-motion'; import { ArrowLeft, ChevronDown, ChevronRight, DollarSign, Package, CheckCircle, Clock, AlertCircle } from 'lucide-react'; interface AccountsPayable { id: string; plan_id: string; textile_factory_id: string; factory_name: string; total_amount: number; paid_amount: number; unpaid_amount: number; total_quantity: number; paid_quantity: number; unpaid_quantity: number; price_per_meter: number; status: string; created_at: string; updated_at: string; plan_code: string; product_name: string; color: string; } interface AccountsPayableItem { id: string; inventory_record_id: string | null; quantity: number; rolls: number | null; price_per_meter: number; amount: number; status: string; created_at: string; paid_at: string | null; } export function AccountsPayable() { const navigate = useNavigate(); const { auth } = useAuth(); const [payables, setPayables] = useState([]); const [loading, setLoading] = useState(true); const [expandedPlan, setExpandedPlan] = useState(null); const [payableItems, setPayableItems] = useState>({}); useEffect(() => { if (!auth.company) return; fetchPayables(); }, [auth.company]); const fetchPayables = async () => { setLoading(true); try { const res = await purchaserApi.listAccountsPayable(); setPayables((res.data || []) as AccountsPayable[]); } catch { setPayables([]); } setLoading(false); }; const fetchPayableItems = async (payableId: string) => { try { const res = await http.get( `/purchaser/accounts-payable/${payableId}/items` ); setPayableItems(prev => ({ ...prev, [payableId]: res.data || [] })); } catch { setPayableItems(prev => ({ ...prev, [payableId]: [] })); } }; const toggleExpand = (payableId: string) => { if (expandedPlan === payableId) { setExpandedPlan(null); } else { setExpandedPlan(payableId); if (!payableItems[payableId]) { fetchPayableItems(payableId); } } }; const getStatusConfig = (status: string) => { switch (status) { case 'paid': return { label: '已结清', color: 'text-green-600', bgColor: 'bg-green-50', icon: CheckCircle }; case 'partial': return { label: '部分付款', color: 'text-amber-600', bgColor: 'bg-amber-50', icon: Clock }; default: return { label: '未付款', color: 'text-red-600', bgColor: 'bg-red-50', icon: AlertCircle }; } }; // 统计 const totalUnpaid = payables.reduce((sum, p) => sum + p.unpaid_amount, 0); const totalPaid = payables.reduce((sum, p) => sum + p.paid_amount, 0); return (
{/* Header */}
navigate('/purchaser')} className="p-2 hover:bg-gray-200 rounded-full transition-colors will-change-transform" >

应付账款

{/* 统计卡片 */}
未付金额

¥{totalUnpaid.toFixed(2)}

已付金额

¥{totalPaid.toFixed(2)}

{/* 应付账款列表 */} {loading ? (

加载中...

) : payables.length === 0 ? (

暂无应付账款

生产记录将自动生成应付账款

) : (
{payables.map((payable, index) => { const statusConfig = getStatusConfig(payable.status); const StatusIcon = statusConfig.icon; const isExpanded = expandedPlan === payable.id; const items = payableItems[payable.id] || []; return ( {/* 头部 */} toggleExpand(payable.id)} whileTap={{ scale: 0.995 }} className="flex items-center justify-between p-3 sm:p-4 cursor-pointer hover:bg-gray-50 transition-colors will-change-transform" >

{payable.product_name}-{payable.color}

{statusConfig.label}

{payable.plan_code} · {payable.factory_name}

¥{payable.unpaid_amount.toFixed(2)}

未付

{/* 展开的明细 */} {isExpanded && (
{/* 汇总信息 */}

总数量

{payable.total_quantity}米

单价

¥{payable.price_per_meter}/米

总金额

¥{payable.total_amount.toFixed(2)}

{/* 入库明细 */}

入库明细

{items.length === 0 ? (

暂无明细

) : (
{items.map((item, itemIndex) => (
{new Date(item.created_at).toLocaleDateString('zh-CN')} {item.quantity}米 / {item.rolls}匹/件
¥{item.price_per_meter}/米 ¥{item.amount.toFixed(2)} {item.status === 'paid' ? ( 已付 ) : ( 未付 )}
))}
)}
)}
); })}
)}
); }