feat(admin-web): 固定系统账户卡片添加查看分类账明细按钮

在固定系统账户卡片下方添加"查看明细"按钮,点击后展开显示
该账户的分类账流水记录,包括时间、类型、金额、余额和备注。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-06 22:50:03 -08:00
parent d53b1c7499
commit d6b3f04612
2 changed files with 172 additions and 26 deletions

View File

@ -592,3 +592,49 @@
font-size: 13px;
color: #6b7280;
}
/* [2026-01-07] 新增:固定账户卡片底部样式 */
.cardFooter {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e5e7eb;
}
.viewLedgerButton {
width: 100%;
padding: 8px 12px;
background-color: #f3f4f6;
color: #374151;
border: 1px solid #d1d5db;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
transition: all 0.2s ease;
&:hover:not(:disabled) {
background-color: #e5e7eb;
border-color: #9ca3af;
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
.accountLedgerSection {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #e5e7eb;
max-height: 400px;
overflow-y: auto;
.table {
font-size: 12px;
th,
td {
padding: 8px 10px;
}
}
}

View File

@ -296,6 +296,7 @@ export default function SystemAccountsTab() {
/**
*
* [2026-01-05] USDT改为绿积分
* [2026-01-07]
*/
function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fixedAccounts'] }) {
// [2026-01-07] 更新:使用 SYSTEM_ACCOUNT_NAMES 映射获取正式名称
@ -307,37 +308,136 @@ function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fix
{ key: 'platformFee', sequence: 'S0000000005', data: data.platformFee },
];
// [2026-01-07] 新增:展开状态管理和分类账数据
const [expandedAccount, setExpandedAccount] = useState<string | null>(null);
const [allLedgerData, setAllLedgerData] = useState<AllSystemAccountsLedgerResponse | null>(null);
const [ledgerLoading, setLedgerLoading] = useState(false);
// 加载所有固定账户的分类账明细
const loadAllLedger = useCallback(async () => {
if (allLedgerData) return; // 已加载过,不重复加载
setLedgerLoading(true);
try {
const response = await systemAccountReportService.getAllLedger({ pageSize: 50 });
if (response.data) {
setAllLedgerData(response.data);
}
} catch (err) {
console.error('Failed to load ledger data:', err);
} finally {
setLedgerLoading(false);
}
}, [allLedgerData]);
// 切换显示某个账户的明细
const handleToggleLedger = async (accountSequence: string) => {
if (expandedAccount === accountSequence) {
setExpandedAccount(null);
return;
}
// 如果还没有加载数据,先加载
if (!allLedgerData) {
await loadAllLedger();
}
setExpandedAccount(accountSequence);
};
// 获取指定账户的分类账数据
const getAccountLedger = (accountSequence: string): LedgerEntryDTO[] => {
if (!allLedgerData?.fixedAccountsLedger) return [];
const accountLedger = allLedgerData.fixedAccountsLedger.find(
(item) => item.accountSequence === accountSequence
);
return accountLedger?.ledger || [];
};
return (
<div className={styles.section}>
<h3 className={styles.sectionTitle}></h3>
<div className={styles.cardGrid}>
{accounts.map(({ key, sequence, data: accountData }) => (
<div key={key} className={styles.accountCard}>
<div className={styles.cardHeader}>
<span className={styles.accountLabel}>{getAccountDisplayName(sequence)}</span>
{accounts.map(({ key, sequence, data: accountData }) => {
const isExpanded = expandedAccount === sequence;
const ledger = isExpanded ? getAccountLedger(sequence) : [];
return (
<div key={key} className={styles.accountCard}>
<div className={styles.cardHeader}>
<span className={styles.accountLabel}>{getAccountDisplayName(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>
{/* [2026-01-07] 新增:查看明细按钮 */}
<div className={styles.cardFooter}>
<button
className={styles.viewLedgerButton}
onClick={() => handleToggleLedger(sequence)}
disabled={ledgerLoading}
>
{ledgerLoading && !allLedgerData ? '加载中...' : isExpanded ? '收起明细' : '查看明细'}
</button>
</div>
{/* [2026-01-07] 新增:展开的分类账明细 */}
{isExpanded && (
<div className={styles.accountLedgerSection}>
{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}>
{ENTRY_TYPE_LABELS[entry.entryType] || 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>
<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>
);