refactor: 收口个人资金展示模型

This commit is contained in:
2026-06-03 18:06:31 +08:00
parent d67abecc9e
commit f67f57b415
7 changed files with 343 additions and 49 deletions

View File

@@ -141,6 +141,12 @@ import {
formatPlayedWorkType,
formatTotalPlayTimeHours,
} from './rpgEntryProfileDashboardPresentation';
import {
buildMembershipLabel,
buildRechargeProductValueLabel,
buildWalletLedgerPresentation,
formatRechargePrice,
} from './rpgEntryProfileFundsViewModel';
import {
buildProfileTaskCardSummary,
buildProfileTaskProgressLabel,
@@ -2150,27 +2156,6 @@ function ProfileNicknameModal({
);
}
const WALLET_LEDGER_SOURCE_LABELS: Record<string, string> = {
new_user_registration_reward: '注册赠送',
points_recharge: '泥点充值',
invite_inviter_reward: '邀请奖励',
invite_invitee_reward: '填写邀请码奖励',
snapshot_sync: '账户同步',
asset_operation_consume: '资产操作消耗',
asset_operation_refund: '资产操作退回',
redeem_code_reward: '兑换码奖励',
daily_task_reward: '每日任务奖励',
};
function formatWalletLedgerAmount(amountDelta: number) {
return amountDelta > 0 ? `+${amountDelta}` : `${amountDelta}`;
}
function formatRechargePrice(priceCents: number) {
const yuan = priceCents / 100;
return `¥${Number.isInteger(yuan) ? yuan.toFixed(0) : yuan.toFixed(2)}`;
}
function clearWechatPayResultHash() {
if (typeof window === 'undefined') {
return;
@@ -2378,12 +2363,8 @@ function RechargeProductCard({
onBuy: (product: ProfileRechargeProduct) => void;
}) {
const submitting = submittingProductId === product.productId;
const effectiveBonusPoints = product.bonusPoints;
const badgeLabel = product.badgeLabel;
const value =
product.kind === 'points'
? `${product.pointsAmount}${effectiveBonusPoints > 0 ? `+${effectiveBonusPoints}` : ''}泥点`
: `${product.durationDays}`;
const value = buildRechargeProductValueLabel(product);
return (
<button
@@ -2447,12 +2428,10 @@ function ProfileRechargeModal({
activeTab === 'points'
? (center?.pointProducts ?? [])
: (center?.membershipProducts ?? []);
const memberLabel =
center?.membership.status === 'active'
? center.membership.expiresAt
? `会员至 ${formatSnapshotTime(center.membership.expiresAt)}`
: '会员已生效'
: '普通用户';
const memberLabel = buildMembershipLabel(
center?.membership,
formatSnapshotTime,
);
return (
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
@@ -2664,8 +2643,11 @@ function WalletLedgerModal({
onClose: () => void;
onRetry: () => void;
}) {
const entries = ledger?.entries ?? [];
const balance = entries[0]?.balanceAfter ?? fallbackBalance;
const walletLedgerPresentation = buildWalletLedgerPresentation(
ledger,
fallbackBalance,
);
const entries = walletLedgerPresentation.entries;
return (
<div className="fixed inset-0 z-[80] flex items-center justify-center bg-black/48 px-3 py-5">
@@ -2686,7 +2668,7 @@ function WalletLedgerModal({
<div className="mt-1 text-2xl font-black"></div>
<div className="mt-3 inline-flex items-center gap-2 rounded-full border border-rose-100 bg-white/70 px-3 py-1.5 text-xs font-bold text-zinc-600">
<Coins className="h-3.5 w-3.5 text-[#ff4056]" />
<span>{balance}</span>
<span>{walletLedgerPresentation.balanceLabel}</span>
</div>
</div>
@@ -2717,11 +2699,6 @@ function WalletLedgerModal({
) : (
<div className="mt-5 space-y-2.5">
{entries.map((entry) => {
const isIncome = entry.amountDelta > 0;
const label =
WALLET_LEDGER_SOURCE_LABELS[entry.sourceType] ??
entry.sourceType;
return (
<div
key={entry.id}
@@ -2729,7 +2706,7 @@ function WalletLedgerModal({
>
<div className="min-w-0">
<div className="truncate text-sm font-black text-zinc-900">
{label}
{entry.sourceLabel}
</div>
<div className="mt-1 text-xs font-semibold text-zinc-500">
{formatPlatformWorldTime(entry.createdAt)}
@@ -2738,13 +2715,15 @@ function WalletLedgerModal({
<div className="shrink-0 text-right">
<div
className={`text-base font-black ${
isIncome ? 'text-emerald-600' : 'text-rose-500'
entry.isIncome
? 'text-emerald-600'
: 'text-rose-500'
}`}
>
{formatWalletLedgerAmount(entry.amountDelta)}
{entry.amountLabel}
</div>
<div className="mt-1 text-[11px] font-semibold text-zinc-400">
{entry.balanceAfter}
{entry.balanceLabel}
</div>
</div>
</div>