feat(mining-admin-web): update contribution records display to match backend API

- Update ContributionRecord type to match backend response fields (sourceType, baseContribution, distributionRate, etc.)
- Update contribution-records-list component with improved UI showing source type badges, user info, tree count, and expiry status

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-12 07:30:00 -08:00
parent dbe9ab223f
commit 04fd7b946a
2 changed files with 64 additions and 29 deletions

View File

@ -3,22 +3,26 @@
import { useState } from 'react';
import { useContributionRecords } from '../hooks/use-users';
import { formatDecimal, formatPercent } from '@/lib/utils/format';
import { formatDateTime } from '@/lib/utils/date';
import { formatDateTime, formatDate } from '@/lib/utils/date';
import { Card, CardContent } from '@/components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { Badge } from '@/components/ui/badge';
import { ChevronLeft, ChevronRight } from 'lucide-react';
const contributionTypeLabels: Record<string, string> = {
PERSONAL: '个人',
SYSTEM_OPERATION: '系统运营',
SYSTEM_PROVINCE: '系统省级',
SYSTEM_CITY: '系统市级',
const sourceTypeLabels: Record<string, string> = {
PERSONAL: '个人认种',
TEAM_LEVEL: '团队层级',
TEAM_BONUS: '团队奖励',
};
const sourceTypeBadgeVariant: Record<string, 'default' | 'secondary' | 'outline'> = {
PERSONAL: 'default',
TEAM_LEVEL: 'secondary',
TEAM_BONUS: 'outline',
};
interface ContributionRecordsListProps {
accountSequence: string;
}
@ -35,20 +39,22 @@ export function ContributionRecordsList({ accountSequence }: ContributionRecords
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead>/</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{isLoading ? (
[...Array(5)].map((_, i) => (
<TableRow key={i}>
{[...Array(7)].map((_, j) => (
{[...Array(9)].map((_, j) => (
<TableCell key={j}>
<Skeleton className="h-4 w-full" />
</TableCell>
@ -57,18 +63,34 @@ export function ContributionRecordsList({ accountSequence }: ContributionRecords
))
) : data?.items.length === 0 ? (
<TableRow>
<TableCell colSpan={7} className="text-center py-8 text-muted-foreground">
<TableCell colSpan={9} className="text-center py-8 text-muted-foreground">
</TableCell>
</TableRow>
) : (
data?.items.map((record) => (
<TableRow key={record.id}>
<TableCell className="font-mono">{record.sourceAccountSequence}</TableCell>
<TableCell>{contributionTypeLabels[record.contributionType] || record.contributionType}</TableCell>
<TableCell>{record.distributionType === 'UPSTREAM' ? '上游' : '下游'}</TableCell>
<TableCell className="text-right">{formatPercent(record.rate)}</TableCell>
<TableCell className="text-right font-mono">{formatDecimal(record.amount, 4)}</TableCell>
<TableRow key={record.id} className={record.isExpired ? 'opacity-50' : ''}>
<TableCell>
<Badge variant={sourceTypeBadgeVariant[record.sourceType] || 'default'}>
{sourceTypeLabels[record.sourceType] || record.sourceType}
</Badge>
</TableCell>
<TableCell>
{record.sourceType === 'PERSONAL' ? (
<span className="text-muted-foreground"></span>
) : record.sourceUserInfo ? (
<div className="flex flex-col">
<span className="font-medium">{record.sourceUserInfo.nickname || record.sourceUserInfo.phone}</span>
<span className="text-xs text-muted-foreground font-mono">{record.sourceAccountSequence}</span>
</div>
) : (
<span className="font-mono text-sm">{record.sourceAccountSequence}</span>
)}
</TableCell>
<TableCell className="text-right">{record.treeCount}</TableCell>
<TableCell className="text-right font-mono">{formatDecimal(record.baseContribution, 2)}</TableCell>
<TableCell className="text-right">{formatPercent(record.distributionRate)}</TableCell>
<TableCell className="text-right font-mono font-medium">{formatDecimal(record.amount, 4)}</TableCell>
<TableCell>
{record.levelDepth !== null
? `L${record.levelDepth}`
@ -76,7 +98,14 @@ export function ContributionRecordsList({ accountSequence }: ContributionRecords
? `T${record.bonusTier}`
: '-'}
</TableCell>
<TableCell className="text-sm">{formatDateTime(record.createdAt)}</TableCell>
<TableCell className="text-sm">{formatDate(record.effectiveDate)}</TableCell>
<TableCell>
{record.isExpired ? (
<Badge variant="destructive"></Badge>
) : (
<Badge variant="secondary"></Badge>
)}
</TableCell>
</TableRow>
))
)}

View File

@ -65,15 +65,21 @@ export interface ContributionBreakdown {
export interface ContributionRecord {
id: string;
accountSequence: number;
sourceAccountSequence: number;
adoptionId: string;
contributionType: string;
distributionType: string;
amount: string;
rate: string;
sourceType: string; // PERSONAL, TEAM_LEVEL, TEAM_BONUS
sourceAccountSequence: string;
sourceUserInfo?: {
nickname: string | null;
phone: string;
} | null;
treeCount: number;
baseContribution: string;
distributionRate: string;
levelDepth: number | null;
bonusTier: number | null;
amount: string;
effectiveDate: string;
expireDate: string;
isExpired: boolean;
createdAt: string;
}