177 lines
6.8 KiB
TypeScript
177 lines
6.8 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { Table, Tag, Empty, Spin, Typography, Row, Col, Pagination } from 'antd';
|
|
import { WalletOutlined, PlusCircleOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
|
import { getCredits } from '../api';
|
|
|
|
const CreditRecordsPage: React.FC = () => {
|
|
const [loading, setLoading] = useState(true);
|
|
const [records, setRecords] = useState<any[]>([]);
|
|
const [credits, setCredits] = useState(0);
|
|
const [total, setTotal] = useState(0);
|
|
const [page, setPage] = useState(1);
|
|
const [pageSize] = useState(10);
|
|
|
|
useEffect(() => {
|
|
loadData();
|
|
}, [page]);
|
|
|
|
const loadData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const data = await getCredits(page, pageSize);
|
|
setRecords(data.records || []);
|
|
setCredits(data.credits || 0);
|
|
setTotal(data.total || 0);
|
|
} catch {
|
|
setRecords([]);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
const handlePageChange = (newPage: number) => {
|
|
setPage(newPage);
|
|
};
|
|
|
|
const columns = [
|
|
{
|
|
title: '变动类型',
|
|
dataIndex: 'type',
|
|
key: 'type',
|
|
width: 120,
|
|
render: (text: string) => {
|
|
const typeConfig: Record<string, { color: string; label: string; bg: string }> = {
|
|
recharge: { color: '#10b981', label: '充值', bg: 'rgba(16,185,129,0.1)' },
|
|
consume: { color: '#ef4444', label: '消费', bg: 'rgba(239,68,68,0.1)' },
|
|
admin: { color: '#f59e0b', label: '管理员调整', bg: 'rgba(245,158,11,0.1)' },
|
|
refund: { color: '#8b5cf6', label: '退款', bg: 'rgba(139,92,246,0.1)' },
|
|
team_internal: { color: '#0958d9', label: '团队内部', bg: 'rgba(9, 88, 217, 0.1)' },
|
|
};
|
|
const config = typeConfig[text] || { color: '#64748b', label: text, bg: 'rgba(100,116,139,0.1)' };
|
|
return (
|
|
<Tag color={config.color} style={{ background: config.bg, fontWeight: 500, padding: '4px 12px' }}>
|
|
{config.label}
|
|
</Tag>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: '描述',
|
|
dataIndex: 'description',
|
|
key: 'description',
|
|
ellipsis: true,
|
|
},
|
|
{
|
|
title: '变动积分',
|
|
dataIndex: 'amount',
|
|
key: 'amount',
|
|
width: 120,
|
|
align: 'right' as const,
|
|
render: (amount: number) => (
|
|
<Typography.Text strong style={{ fontWeight: 700, color: amount > 0 ? '#10b981' : '#ef4444', fontSize: 15 }}>
|
|
{amount > 0 ? '+' : ''}{amount}
|
|
</Typography.Text>
|
|
),
|
|
},
|
|
{
|
|
title: '创建时间',
|
|
key: 'createdAt',
|
|
width: 180,
|
|
render: (_, record: any) => {
|
|
const createdAt = record.created_at || record.createdAt || record.create_time || record.createTime;
|
|
return createdAt ? new Date(createdAt).toLocaleString('zh-CN') : '-';
|
|
},
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div>
|
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
|
<Col xs={24} sm={8}>
|
|
<div style={{
|
|
borderRadius: 16, padding: 24,
|
|
background: 'linear-gradient(135deg, #f59e0b 0%, #ea580c 100%)',
|
|
position: 'relative', overflow: 'hidden',
|
|
}}>
|
|
<div style={{ position: 'absolute', right: -20, top: -20, width: 120, height: 120, borderRadius: '50%', background: 'rgba(255,255,255,0.1)' }} />
|
|
<div style={{ position: 'relative' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
|
<WalletOutlined style={{ color: 'rgba(255,255,255,0.8)', fontSize: 16 }} />
|
|
<span style={{ color: 'rgba(255,255,255,0.8)', fontSize: 14 }}>可用积分</span>
|
|
</div>
|
|
<div style={{ color: '#fff', fontSize: 38, fontWeight: 800, lineHeight: 1 }}>
|
|
{credits.toLocaleString()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
<Col xs={12} sm={8}>
|
|
<div style={{
|
|
borderRadius: 16, padding: 24, background: '#fff', border: '1px solid #f0f0f5',
|
|
}}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
|
<div style={{ width: 32, height: 32, borderRadius: 8, background: 'rgba(16,185,129,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<PlusCircleOutlined style={{ color: '#10b981', fontSize: 14 }} />
|
|
</div>
|
|
<span style={{ color: '#94a3b8', fontSize: 14 }}>累计获得</span>
|
|
</div>
|
|
<div style={{ color: '#10b981', fontSize: 28, fontWeight: 800 }}>
|
|
{records.filter((r: any) => r.amount > 0).reduce((sum: number, r: any) => sum + r.amount, 0).toLocaleString()}
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
<Col xs={12} sm={8}>
|
|
<div style={{
|
|
borderRadius: 16, padding: 24, background: '#fff', border: '1px solid #f0f0f5',
|
|
}}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
|
<div style={{ width: 32, height: 32, borderRadius: 8, background: 'rgba(239,68,68,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<ThunderboltOutlined style={{ color: '#ef4444', fontSize: 14 }} />
|
|
</div>
|
|
<span style={{ color: '#94a3b8', fontSize: 14 }}>累计消耗</span>
|
|
</div>
|
|
<div style={{ color: '#ef4444', fontSize: 28, fontWeight: 800 }}>
|
|
{records.filter((r: any) => r.amount < 0).reduce((sum: number, r: any) => sum + Math.abs(r.amount), 0).toLocaleString()}
|
|
</div>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
|
|
<div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
|
|
<ThunderboltOutlined style={{ color: '#f59e0b', fontSize: 16 }} />
|
|
<Typography.Text strong style={{ fontSize: 16 }}>积分明细</Typography.Text>
|
|
</div>
|
|
<div style={{ borderRadius: 16, background: '#fff', border: '1px solid #f0f0f5', overflow: 'hidden' }}>
|
|
<Spin spinning={loading}>
|
|
{records.length === 0 ? (
|
|
<Empty
|
|
description={<span style={{ color: '#94a3b8' }}>暂无积分变动记录</span>}
|
|
/>
|
|
) : (
|
|
<>
|
|
<Table
|
|
dataSource={records}
|
|
columns={columns}
|
|
rowKey="id"
|
|
pagination={false}
|
|
bordered={false}
|
|
/>
|
|
<div style={{ padding: '16px', textAlign: 'right' }}>
|
|
<Pagination
|
|
current={page}
|
|
pageSize={pageSize}
|
|
total={total}
|
|
onChange={handlePageChange}
|
|
size="small"
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
</Spin>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CreditRecordsPage; |