2026-03-18 19:12:38 +08:00
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
import { Table, Button, Input, Select, DatePicker, message } from 'antd';
|
|
|
|
|
import { SearchOutlined, FilterOutlined, ExportOutlined } from '@ant-design/icons';
|
|
|
|
|
import { useNavigate } from 'react-router-dom';
|
2026-03-19 01:39:34 +08:00
|
|
|
import { financeDataSource, Transaction } from '@/services/financeDataSource';
|
2026-03-18 19:12:38 +08:00
|
|
|
|
|
|
|
|
const { Option } = Select;
|
|
|
|
|
const { RangePicker } = DatePicker;
|
|
|
|
|
const { Search } = Input;
|
|
|
|
|
|
|
|
|
|
const Transactions: React.FC = () => {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [filters, setFilters] = useState({
|
|
|
|
|
type: '',
|
|
|
|
|
category: '',
|
|
|
|
|
status: '',
|
|
|
|
|
search: '',
|
|
|
|
|
dateRange: null as any,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
fetchTransactions();
|
|
|
|
|
}, [filters]);
|
|
|
|
|
|
|
|
|
|
const fetchTransactions = async () => {
|
|
|
|
|
setLoading(true);
|
2026-03-19 01:39:34 +08:00
|
|
|
try {
|
|
|
|
|
const data = await financeDataSource.fetchTransactions(filters);
|
|
|
|
|
setTransactions(data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
message.error('Failed to load transactions');
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-03-18 19:12:38 +08:00
|
|
|
|
|
|
|
|
const handleExport = () => {
|
|
|
|
|
message.success('交易记录已导出');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const columns = [
|
|
|
|
|
{
|
|
|
|
|
title: '交易ID',
|
|
|
|
|
dataIndex: 'transactionId',
|
|
|
|
|
key: 'transactionId',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '金额',
|
|
|
|
|
dataIndex: 'amount',
|
|
|
|
|
key: 'amount',
|
|
|
|
|
render: (amount: number, record: Transaction) => (
|
|
|
|
|
<span style={{ color: record.type === 'income' ? '#3f8600' : '#cf1322' }}>
|
|
|
|
|
{record.type === 'income' ? '+' : '-'}$${amount.toFixed(2)}
|
|
|
|
|
</span>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '类型',
|
|
|
|
|
dataIndex: 'type',
|
|
|
|
|
key: 'type',
|
|
|
|
|
render: (type: string) => {
|
|
|
|
|
return type === 'income' ? '收入' : '支出';
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '分类',
|
|
|
|
|
dataIndex: 'category',
|
|
|
|
|
key: 'category',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '描述',
|
|
|
|
|
dataIndex: 'description',
|
|
|
|
|
key: 'description',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '日期',
|
|
|
|
|
dataIndex: 'date',
|
|
|
|
|
key: 'date',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '状态',
|
|
|
|
|
dataIndex: 'status',
|
|
|
|
|
key: 'status',
|
|
|
|
|
render: (status: string) => {
|
|
|
|
|
const statusMap = {
|
|
|
|
|
completed: '已完成',
|
|
|
|
|
pending: '待处理',
|
|
|
|
|
failed: '失败',
|
|
|
|
|
};
|
|
|
|
|
return statusMap[status] || status;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="transactions">
|
|
|
|
|
<div className="page-header">
|
|
|
|
|
<h1>交易记录</h1>
|
|
|
|
|
<Button
|
|
|
|
|
type="primary"
|
|
|
|
|
icon={<ExportOutlined />}
|
|
|
|
|
onClick={handleExport}
|
|
|
|
|
>
|
|
|
|
|
导出
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="filter-section" style={{ marginBottom: 24, display: 'flex', gap: 16, flexWrap: 'wrap' }}>
|
|
|
|
|
<Search
|
|
|
|
|
placeholder="搜索交易ID或描述"
|
|
|
|
|
style={{ width: 300 }}
|
|
|
|
|
onChange={(e) => setFilters({ ...filters, search: e.target.value })}
|
|
|
|
|
/>
|
|
|
|
|
<Select
|
|
|
|
|
placeholder="类型"
|
|
|
|
|
style={{ width: 120 }}
|
|
|
|
|
onChange={(value) => setFilters({ ...filters, type: value })}
|
|
|
|
|
>
|
|
|
|
|
<Option value="">全部</Option>
|
|
|
|
|
<Option value="income">收入</Option>
|
|
|
|
|
<Option value="expense">支出</Option>
|
|
|
|
|
</Select>
|
|
|
|
|
<Select
|
|
|
|
|
placeholder="分类"
|
|
|
|
|
style={{ width: 120 }}
|
|
|
|
|
onChange={(value) => setFilters({ ...filters, category: value })}
|
|
|
|
|
>
|
|
|
|
|
<Option value="">全部</Option>
|
|
|
|
|
<Option value="Sales">销售</Option>
|
|
|
|
|
<Option value="Suppliers">供应商</Option>
|
|
|
|
|
<Option value="Marketing">营销</Option>
|
|
|
|
|
<Option value="Shipping">物流</Option>
|
|
|
|
|
</Select>
|
|
|
|
|
<Select
|
|
|
|
|
placeholder="状态"
|
|
|
|
|
style={{ width: 120 }}
|
|
|
|
|
onChange={(value) => setFilters({ ...filters, status: value })}
|
|
|
|
|
>
|
|
|
|
|
<Option value="">全部</Option>
|
|
|
|
|
<Option value="completed">已完成</Option>
|
|
|
|
|
<Option value="pending">待处理</Option>
|
|
|
|
|
<Option value="failed">失败</Option>
|
|
|
|
|
</Select>
|
|
|
|
|
<RangePicker
|
|
|
|
|
style={{ width: 300 }}
|
|
|
|
|
onChange={(dates) => setFilters({ ...filters, dateRange: dates })}
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
icon={<FilterOutlined />}
|
|
|
|
|
onClick={fetchTransactions}
|
|
|
|
|
>
|
|
|
|
|
筛选
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Table
|
|
|
|
|
columns={columns}
|
|
|
|
|
dataSource={transactions}
|
|
|
|
|
loading={loading}
|
|
|
|
|
rowKey="id"
|
|
|
|
|
pagination={{
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
showSizeChanger: true,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default Transactions;
|