RechargeRecords.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { useState, useEffect } from 'react';
  2. import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
  3. import { Badge } from '@/client/components/ui/badge';
  4. import { Skeleton } from '@/client/components/ui/skeleton';
  5. import { Alert, AlertDescription } from '@/client/components/ui/alert';
  6. import { Clock, History, AlertCircle } from 'lucide-react';
  7. import { format } from 'date-fns';
  8. import { zhCN } from 'date-fns/locale';
  9. import { useAuth } from '@/client/home-shadcn/hooks/AuthProvider';
  10. interface PaymentRecord {
  11. id: string;
  12. amount: number;
  13. count: number;
  14. status: string;
  15. createdAt: string;
  16. expireAt?: string;
  17. paymentMethod?: string;
  18. transactionId?: string;
  19. }
  20. interface RechargeRecordsProps {
  21. userId: number;
  22. }
  23. export default function RechargeRecords({ userId }: RechargeRecordsProps) {
  24. const { token } = useAuth();
  25. const [records, setRecords] = useState<PaymentRecord[]>([]);
  26. const [loading, setLoading] = useState(true);
  27. const [error, setError] = useState<string | null>(null);
  28. useEffect(() => {
  29. fetchRechargeRecords();
  30. }, [userId]);
  31. const fetchRechargeRecords = async () => {
  32. try {
  33. setLoading(true);
  34. setError(null);
  35. // 这里应该调用实际的API获取充值记录
  36. // 暂时使用模拟数据
  37. const mockRecords: PaymentRecord[] = [
  38. {
  39. id: '1',
  40. amount: 99.9,
  41. count: 500,
  42. status: 'completed',
  43. createdAt: '2024-08-15T10:30:00Z',
  44. expireAt: '2025-08-15T23:59:59Z',
  45. paymentMethod: 'alipay',
  46. transactionId: '202408151030001234'
  47. },
  48. {
  49. id: '2',
  50. amount: 29.9,
  51. count: 100,
  52. status: 'completed',
  53. createdAt: '2024-08-01T14:20:00Z',
  54. paymentMethod: 'wechat',
  55. transactionId: '202408011420005678'
  56. },
  57. {
  58. id: '3',
  59. amount: 299.9,
  60. count: 2000,
  61. status: 'completed',
  62. createdAt: '2024-07-20T09:15:00Z',
  63. expireAt: '2025-07-20T23:59:59Z',
  64. paymentMethod: 'alipay',
  65. transactionId: '202407200915003210'
  66. }
  67. ];
  68. // 模拟API延迟
  69. setTimeout(() => {
  70. setRecords(mockRecords);
  71. setLoading(false);
  72. }, 800);
  73. } catch (err) {
  74. setError('获取充值记录失败,请稍后重试');
  75. setLoading(false);
  76. }
  77. };
  78. const getStatusBadge = (status: string) => {
  79. switch (status) {
  80. case 'completed':
  81. return <Badge className="bg-green-100 text-green-800">已完成</Badge>;
  82. case 'pending':
  83. return <Badge className="bg-yellow-100 text-yellow-800">处理中</Badge>;
  84. case 'failed':
  85. return <Badge className="bg-red-100 text-red-800">失败</Badge>;
  86. case 'refunded':
  87. return <Badge className="bg-gray-100 text-gray-800">已退款</Badge>;
  88. default:
  89. return <Badge>{status}</Badge>;
  90. }
  91. };
  92. const getPaymentMethodText = (method?: string) => {
  93. switch (method) {
  94. case 'alipay':
  95. return '支付宝';
  96. case 'wechat':
  97. return '微信支付';
  98. case 'bank':
  99. return '银行转账';
  100. default:
  101. return '未知方式';
  102. }
  103. };
  104. if (loading) {
  105. return (
  106. <div className="space-y-4">
  107. {[1, 2, 3].map((i) => (
  108. <Card key={i} className="border-0 shadow-sm">
  109. <CardContent className="p-4">
  110. <div className="space-y-2">
  111. <Skeleton className="h-4 w-1/3" />
  112. <Skeleton className="h-3 w-1/4" />
  113. <Skeleton className="h-3 w-1/5" />
  114. </div>
  115. </CardContent>
  116. </Card>
  117. ))}
  118. </div>
  119. );
  120. }
  121. if (error) {
  122. return (
  123. <Alert variant="destructive">
  124. <AlertCircle className="h-4 w-4" />
  125. <AlertDescription>{error}</AlertDescription>
  126. </Alert>
  127. );
  128. }
  129. if (records.length === 0) {
  130. return (
  131. <div className="text-center py-8 text-gray-500">
  132. <History className="h-12 w-12 mx-auto mb-2 text-gray-300" />
  133. <p>暂无充值记录</p>
  134. <p className="text-sm text-gray-400 mt-1">您还没有进行过充值</p>
  135. </div>
  136. );
  137. }
  138. return (
  139. <div className="space-y-4">
  140. {records.map((record) => (
  141. <Card key={record.id} className="border-0 shadow-sm hover:shadow-md transition-shadow">
  142. <CardContent className="p-4 space-y-3">
  143. <div className="flex justify-between items-start">
  144. <div className="flex-1">
  145. <div className="flex items-center space-x-2 mb-1">
  146. <p className="font-semibold text-lg">充值 {record.count} 次</p>
  147. {getStatusBadge(record.status)}
  148. </div>
  149. <p className="text-sm text-gray-500">
  150. {format(new Date(record.createdAt), 'yyyy年MM月dd日 HH:mm', { locale: zhCN })}
  151. </p>
  152. <p className="text-sm text-gray-500">
  153. 支付方式:{getPaymentMethodText(record.paymentMethod)}
  154. </p>
  155. {record.transactionId && (
  156. <p className="text-xs text-gray-400">
  157. 订单号:{record.transactionId}
  158. </p>
  159. )}
  160. </div>
  161. <div className="text-right">
  162. <p className="text-xl font-bold text-blue-600">¥{record.amount}</p>
  163. </div>
  164. </div>
  165. {record.expireAt && (
  166. <div className="flex items-center space-x-2 text-sm text-gray-600 bg-blue-50 p-2 rounded">
  167. <Clock className="h-4 w-4 text-blue-600" />
  168. <span>有效期至:{format(new Date(record.expireAt), 'yyyy年MM月dd日', { locale: zhCN })}</span>
  169. </div>
  170. )}
  171. </CardContent>
  172. </Card>
  173. ))}
  174. </div>
  175. );
  176. }