import { useState } from 'react'; import { Card, Table, Tag, Button, Space, Select, Modal, Descriptions, Typography, Popconfirm, Form, Input, Spin, } from 'antd'; import { CheckCircleOutlined, LockOutlined, PlusOutlined, } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; import { useFinancialReports, useGenerateReport, useConfirmReport, useLockReport, type FinancialReportDto, } from '../../application'; const { Title, Text } = Typography; const STATUS_COLORS: Record = { DRAFT: 'default', CONFIRMED: 'success', LOCKED: 'purple', }; const STATUS_LABELS: Record = { DRAFT: '草稿', CONFIRMED: '已确认', LOCKED: '已锁定', }; export function ReportsPage() { const currentYear = dayjs().year(); const [selectedYear, setSelectedYear] = useState(currentYear); const [selectedStatus, setSelectedStatus] = useState(); const [detailModalOpen, setDetailModalOpen] = useState(false); const [selectedReport, setSelectedReport] = useState(null); const [generateModalOpen, setGenerateModalOpen] = useState(false); const [generateForm] = Form.useForm(); // Queries const { data: reports, isLoading } = useFinancialReports(selectedYear, selectedStatus); // Mutations const generateMutation = useGenerateReport(); const confirmMutation = useConfirmReport(); const lockMutation = useLockReport(); const showDetail = (report: FinancialReportDto) => { setSelectedReport(report); setDetailModalOpen(true); }; const handleGenerate = async () => { try { const values = await generateForm.validateFields(); generateMutation.mutate(values.month); setGenerateModalOpen(false); generateForm.resetFields(); } catch { // Form validation failed } }; const columns: ColumnsType = [ { title: '月份', dataIndex: 'reportMonth', key: 'reportMonth', width: 100, }, { title: '总收入', dataIndex: 'totalRevenue', key: 'totalRevenue', render: (v) => `¥${Number(v).toFixed(2)}`, }, { title: '退款', dataIndex: 'totalRefunds', key: 'totalRefunds', render: (v) => `¥${Number(v).toFixed(2)}`, }, { title: '净收入', dataIndex: 'netRevenue', key: 'netRevenue', render: (v) => `¥${Number(v).toFixed(2)}`, }, { title: '总成本', dataIndex: 'totalCosts', key: 'totalCosts', render: (v) => `¥${Number(v).toFixed(2)}`, }, { title: '毛利', dataIndex: 'grossProfit', key: 'grossProfit', render: (v) => ( = 0 ? '#52c41a' : '#f5222d' }}> ¥{Number(v).toFixed(2)} ), }, { title: '毛利率', dataIndex: 'grossMargin', key: 'grossMargin', render: (v) => `${(Number(v) * 100).toFixed(1)}%`, }, { title: '状态', dataIndex: 'status', key: 'status', render: (status) => ( {STATUS_LABELS[status] || status} ), }, { title: '操作', key: 'action', width: 200, render: (_, record) => ( {record.status === 'DRAFT' && ( confirmMutation.mutate(record.reportMonth)} okText="确认" cancelText="取消" > )} {record.status === 'CONFIRMED' && ( lockMutation.mutate(record.reportMonth)} okText="锁定" cancelText="取消" > )} ), }, ]; // Generate year options (last 3 years) const yearOptions = Array.from({ length: 3 }, (_, i) => ({ value: currentYear - i, label: `${currentYear - i}年`, })); return (
财务报表
{/* Filters */} 年份: {/* Reports Table */} `共 ${total} 条`, }} /> {/* Detail Modal */} setDetailModalOpen(false)} footer={[ , ]} width={800} > {selectedReport && (
{selectedReport.reportMonth} {STATUS_LABELS[selectedReport.status]} ¥{Number(selectedReport.totalRevenue).toFixed(2)} ¥{Number(selectedReport.assessmentRevenue).toFixed(2)} ¥{Number(selectedReport.consultationRevenue).toFixed(2)} ¥{Number(selectedReport.otherRevenue).toFixed(2)} ¥{Number(selectedReport.totalRefunds).toFixed(2)} ¥{Number(selectedReport.netRevenue).toFixed(2)} ¥{Number(selectedReport.apiCost).toFixed(2)} ¥{Number(selectedReport.paymentFees).toFixed(2)} ¥{Number(selectedReport.otherCosts).toFixed(2)} ¥{Number(selectedReport.totalCosts).toFixed(2)} = 0 ? '#52c41a' : '#f5222d' }}> ¥{Number(selectedReport.grossProfit).toFixed(2)} {(Number(selectedReport.grossMargin) * 100).toFixed(1)}% {selectedReport.totalOrders} {selectedReport.successfulOrders} ¥{Number(selectedReport.avgOrderAmount).toFixed(2)} {selectedReport.confirmedBy || '-'} {selectedReport.confirmedAt ? dayjs(selectedReport.confirmedAt).format('YYYY-MM-DD HH:mm:ss') : '-'} {dayjs(selectedReport.createdAt).format('YYYY-MM-DD HH:mm:ss')} {/* Revenue by Category */}
按类别收入
{Object.entries(selectedReport.revenueByCategory || {}).length > 0 ? ( Object.entries(selectedReport.revenueByCategory).map(([key, value]) => ( {key}: ¥{Number(value).toFixed(2)} )) ) : ( 暂无数据 )}
{/* Revenue by Channel */}
按渠道收入
{Object.entries(selectedReport.revenueByChannel || {}).length > 0 ? ( Object.entries(selectedReport.revenueByChannel).map(([key, value]) => ( {key}: ¥{Number(value).toFixed(2)} )) ) : ( 暂无数据 )}
{/* Notes */} {selectedReport.notes && (
备注
{selectedReport.notes}
)}
)}
{/* Generate Report Modal */} { setGenerateModalOpen(false); generateForm.resetFields(); }} confirmLoading={generateMutation.isPending} >

生成指定月份的财务报表。如果报表已存在且为草稿状态,将会更新数据。

); }