import React, { useState, useEffect, useMemo } from 'react'; import { Table, Button, Input, Select, DatePicker, message, Card, Row, Col, Tag, Space, Modal, Drawer, Descriptions, Typography, Alert, Statistic, Tooltip } from 'antd'; import { SearchOutlined, FilterOutlined, ExportOutlined, DownloadOutlined, EyeOutlined, CheckCircleOutlined, CloseCircleOutlined, SyncOutlined, DollarOutlined, ArrowUpOutlined, ArrowDownOutlined, ReloadOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import { financeDataSource, Transaction } from '@/services/financeDataSource'; import { LineChart, Line, Bar, Pie, ResponsiveContainer, Legend, XAxis, YAxis, CartesianGrid, Cell } from 'recharts'; import moment from 'moment'; const { Option } = Select; const { RangePicker } = DatePicker; const { Search } = Input; const { Title, Text } = Typography; const Transactions: React.FC = () => { const navigate = useNavigate(); const [transactions, setTransactions] = useState([]); const [loading, setLoading] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [detailDrawerVisible, setDetailDrawerVisible] = useState(false); const [currentTransaction, setCurrentTransaction] = useState(null); const [filters, setFilters] = useState({ type: '', category: '', status: '', search: '', dateRange: null as any, minAmount: null as number | null, maxAmount: null as number | null, }); useEffect(() => { fetchTransactions(); }, [filters]); const fetchTransactions = async () => { setLoading(true); try { const data = await financeDataSource.fetchTransactions(filters); setTransactions(data); } catch (error) { message.error('Failed to load transactions'); } finally { setLoading(false); } }; const handleExport = () => { message.success('交易记录已导出'); }; const handleBatchExport = () => { if (selectedRows.length === 0) { message.warning('请先选择要导出的交易'); return; } message.success(`成功导出 ${selectedRows.length} 条交易记录`); }; const handleViewDetail = (record: Transaction) => { setCurrentTransaction(record); setDetailDrawerVisible(true); }; const handleRefresh = () => { fetchTransactions(); message.success('数据已刷新'); }; const stats = useMemo(() => { const income = transactions.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0); const expense = transactions.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0); const pending = transactions.filter(t => t.status === 'pending').reduce((sum, t) => sum + t.amount, 0); return { income, expense, profit: income - expense, pending, total: transactions.length, }; }, [transactions]); const trendData = useMemo(() => { const trendData = Array.from({ length: 7 }, (_, i) => { const date = moment().subtract(i, 'days').format('YYYY-MM-DD'); const dayTransactions = transactions.filter(t => moment(t.createdAt).format('YYYY-MM-DD') === date); return { date, income: dayTransactions.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0), expense: dayTransactions.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0), }; }).reverse(); return trendData; }, [transactions]); const categoryData = useMemo(() => { const categoryMap: Record = {}; transactions.filter(t => t.type === 'expense').forEach(t => { categoryMap[t.category] = (categoryMap[t.category] || 0) + t.amount; }); return Object.entries(categoryMap).map(([name, value]) => ({ name, value })); }, [transactions]); const columns = [ { title: '交易ID', dataIndex: 'transactionId', key: 'transactionId', render: (text: string) => {text}, }, { title: '金额', dataIndex: 'amount', key: 'amount', sorter: (a: Transaction, b: Transaction) => a.amount - b.amount, render: (amount: number, record: Transaction) => ( {record.type === 'income' ? '+' : '-'}${amount.toFixed(2)} ), }, { title: '类型', dataIndex: 'type', key: 'type', filters: [ { text: '收入', value: 'income' }, { text: '支出', value: 'expense' }, ], render: (type: string) => { const typeConfig = { income: { text: '收入', color: 'success' }, expense: { text: '支出', color: 'error' }, }; const config = typeConfig[type as keyof typeof typeConfig]; return {config.text}; }, }, { title: '分类', dataIndex: 'category', key: 'category', filters: [ { text: '销售', value: 'Sales' }, { text: '供应商', value: 'Suppliers' }, { text: '营销', value: 'Marketing' }, { text: '物流', value: 'Shipping' }, { text: '平台费用', value: 'Platform' }, { text: '退款', value: 'Refund' }, ], }, { title: '描述', dataIndex: 'description', key: 'description', ellipsis: true, }, { title: '日期', dataIndex: 'date', key: 'date', sorter: (a: Transaction, b: Transaction) => moment(a.createdAt).unix() - moment(b.createdAt).unix(), render: (date: string) => moment(date).format('YYYY-MM-DD HH:mm'), }, { title: '状态', dataIndex: 'status', key: 'status', filters: [ { text: '已完成', value: 'completed' }, { text: '待处理', value: 'pending' }, { text: '失败', value: 'failed' }, ], render: (status: string) => { const statusConfig = { completed: { text: '已完成', color: 'success', icon: }, pending: { text: '待处理', color: 'warning', icon: }, failed: { text: '失败', color: 'error', icon: }, }; const config = statusConfig[status as keyof typeof statusConfig]; return {config.text}; }, }, { title: '操作', key: 'action', render: (_: any, record: Transaction) => ( ), }, ]; return (
交易记录 查看和管理所有财务交易 } valueStyle={{ color: '#52c41a' }} suffix="USD" /> } valueStyle={{ color: '#ff4d4f' }} suffix="USD" /> } valueStyle={{ color: stats.profit >= 0 ? '#52c41a' : '#ff4d4f' }} suffix="USD" /> } valueStyle={{ color: '#faad14' }} suffix="USD" /> `${name}: ${((percent || 0) * 100).toFixed(0)}%`} > {categoryData.map((entry, index) => ( ))} {selectedRows.length > 0 && ( } style={{ marginBottom: 16 }} /> )}
setFilters({ ...filters, search: value })} allowClear /> setFilters({ ...filters, dateRange: dates })} />
r.id), onChange: (selectedRowKeys: React.Key[], selectedRows: Transaction[]) => { setSelectedRows(selectedRows); }, }} pagination={{ pageSize: 20, showSizeChanger: true, showQuickJumper: true, showTotal: (total) => `共 ${total} 条`, pageSizeOptions: ['10', '20', '50', '100'], }} scroll={{ x: 1200 }} /> setDetailDrawerVisible(false)} visible={detailDrawerVisible} width={600} > {currentTransaction && ( {currentTransaction.id} {currentTransaction.type === 'income' ? '+' : '-'}${currentTransaction.amount.toFixed(2)} {currentTransaction.type === 'income' ? '收入' : '支出'} {currentTransaction.category} {currentTransaction.description} {moment(currentTransaction.createdAt).format('YYYY-MM-DD HH:mm:ss')} {currentTransaction.status === 'completed' ? '已完成' : currentTransaction.status === 'pending' ? '待处理' : '失败'} )} ); }; export default Transactions;