import React, { useEffect, useState, useMemo } from 'react'; import { Table, Modal, Form, Select, DatePicker, InputNumber, Input, Space, message, Tag, Popconfirm, Tabs, List, Card, } from 'antd'; import { PlusOutlined, DeleteOutlined, DollarOutlined, CheckOutlined } from '@ant-design/icons'; import dayjs from 'dayjs'; import api from '../../api'; import PermissionButton from '../../components/PermissionButton'; const statusMap: Record = { paid: { text: '已缴', color: 'green' }, refunded: { text: '已全退', color: 'blue' }, partial_refund: { text: '部分退还', color: 'orange' }, deducted: { text: '已全扣', color: 'red' }, }; const refundStatusMap: Record = { pending: { text: '待班主任审批', color: 'orange' }, head_teacher_approved: { text: '待财务审批', color: 'blue' }, finance_approved: { text: '已退款', color: 'green' }, refunded: { text: '已退款', color: 'green' }, }; const installmentStatusMap: Record = { pending: { text: '待缴', color: 'orange' }, paid: { text: '已缴', color: 'green' }, }; interface PendingRefund { id: number; student?: { name?: string }; amount?: number; paidDate?: string; refundStatus?: string; refundRequestedAt?: string; } const DepositsPage: React.FC = () => { const [data, setData] = useState([]); const [students, setStudents] = useState([]); const [loading, setLoading] = useState(false); const [createModal, setCreateModal] = useState(false); const [refundModal, setRefundModal] = useState(null); const [detailModal, setDetailModal] = useState(null); const [installmentModal, setInstallmentModal] = useState(null); const [pendingRefunds, setPendingRefunds] = useState([]); const [pendingLoading, setPendingLoading] = useState(false); const [rejectModal, setRejectModal] = useState(null); const [rejectReason, setRejectReason] = useState(''); const [createForm] = Form.useForm(); const [refundForm] = Form.useForm(); const [installmentForm] = Form.useForm(); const [searchText, setSearchText] = useState(''); const [filterStatus, setFilterStatus] = useState(undefined); const [activeTab, setActiveTab] = useState('all'); const fetchData = async () => { setLoading(true); try { const [d, s]: any[] = await Promise.all([api.get('/deposits'), api.get('/students')]); setData(d); setStudents(s); } catch (e) { console.error(e); } setLoading(false); }; const fetchPendingRefunds = async () => { setPendingLoading(true); try { const res = await api.get('/deposits/pending-refunds'); setPendingRefunds(res || []); } catch (e) { console.error(e); } setPendingLoading(false); }; useEffect(() => { fetchData(); }, []); const filteredData = useMemo(() => { return data.filter((d: any) => { if (searchText) { const s = searchText.toLowerCase(); if (!d.student?.name?.toLowerCase().includes(s)) return false; } if (filterStatus && d.status !== filterStatus) return false; return true; }); }, [data, searchText, filterStatus]); const handleCreate = async () => { const values = await createForm.validateFields(); try { await api.post('/deposits', { studentId: values.studentId, amount: values.amount, paidDate: values.paidDate.format('YYYY-MM-DD'), notes: values.notes, }); message.success('押金记录已创建'); setCreateModal(false); createForm.resetFields(); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const handleRefund = async () => { const values = await refundForm.validateFields(); try { await api.put(`/deposits/${refundModal.id}/refund`, { refundDate: values.refundDate.format('YYYY-MM-DD'), deductionAmount: values.deductionAmount || 0, deductionReason: values.deductionReason, notes: values.notes, }); message.success('退还操作完成'); setRefundModal(null); refundForm.resetFields(); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const handleRequestRefund = async (record: any) => { try { await api.post(`/deposits/${record.id}/request-refund`); message.success('退款申请已提交'); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const handleApproveRefund = async (record: any) => { try { await api.put(`/deposits/${record.id}/approve-refund`); message.success('审批通过'); fetchPendingRefunds(); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const handleRejectRefund = async () => { if (!rejectModal) return; try { await api.put(`/deposits/${rejectModal.id}/reject-refund`, { reason: rejectReason || '未说明原因' }); message.success('已驳回退款申请'); setRejectModal(null); setRejectReason(''); fetchPendingRefunds(); fetchData(); } catch (e: any) { message.error(e?.message || '驳回失败'); } }; const handleAddInstallment = async () => { if (installmentModal == null) return; const values = await installmentForm.validateFields(); try { await api.post(`/deposits/${installmentModal}/installments`, { amount: values.amount, dueDate: values.dueDate.format('YYYY-MM-DD'), }); message.success('分期已添加'); setInstallmentModal(null); installmentForm.resetFields(); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const handlePayInstallment = async (installmentId: number) => { try { await api.put(`/deposits/installments/${installmentId}`, { paidDate: dayjs().format('YYYY-MM-DD'), status: 'paid', }); message.success('分期已标记为已缴'); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const handleDeleteInstallment = async (installmentId: number) => { try { await api.delete(`/deposits/installments/${installmentId}`); message.success('分期已删除'); fetchData(); } catch (e: any) { message.error(e?.message || '操作失败'); } }; const columns = [ { title: '学生', render: (_: any, r: any) => r.student?.name || '-' }, { title: '押金金额', dataIndex: 'amount', render: (v: number) => `¥${Number(v).toFixed(2)}` }, { title: '缴纳日期', dataIndex: 'paidDate' }, { title: '状态', dataIndex: 'status', render: (s: string) => {statusMap[s]?.text || s}, }, { title: '退款审批', dataIndex: 'refundStatus', render: (s: string) => s ? {refundStatusMap[s]?.text || s} : '-', }, { title: '退还金额', dataIndex: 'refundAmount', render: (v: any) => (v != null ? `¥${Number(v).toFixed(2)}` : '-'), }, { title: '扣除金额', dataIndex: 'deductionAmount', render: (v: any) => (v > 0 ? `¥${Number(v).toFixed(2)}` : '-'), }, { title: '扣除原因', dataIndex: 'deductionReason', render: (v: any) => v || '-' }, { title: '退还日期', dataIndex: 'refundDate', render: (v: any) => v || '-' }, { title: '备注', dataIndex: 'notes', render: (v: any) => v || '-' }, { title: '操作', width: 240, render: (_: any, record: any) => ( { setDetailModal(record); }} > 详情 {record.status === 'paid' && !record.refundStatus && ( <> { setRefundModal(record); refundForm.setFieldsValue({ refundDate: dayjs(), deductionAmount: 0 }); }} > 退还 handleRequestRefund(record)} > 申请退款 )} { try { await api.delete(`/deposits/${record.id}`); message.success('删除成功'); fetchData(); } catch (e: any) { message.error(e?.message || '删除失败'); } }} > } > 删除 ), }, ]; const pendingColumns = [ { title: '学生', render: (_: any, r: any) => r.student?.name || '-' }, { title: '押金金额', dataIndex: 'amount', render: (v: number) => `¥${Number(v).toFixed(2)}` }, { title: '缴纳日期', dataIndex: 'paidDate' }, { title: '审批状态', dataIndex: 'refundStatus', render: (s: string) => {refundStatusMap[s]?.text || s}, }, { title: '申请时间', dataIndex: 'refundRequestedAt', render: (v: any) => (v ? dayjs(v).format('YYYY-MM-DD HH:mm') : '-'), }, { title: '操作', render: (_: unknown, record: PendingRefund) => ( } onClick={() => handleApproveRefund(record)} > 审批通过 { setRejectModal(record); setRejectReason(''); }} > 驳回 ), }, ]; return (
{ setActiveTab(key); if (key === 'pending') fetchPendingRefunds(); }} items={[ { key: 'all', label: '押金列表', children: ( <>
setSearchText(v)} onChange={(e) => { if (!e.target.value) setSearchText(''); }} /> s.status === 'active') .map((s: any) => ({ value: s.id, label: `${s.name} (${s.idNumber || s.phone || ''})`, }))} /> {/* Refund Modal */} setRefundModal(null)} okText="确认退还" >
押金金额: ¥{Number(refundModal?.amount || 0).toFixed(2)}
{/* Detail Modal */} setDetailModal(null)} footer={null} width={640} > {detailModal && (

押金金额: ¥{Number(detailModal.amount).toFixed(2)}

缴纳日期: {detailModal.paidDate}

状态:{' '} {statusMap[detailModal.status]?.text || detailModal.status}

{detailModal.refundStatus && (

退款审批:{' '} {refundStatusMap[detailModal.refundStatus]?.text || detailModal.refundStatus}

)} {detailModal.notes &&

备注: {detailModal.notes}

}
{/* Installments Section */}

分期记录

} onClick={() => { setInstallmentModal(detailModal.id); installmentForm.resetFields(); }} > 添加分期
{detailModal.installments?.length > 0 ? ( ( } onClick={() => handlePayInstallment(item.id)} > 标记已缴 ), handleDeleteInstallment(item.id)} > }> 删除 ].filter(Boolean)} > {installmentStatusMap[item.status]?.text || item.status} )} /> ) : (

暂无分期记录

)}
)}
{/* Add Installment Modal */} setInstallmentModal(null)} okText="确认" >
{/* Reject Refund Modal */} { setRejectModal(null); setRejectReason(''); }} okText="确认驳回" okButtonProps={{ danger: true }} > setRejectReason(e.target.value)} rows={3} />
); }; export default DepositsPage;