rwadurian/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.tsx

963 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 系统账户报表Tab组件
* [2026-01-04] 新增:显示系统账户统计数据
* [2026-01-05] 更新:添加分类账明细显示
* [2026-01-06] 更新:添加收益类型汇总统计(手续费、省团队、市团队、分享引荐、社区)
* 回滚方式:删除此文件及整个 system-account-report 目录
*/
'use client';
import { useState, useEffect, useCallback } from 'react';
import { systemAccountReportService } from '@/services/systemAccountReportService';
import type {
SystemAccountReportResponse,
RegionAccountsSummary,
AllSystemAccountsLedgerResponse,
FixedAccountLedger,
RegionAccountLedger,
LedgerEntryDTO,
AllRewardTypeSummariesResponse,
RewardTypeSummary,
FeeAccountSummary,
} from '@/types';
import { ENTRY_TYPE_LABELS, ACCOUNT_TYPE_LABELS, FEE_TYPE_LABELS } from '@/types';
import styles from './SystemAccountsTab.module.scss';
/**
* 格式化金额显示
*/
const formatAmount = (value: string | number | undefined): string => {
if (value === undefined || value === null) return '0.00';
const num = typeof value === 'string' ? parseFloat(value) : value;
if (isNaN(num)) return '0.00';
return num.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};
/**
* 权益类型显示名称
*/
const getRightTypeName = (type: string): string => {
const labels: Record<string, string> = {
SETTLEABLE: '可结算收益',
SHARE: '分享权益',
PROVINCE_TEAM: '省团队权益',
CITY_TEAM: '市团队权益',
PROVINCE_REGION: '省区域权益',
CITY_REGION: '市区域权益',
COMMUNITY: '社区权益',
};
return labels[type] || type;
};
/**
* 系统账户报表Tab组件
*/
// Tab类型定义
type TabType = 'fixed' | 'province' | 'city' | 'settlement' | 'expired' | 'ledger' | 'feeAccount' | 'provinceTeam' | 'cityTeam' | 'shareReferral' | 'community';
export default function SystemAccountsTab() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [reportData, setReportData] = useState<SystemAccountReportResponse | null>(null);
const [ledgerData, setLedgerData] = useState<AllSystemAccountsLedgerResponse | null>(null);
const [ledgerLoading, setLedgerLoading] = useState(false);
// [2026-01-06] 新增:收益类型汇总数据
const [rewardTypeSummaries, setRewardTypeSummaries] = useState<AllRewardTypeSummariesResponse | null>(null);
const [rewardTypeSummariesLoading, setRewardTypeSummariesLoading] = useState(false);
const [activeTab, setActiveTab] = useState<TabType>('fixed');
// 加载报表数据
const loadReportData = useCallback(async () => {
setLoading(true);
setError(null);
try {
const response = await systemAccountReportService.getFullReport();
if (response.data) {
setReportData(response.data);
}
} catch (err) {
setError('加载系统账户报表失败');
console.error('Failed to load system account report:', err);
} finally {
setLoading(false);
}
}, []);
// 加载分类账明细
const loadLedgerData = useCallback(async () => {
setLedgerLoading(true);
try {
const response = await systemAccountReportService.getAllLedger({ pageSize: 50 });
if (response.data) {
setLedgerData(response.data);
}
} catch (err) {
console.error('Failed to load ledger data:', err);
} finally {
setLedgerLoading(false);
}
}, []);
// [2026-01-06] 新增:加载收益类型汇总数据
const loadRewardTypeSummaries = useCallback(async () => {
setRewardTypeSummariesLoading(true);
try {
const response = await systemAccountReportService.getRewardTypeSummaries();
if (response.data) {
setRewardTypeSummaries(response.data);
}
} catch (err) {
console.error('Failed to load reward type summaries:', err);
} finally {
setRewardTypeSummariesLoading(false);
}
}, []);
useEffect(() => {
loadReportData();
}, [loadReportData]);
// 切换到分类账明细时加载数据
useEffect(() => {
if (activeTab === 'ledger' && !ledgerData && !ledgerLoading) {
loadLedgerData();
}
}, [activeTab, ledgerData, ledgerLoading, loadLedgerData]);
// [2026-01-06] 切换到收益类型汇总Tab时加载数据
const rewardTypeTabs: TabType[] = ['feeAccount', 'provinceTeam', 'cityTeam', 'shareReferral', 'community'];
useEffect(() => {
if (rewardTypeTabs.includes(activeTab) && !rewardTypeSummaries && !rewardTypeSummariesLoading) {
loadRewardTypeSummaries();
}
}, [activeTab, rewardTypeSummaries, rewardTypeSummariesLoading, loadRewardTypeSummaries]);
if (loading) {
return (
<div className={styles.loading}>
<div className={styles.spinner} />
<span>...</span>
</div>
);
}
if (error) {
return (
<div className={styles.error}>
<span>{error}</span>
<button onClick={loadReportData} className={styles.retryButton}>
</button>
</div>
);
}
if (!reportData) {
return <div className={styles.empty}></div>;
}
return (
<div className={styles.container}>
{/* Tab 切换 */}
<div className={styles.tabs}>
<button
className={`${styles.tab} ${activeTab === 'fixed' ? styles.active : ''}`}
onClick={() => setActiveTab('fixed')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'province' ? styles.active : ''}`}
onClick={() => setActiveTab('province')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'city' ? styles.active : ''}`}
onClick={() => setActiveTab('city')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'settlement' ? styles.active : ''}`}
onClick={() => setActiveTab('settlement')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'expired' ? styles.active : ''}`}
onClick={() => setActiveTab('expired')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'ledger' ? styles.active : ''}`}
onClick={() => setActiveTab('ledger')}
>
</button>
{/* [2026-01-06] 新增收益类型汇总Tab */}
<button
className={`${styles.tab} ${activeTab === 'feeAccount' ? styles.active : ''}`}
onClick={() => setActiveTab('feeAccount')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'provinceTeam' ? styles.active : ''}`}
onClick={() => setActiveTab('provinceTeam')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'cityTeam' ? styles.active : ''}`}
onClick={() => setActiveTab('cityTeam')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'shareReferral' ? styles.active : ''}`}
onClick={() => setActiveTab('shareReferral')}
>
</button>
<button
className={`${styles.tab} ${activeTab === 'community' ? styles.active : ''}`}
onClick={() => setActiveTab('community')}
>
</button>
</div>
{/* 内容区域 */}
<div className={styles.content}>
{activeTab === 'fixed' && <FixedAccountsSection data={reportData.fixedAccounts} />}
{activeTab === 'province' && <RegionAccountsSection data={reportData.provinceSummary} type="province" />}
{activeTab === 'city' && <RegionAccountsSection data={reportData.citySummary} type="city" />}
{activeTab === 'settlement' && <OfflineSettlementSection data={reportData.offlineSettlement} />}
{activeTab === 'expired' && <ExpiredRewardsSection data={reportData.expiredRewards} />}
{activeTab === 'ledger' && <LedgerSection data={ledgerData} loading={ledgerLoading} onRefresh={loadLedgerData} />}
{/* [2026-01-06] 新增收益类型汇总Tab内容 */}
{activeTab === 'feeAccount' && (
<FeeAccountSection
data={rewardTypeSummaries?.feeAccountSummary}
loading={rewardTypeSummariesLoading}
onRefresh={loadRewardTypeSummaries}
/>
)}
{activeTab === 'provinceTeam' && (
<RewardTypeSummarySection
title="省团队收益汇总"
data={rewardTypeSummaries?.provinceTeamSummary}
loading={rewardTypeSummariesLoading}
onRefresh={loadRewardTypeSummaries}
/>
)}
{activeTab === 'cityTeam' && (
<RewardTypeSummarySection
title="市团队收益汇总"
data={rewardTypeSummaries?.cityTeamSummary}
loading={rewardTypeSummariesLoading}
onRefresh={loadRewardTypeSummaries}
/>
)}
{activeTab === 'shareReferral' && (
<RewardTypeSummarySection
title="分享引荐收益汇总"
data={rewardTypeSummaries?.shareReferralSummary}
loading={rewardTypeSummariesLoading}
onRefresh={loadRewardTypeSummaries}
/>
)}
{activeTab === 'community' && (
<RewardTypeSummarySection
title="社区收益汇总"
data={rewardTypeSummaries?.communitySummary}
loading={rewardTypeSummariesLoading}
onRefresh={loadRewardTypeSummaries}
/>
)}
</div>
{/* 报表生成时间 */}
<div className={styles.footer}>
: {new Date(reportData.generatedAt).toLocaleString('zh-CN')}
</div>
</div>
);
}
/**
* 固定系统账户区域
* [2026-01-05] 更新根据业务需求调整显示名称USDT改为绿积分
*/
function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fixedAccounts'] }) {
const accounts = [
{ key: 'costAccount', label: '总部储备', sequence: 'S0000000001', data: data.costAccount },
{ key: 'operationAccount', label: '运营账户1', sequence: 'S0000000002', data: data.operationAccount },
{ key: 'hqCommunity', label: '运营账户2', sequence: 'S0000000003', data: data.hqCommunity },
{ key: 'rwadPoolPending', label: '积分股池', sequence: 'S0000000004', data: data.rwadPoolPending },
{ key: 'platformFee', label: '平台手续费', sequence: 'S0000000005', data: data.platformFee },
];
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}></h3>
<div className={styles.cardGrid}>
{accounts.map(({ key, label, sequence, data: accountData }) => (
<div key={key} className={styles.accountCard}>
<div className={styles.cardHeader}>
<span className={styles.accountLabel}>{label}</span>
<span className={styles.accountSequence}>{sequence}</span>
</div>
<div className={styles.cardBody}>
<div className={styles.statRow}>
<span className={styles.statLabel}></span>
<span className={styles.statValue}>
{accountData ? formatAmount(accountData.usdtBalance) : '0.00'} 绿
</span>
</div>
<div className={styles.statRow}>
<span className={styles.statLabel}></span>
<span className={styles.statValue}>
{accountData ? formatAmount(accountData.totalReceived) : '0.00'} 绿
</span>
</div>
<div className={styles.statRow}>
<span className={styles.statLabel}></span>
<span className={styles.statValue}>
{accountData ? formatAmount(accountData.totalTransferred) : '0.00'} 绿
</span>
</div>
</div>
</div>
))}
</div>
</div>
);
}
/**
* 区域账户汇总区域
* [2026-01-05] 更新USDT改为绿积分
*/
function RegionAccountsSection({ data, type }: { data: RegionAccountsSummary; type: 'province' | 'city' }) {
const typeLabel = type === 'province' ? '省' : '市';
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}>{typeLabel}</h3>
{/* 汇总卡片 */}
<div className={styles.summaryCards}>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{data.summary.count}</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.summary.totalBalance)} 绿</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.summary.totalReceived)} 绿</span>
</div>
</div>
{/* 账户列表 */}
{data.accounts.length > 0 ? (
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th></th>
<th> (绿)</th>
<th> (绿)</th>
<th></th>
</tr>
</thead>
<tbody>
{data.accounts.map((account) => (
<tr key={account.id}>
<td>{account.regionCode || '-'}</td>
<td>{account.regionName || '-'}</td>
<td>{formatAmount(account.usdtBalance)}</td>
<td>{formatAmount(account.totalReceived)}</td>
<td>
<span className={`${styles.statusBadge} ${account.status === 'ACTIVE' ? styles.active : ''}`}>
{account.status === 'ACTIVE' ? '正常' : account.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
) : (
<div className={styles.emptyTable}>{typeLabel}</div>
)}
</div>
);
}
/**
* 面对面结算统计区域
* [2026-01-05] 更新USDT改为绿积分添加空值检查
*/
function OfflineSettlementSection({ data }: { data: SystemAccountReportResponse['offlineSettlement'] | null | undefined }) {
if (!data) {
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}></h3>
<div className={styles.emptyTable}></div>
</div>
);
}
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}></h3>
{/* 汇总卡片 */}
<div className={styles.summaryCards}>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{data.totalCount ?? 0}</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿</span>
</div>
</div>
{/* 按月统计 */}
{data.byMonth && data.byMonth.length > 0 && (
<>
<h4 className={styles.subTitle}></h4>
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th></th>
<th> (绿)</th>
</tr>
</thead>
<tbody>
{data.byMonth.map((item) => (
<tr key={item.month}>
<td>{item.month}</td>
<td>{item.count}</td>
<td>{formatAmount(item.amount)}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
)}
{(!data.byMonth || data.byMonth.length === 0) && (
<div className={styles.emptyTable}></div>
)}
</div>
);
}
/**
* 过期收益统计区域
* [2026-01-05] 更新USDT改为绿积分添加空值检查
*/
function ExpiredRewardsSection({ data }: { data: SystemAccountReportResponse['expiredRewards'] | null | undefined }) {
if (!data) {
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}></h3>
<div className={styles.emptyTable}></div>
</div>
);
}
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}></h3>
{/* 汇总卡片 */}
<div className={styles.summaryCards}>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{data.totalCount ?? 0}</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿</span>
</div>
</div>
{/* 按权益类型统计 */}
{data.byRightType && data.byRightType.length > 0 && (
<>
<h4 className={styles.subTitle}></h4>
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th></th>
<th> (绿)</th>
</tr>
</thead>
<tbody>
{data.byRightType.map((item) => (
<tr key={item.rightType}>
<td>{getRightTypeName(item.rightType)}</td>
<td>{item.count}</td>
<td>{formatAmount(item.amount)}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
)}
{/* 按月统计 */}
{data.byMonth && data.byMonth.length > 0 && (
<>
<h4 className={styles.subTitle}></h4>
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th></th>
<th> (绿)</th>
</tr>
</thead>
<tbody>
{data.byMonth.map((item) => (
<tr key={item.month}>
<td>{item.month}</td>
<td>{item.count}</td>
<td>{formatAmount(item.amount)}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
)}
{(!data.byRightType || data.byRightType.length === 0) && (!data.byMonth || data.byMonth.length === 0) && (
<div className={styles.emptyTable}></div>
)}
</div>
);
}
// [2026-01-05] 新增:分类账明细组件
/**
* 分类账明细区域
*/
function LedgerSection({
data,
loading,
onRefresh,
}: {
data: AllSystemAccountsLedgerResponse | null;
loading: boolean;
onRefresh: () => void;
}) {
const [expandedAccounts, setExpandedAccounts] = useState<Set<string>>(new Set());
const toggleExpand = (accountKey: string) => {
setExpandedAccounts((prev) => {
const newSet = new Set(prev);
if (newSet.has(accountKey)) {
newSet.delete(accountKey);
} else {
newSet.add(accountKey);
}
return newSet;
});
};
if (loading) {
return (
<div className={styles.loading}>
<div className={styles.spinner} />
<span>...</span>
</div>
);
}
if (!data) {
return (
<div className={styles.section}>
<div className={styles.emptyTable}>
<span></span>
<button onClick={onRefresh} className={styles.retryButton}>
</button>
</div>
</div>
);
}
const getEntryTypeLabel = (type: string): string => {
return ENTRY_TYPE_LABELS[type] || type;
};
const getAccountTypeLabel = (type: string): string => {
return ACCOUNT_TYPE_LABELS[type] || type;
};
return (
<div className={styles.section}>
<div className={styles.sectionHeader}>
<h3 className={styles.sectionTitle}></h3>
<button onClick={onRefresh} className={styles.refreshButton}>
</button>
</div>
{/* 固定系统账户分类账 */}
{data.fixedAccountsLedger && data.fixedAccountsLedger.length > 0 && (
<div className={styles.ledgerGroup}>
<h4 className={styles.subTitle}></h4>
{data.fixedAccountsLedger.map((account) => (
<LedgerAccountCard
key={account.accountSequence}
title={getAccountTypeLabel(account.accountType)}
subtitle={account.accountSequence}
ledger={account.ledger}
total={account.total}
expanded={expandedAccounts.has(account.accountSequence)}
onToggle={() => toggleExpand(account.accountSequence)}
getEntryTypeLabel={getEntryTypeLabel}
/>
))}
</div>
)}
{/* 省区域账户分类账 */}
{data.provinceAccountsLedger && data.provinceAccountsLedger.length > 0 && (
<div className={styles.ledgerGroup}>
<h4 className={styles.subTitle}> ({data.provinceAccountsLedger.length})</h4>
{data.provinceAccountsLedger.map((account) => (
<LedgerAccountCard
key={account.accountSequence}
title={account.regionName || account.regionCode}
subtitle={account.accountSequence}
ledger={account.ledger}
total={account.total}
expanded={expandedAccounts.has(account.accountSequence)}
onToggle={() => toggleExpand(account.accountSequence)}
getEntryTypeLabel={getEntryTypeLabel}
/>
))}
</div>
)}
{/* 市区域账户分类账 */}
{data.cityAccountsLedger && data.cityAccountsLedger.length > 0 && (
<div className={styles.ledgerGroup}>
<h4 className={styles.subTitle}> ({data.cityAccountsLedger.length})</h4>
{data.cityAccountsLedger.map((account) => (
<LedgerAccountCard
key={account.accountSequence}
title={account.regionName || account.regionCode}
subtitle={account.accountSequence}
ledger={account.ledger}
total={account.total}
expanded={expandedAccounts.has(account.accountSequence)}
onToggle={() => toggleExpand(account.accountSequence)}
getEntryTypeLabel={getEntryTypeLabel}
/>
))}
</div>
)}
{(!data.fixedAccountsLedger || data.fixedAccountsLedger.length === 0) &&
(!data.provinceAccountsLedger || data.provinceAccountsLedger.length === 0) &&
(!data.cityAccountsLedger || data.cityAccountsLedger.length === 0) && (
<div className={styles.emptyTable}></div>
)}
</div>
);
}
/**
* 资产类型显示名称映射
* [2026-01-05] 新增USDT改为绿积分
*/
const getAssetTypeLabel = (assetType: string): string => {
const labels: Record<string, string> = {
USDT: '绿积分',
usdt: '绿积分',
};
return labels[assetType] || assetType;
};
/**
* 单个账户的分类账卡片
* [2026-01-05] 更新:资产类型显示改用中文
*/
function LedgerAccountCard({
title,
subtitle,
ledger,
total,
expanded,
onToggle,
getEntryTypeLabel,
}: {
title: string;
subtitle: string;
ledger: LedgerEntryDTO[];
total: number;
expanded: boolean;
onToggle: () => void;
getEntryTypeLabel: (type: string) => string;
}) {
return (
<div className={styles.ledgerCard}>
<div className={styles.ledgerCardHeader} onClick={onToggle}>
<div className={styles.ledgerCardTitle}>
<span className={styles.accountName}>{title}</span>
<span className={styles.accountSequence}>{subtitle}</span>
</div>
<div className={styles.ledgerCardInfo}>
<span className={styles.ledgerCount}>{total} </span>
<span className={styles.expandIcon}>{expanded ? '▼' : '▶'}</span>
</div>
</div>
{expanded && (
<div className={styles.ledgerCardBody}>
{ledger.length > 0 ? (
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{ledger.map((entry) => (
<tr key={entry.id}>
<td>{new Date(entry.createdAt).toLocaleString('zh-CN')}</td>
<td>
<span className={styles.entryTypeBadge}>
{getEntryTypeLabel(entry.entryType)}
</span>
</td>
<td className={entry.amount >= 0 ? styles.amountPositive : styles.amountNegative}>
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
</td>
<td>{entry.balanceAfter !== null ? formatAmount(entry.balanceAfter) : '-'}</td>
<td className={styles.memo}>{entry.memo || entry.allocationType || '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
) : (
<div className={styles.emptyLedger}></div>
)}
</div>
)}
</div>
);
}
// [2026-01-06] 新增:手续费账户汇总组件
/**
* 手续费账户汇总区域
*/
function FeeAccountSection({
data,
loading,
onRefresh,
}: {
data: FeeAccountSummary | undefined;
loading: boolean;
onRefresh: () => void;
}) {
if (loading) {
return (
<div className={styles.loading}>
<div className={styles.spinner} />
<span>...</span>
</div>
);
}
if (!data) {
return (
<div className={styles.section}>
<div className={styles.emptyTable}>
<span></span>
<button onClick={onRefresh} className={styles.retryButton}>
</button>
</div>
</div>
);
}
return (
<div className={styles.section}>
<div className={styles.sectionHeader}>
<h3 className={styles.sectionTitle}></h3>
<button onClick={onRefresh} className={styles.refreshButton}>
</button>
</div>
{/* 汇总卡片 */}
<div className={styles.summaryCards}>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{data.totalCount}</span>
</div>
</div>
{/* 按类型明细 */}
{data.breakdown && data.breakdown.length > 0 ? (
<>
<h4 className={styles.subTitle}></h4>
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th> (绿)</th>
<th></th>
</tr>
</thead>
<tbody>
{data.breakdown.map((item) => (
<tr key={item.rightType}>
<td>{FEE_TYPE_LABELS[item.rightType] || item.rightType}</td>
<td>{formatAmount(item.amount)}</td>
<td>{item.count}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
) : (
<div className={styles.emptyTable}></div>
)}
</div>
);
}
// [2026-01-06] 新增:收益类型汇总组件
/**
* 收益类型汇总区域(省团队、市团队、分享引荐、社区)
*/
function RewardTypeSummarySection({
title,
data,
loading,
onRefresh,
}: {
title: string;
data: RewardTypeSummary | undefined;
loading: boolean;
onRefresh: () => void;
}) {
if (loading) {
return (
<div className={styles.loading}>
<div className={styles.spinner} />
<span>{title}...</span>
</div>
);
}
if (!data) {
return (
<div className={styles.section}>
<div className={styles.emptyTable}>
<span></span>
<button onClick={onRefresh} className={styles.retryButton}>
</button>
</div>
</div>
);
}
return (
<div className={styles.section}>
<div className={styles.sectionHeader}>
<h3 className={styles.sectionTitle}>{title}</h3>
<button onClick={onRefresh} className={styles.refreshButton}>
</button>
</div>
{/* 汇总卡片 */}
<div className={styles.summaryCards}>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{data.totalCount}</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.totalSettleableAmount)} 绿</span>
</div>
<div className={styles.summaryCard}>
<span className={styles.summaryLabel}></span>
<span className={styles.summaryValue}>{formatAmount(data.totalSettledAmount)} 绿</span>
</div>
</div>
{/* 按月统计 */}
{data.byMonth && data.byMonth.length > 0 ? (
<>
<h4 className={styles.subTitle}></h4>
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th></th>
<th> (绿)</th>
<th></th>
</tr>
</thead>
<tbody>
{data.byMonth.map((item) => (
<tr key={item.month}>
<td>{item.month}</td>
<td>{formatAmount(item.amount)}</td>
<td>{item.count}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
) : (
<div className={styles.emptyTable}></div>
)}
</div>
);
}