import { useCallback, useEffect, useMemo, useState } from "react"; import Dialog from "@mui/material/Dialog"; import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; import { approveFinanceApplication, approveFinanceWithdrawalApplication, createFinanceApplication, createFinanceCoinSellerRechargeOrder, exportFinanceRechargeBills, fetchFinanceApplicationOptions, fetchFinanceRechargeApps, fetchFinanceRechargeOverview, fetchFinanceRechargeSummaries, fetchFinanceSession, filterOperationsByPermissions, grantFinanceCoinSellerRechargeOrder, listFinanceApplications, listFinanceCoinSellerRechargeOrders, listFinanceRechargeDetails, listFinanceRechargeRegions, listMyFinanceApplications, listFinanceWithdrawalApplications, refreshFinanceGoogleRechargePaid, rejectFinanceApplication, rejectFinanceWithdrawalApplication, verifyFinanceCoinSellerRechargeReceipt, verifyFinanceCoinSellerRechargeOrder, } from "./api.js"; import { FinanceApplicationForm } from "./components/FinanceApplicationForm.jsx"; import { FinanceApplicationList } from "./components/FinanceApplicationList.jsx"; import { FinanceAuditDialog } from "./components/FinanceAuditDialog.jsx"; import { FinanceCoinSellerRechargeOrderList } from "./components/FinanceCoinSellerRechargeOrderList.jsx"; import { FinanceMyApplicationList } from "./components/FinanceMyApplicationList.jsx"; import { FinanceOverview } from "./components/FinanceOverview.jsx"; import { FinanceReconciliation } from "./components/FinanceReconciliation.jsx"; import { FinanceRechargeDetailList } from "./components/FinanceRechargeDetailList.jsx"; import { FinanceShell } from "./components/FinanceShell.jsx"; import { FinanceWithdrawalApplicationList } from "./components/FinanceWithdrawalApplicationList.jsx"; import { DEFAULT_APPLICATION_FORM } from "./constants.js"; import { EMPTY_RECHARGE_OVERVIEW, EMPTY_RECHARGE_SUMMARY, buildApplicationPayload, validateApplicationPayload, } from "./format.js"; import { downloadCsv } from "@/shared/api/download"; import { TimeRangeFilter } from "@/shared/ui/TimeRangeFilter.jsx"; import { useToast } from "@/shared/ui/ToastProvider.jsx"; const emptyOptions = { apps: [], operations: [], walletIdentities: [] }; const pageSize = 50; const DAY_MS = 86_400_000; const CHINA_OFFSET_MS = 8 * 3_600_000; const FINANCE_VIEWS = new Set(["overview", "rechargeDetails", "recon"]); function chinaDayStart(timestamp) { return Math.floor((timestamp + CHINA_OFFSET_MS) / DAY_MS) * DAY_MS - CHINA_OFFSET_MS; } function timePresets() { const now = Date.now(); const todayStart = chinaDayStart(now); const chinaNow = new Date(now + CHINA_OFFSET_MS); const monthStart = Date.UTC(chinaNow.getUTCFullYear(), chinaNow.getUTCMonth(), 1) - CHINA_OFFSET_MS; return [ { key: "today", label: "今天", range: { endMs: todayStart + DAY_MS, startMs: todayStart } }, { key: "7d", label: "近7天", range: { endMs: todayStart + DAY_MS, startMs: todayStart - 6 * DAY_MS } }, { key: "30d", label: "近30天", range: { endMs: todayStart + DAY_MS, startMs: todayStart - 29 * DAY_MS } }, { key: "month", label: "本月", range: { endMs: todayStart + DAY_MS, startMs: monthStart } }, { key: "all", label: "不限", range: { endMs: "", startMs: "" } }, ]; } function reconMonthMeta(offset) { const chinaNow = new Date(Date.now() + CHINA_OFFSET_MS); const year = chinaNow.getUTCFullYear(); const month = chinaNow.getUTCMonth() + offset; const startMs = Date.UTC(year, month, 1) - CHINA_OFFSET_MS; const endMs = Date.UTC(year, month + 1, 1) - CHINA_OFFSET_MS; const startDate = new Date(startMs + CHINA_OFFSET_MS); return { endMs, isCurrent: offset === 0, label: `${startDate.getUTCFullYear()}年${startDate.getUTCMonth() + 1}月`, startMs, }; } export function FinanceApp() { const { showToast } = useToast(); const [sessionState, setSessionState] = useState({ error: "", loading: true, session: null }); const [optionsState, setOptionsState] = useState({ data: emptyOptions, error: "", loading: false }); const [rechargeDetailsState, setRechargeDetailsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false, }); const [rechargeSummaryState, setRechargeSummaryState] = useState({ merged: EMPTY_RECHARGE_SUMMARY, loading: false, perApp: [], prev: null, }); const [rechargeOverviewState, setRechargeOverviewState] = useState({ data: EMPTY_RECHARGE_OVERVIEW, loading: false, }); const [rechargeRegionsState, setRechargeRegionsState] = useState({ byApp: {}, loading: false }); const [rechargeAppsState, setRechargeAppsState] = useState({ items: [], loaded: false }); const [googlePaidRefreshing, setGooglePaidRefreshing] = useState(false); const [reconMonthOffset, setReconMonthOffset] = useState(0); const [reconState, setReconState] = useState({ loading: false, overview: EMPTY_RECHARGE_OVERVIEW, summary: EMPTY_RECHARGE_SUMMARY, }); const [myApplicationsState, setMyApplicationsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false, }); const [applicationsState, setApplicationsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false, }); const [withdrawalsState, setWithdrawalsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false, }); const [coinSellerRechargeOrdersState, setCoinSellerRechargeOrdersState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false, }); const [coinSellerRechargeOrderActionLoading, setCoinSellerRechargeOrderActionLoading] = useState(""); const [activeView, setActiveView] = useState("create"); const [rechargeDetailFilters, setRechargeDetailFilters] = useState(() => ({ appCode: "", currency: "usd", keyword: "", page: 1, paidState: "", rechargeType: "", regionId: "", timeRange: timePresets().find((preset) => preset.key === "30d").range, })); const [rechargeExporting, setRechargeExporting] = useState(false); const [myFilters, setMyFilters] = useState({ appCode: "", keyword: "", operation: "", page: 1, status: "" }); const [filters, setFilters] = useState({ appCode: "", keyword: "", operation: "", page: 1, status: "pending" }); const [withdrawalFilters, setWithdrawalFilters] = useState({ appCode: "", keyword: "", page: 1 }); const [coinSellerRechargeOrderFilters, setCoinSellerRechargeOrderFilters] = useState({ appCode: "", grantStatus: "", keyword: "", page: 1, providerCode: "", status: "", verifyStatus: "", }); const [form, setForm] = useState(DEFAULT_APPLICATION_FORM); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [auditTarget, setAuditTarget] = useState(null); const [auditLoading, setAuditLoading] = useState(false); const [withdrawalAuditTarget, setWithdrawalAuditTarget] = useState(null); const [withdrawalAuditLoading, setWithdrawalAuditLoading] = useState(false); useEffect(() => { let mounted = true; fetchFinanceSession() .then((session) => { if (!mounted) { return; } setSessionState({ error: "", loading: false, session }); setActiveView( session.canViewAppRechargeDetails ? "overview" : session.canViewCoinSellerRechargeOrders ? "coinSellerRechargeOrders" : session.canAuditApplication ? "applications" : session.canCreateApplication ? "create" : "withdrawals", ); }) .catch((err) => { if (mounted) { setSessionState({ error: err.message || "登录已失效", loading: false, session: null }); } }); return () => { mounted = false; }; }, []); const session = sessionState.session; const canLoadOptions = Boolean(session?.canAccessFinance); const canViewFinance = Boolean(session?.canViewAppRechargeDetails); // 财务三页(概览/流水/对账)共享同一套 App × 时间 × 计价 全局筛选。 const rechargeBaseQuery = useMemo( () => ({ app_code: rechargeDetailFilters.appCode, end_at_ms: rechargeDetailFilters.timeRange.endMs, keyword: rechargeDetailFilters.keyword, recharge_type: rechargeDetailFilters.rechargeType, region_id: rechargeDetailFilters.appCode ? rechargeDetailFilters.regionId : "", start_at_ms: rechargeDetailFilters.timeRange.startMs, stat_tz: "Asia/Shanghai", status: "succeeded", }), [rechargeDetailFilters], ); const rechargeDetailQuery = useMemo( () => ({ ...rechargeBaseQuery, page: rechargeDetailFilters.page, page_size: pageSize, paid_state: rechargeDetailFilters.paidState, }), [rechargeBaseQuery, rechargeDetailFilters.page, rechargeDetailFilters.paidState], ); const myApplicationQuery = useMemo( () => ({ app_code: myFilters.appCode, keyword: myFilters.keyword, operation: myFilters.operation, page: myFilters.page, page_size: pageSize, status: myFilters.status, }), [myFilters], ); const applicationQuery = useMemo( () => ({ app_code: filters.appCode, keyword: filters.keyword, operation: filters.operation, page: filters.page, page_size: pageSize, status: filters.status, }), [filters], ); const withdrawalQuery = useMemo( () => ({ app_code: withdrawalFilters.appCode, keyword: withdrawalFilters.keyword, page: withdrawalFilters.page, page_size: pageSize, }), [withdrawalFilters], ); const coinSellerRechargeOrderQuery = useMemo( () => ({ app_code: coinSellerRechargeOrderFilters.appCode, grant_status: coinSellerRechargeOrderFilters.grantStatus, keyword: coinSellerRechargeOrderFilters.keyword, page: coinSellerRechargeOrderFilters.page, page_size: pageSize, provider_code: coinSellerRechargeOrderFilters.providerCode, status: coinSellerRechargeOrderFilters.status, verify_status: coinSellerRechargeOrderFilters.verifyStatus, }), [coinSellerRechargeOrderFilters], ); const loadOptions = useCallback(async () => { if (!canLoadOptions) { return; } setOptionsState((current) => ({ ...current, error: "", loading: true })); try { const data = await fetchFinanceApplicationOptions(); setOptionsState({ data, error: "", loading: false }); } catch (err) { setOptionsState({ data: emptyOptions, error: err.message || "加载财务选项失败", loading: false }); } }, [canLoadOptions]); const loadMyApplications = useCallback(async () => { if (!session?.canCreateApplication) { return; } setMyApplicationsState((current) => ({ ...current, error: "", loading: true })); try { const data = await listMyFinanceApplications(myApplicationQuery); setMyApplicationsState({ data, error: "", loading: false }); } catch (err) { setMyApplicationsState((current) => ({ ...current, error: err.message || "加载我的申请列表失败", loading: false, })); } }, [myApplicationQuery, session?.canCreateApplication]); // 充值详情的 App 目录来自 recharge-apps(含 Yumi/Aslan 等 legacy 外部账单源);接口未就绪时回退到财务通用 App 列表。 const rechargeApps = useMemo( () => rechargeAppsState.loaded && rechargeAppsState.items.length ? rechargeAppsState.items : optionsState.data.apps, [optionsState.data.apps, rechargeAppsState], ); const loadRechargeApps = useCallback(async () => { if (!canViewFinance) { return; } try { const items = await fetchFinanceRechargeApps(); setRechargeAppsState({ items, loaded: true }); } catch { setRechargeAppsState({ items: [], loaded: false }); } }, [canViewFinance]); const loadRechargeDetails = useCallback(async () => { if (!canViewFinance) { return; } setRechargeDetailsState((current) => ({ ...current, error: "", loading: true })); try { const data = await listFinanceRechargeDetails(rechargeDetailQuery, rechargeApps); setRechargeDetailsState({ data, error: "", loading: false }); } catch (err) { setRechargeDetailsState((current) => ({ ...current, error: err.message || "加载充值流水失败", loading: false, })); } }, [canViewFinance, rechargeApps, rechargeDetailQuery]); const loadRechargeSummary = useCallback(async () => { if (!canViewFinance) { return; } setRechargeSummaryState((current) => ({ ...current, loading: true })); try { const { merged, perApp } = await fetchFinanceRechargeSummaries(rechargeBaseQuery, rechargeApps); // 环比:把当前时间范围整体前移一个周期再查一次;未选时间范围时不算环比。 let prev = null; const { endMs, startMs } = rechargeDetailFilters.timeRange; if (startMs && endMs && endMs > startMs) { const length = endMs - startMs; const prevQuery = { ...rechargeBaseQuery, end_at_ms: startMs, start_at_ms: startMs - length }; prev = (await fetchFinanceRechargeSummaries(prevQuery, rechargeApps)).merged; } setRechargeSummaryState({ merged, loading: false, perApp, prev }); } catch { setRechargeSummaryState((current) => ({ ...current, loading: false })); } }, [canViewFinance, rechargeApps, rechargeBaseQuery, rechargeDetailFilters.timeRange]); const loadRechargeOverview = useCallback(async () => { if (!canViewFinance) { return; } setRechargeOverviewState((current) => ({ ...current, loading: true })); try { const data = await fetchFinanceRechargeOverview(rechargeBaseQuery, rechargeApps); setRechargeOverviewState({ data, loading: false }); } catch { setRechargeOverviewState((current) => ({ ...current, loading: false })); } }, [canViewFinance, rechargeApps, rechargeBaseQuery]); const loadRecon = useCallback(async () => { if (!canViewFinance || activeView !== "recon") { return; } const month = reconMonthMeta(reconMonthOffset); const query = { ...rechargeBaseQuery, end_at_ms: month.endMs, keyword: "", recharge_type: "", start_at_ms: month.startMs, }; setReconState((current) => ({ ...current, loading: true })); try { const [{ merged }, overview] = await Promise.all([ fetchFinanceRechargeSummaries(query, rechargeApps), fetchFinanceRechargeOverview(query, rechargeApps), ]); setReconState({ loading: false, overview, summary: merged }); } catch { setReconState((current) => ({ ...current, loading: false })); } }, [activeView, canViewFinance, rechargeApps, rechargeBaseQuery, reconMonthOffset]); const rechargeAppCode = rechargeDetailFilters.appCode; // 一次拉齐所有 App 的区域目录:区域筛选下拉用当前 App 的,流水区域列在“全部 App”下要跨 App 显示名称。 const loadRechargeRegions = useCallback(async () => { if (!canViewFinance || !rechargeApps.length) { return; } setRechargeRegionsState((current) => ({ ...current, loading: true })); try { const entries = await Promise.all( rechargeApps.map(async (app) => { try { return [app.appCode, await listFinanceRechargeRegions(app.appCode)]; } catch { return [app.appCode, []]; } }), ); setRechargeRegionsState({ byApp: Object.fromEntries(entries), loading: false }); } catch { setRechargeRegionsState((current) => ({ ...current, loading: false })); } }, [canViewFinance, rechargeApps]); const refreshGooglePaid = useCallback( async (transactionIds) => { if (!rechargeAppCode || !transactionIds?.length || googlePaidRefreshing) { return; } setGooglePaidRefreshing(true); try { const results = await refreshFinanceGoogleRechargePaid(rechargeAppCode, transactionIds); const failed = results.filter((item) => item.error).length; const succeeded = results.length - failed; showToast( failed ? `谷歌实付同步完成:成功 ${succeeded} 笔,失败 ${failed} 笔` : `谷歌实付同步完成:${succeeded} 笔`, failed ? "warning" : "success", ); await Promise.all([loadRechargeDetails(), loadRechargeOverview()]); } catch (err) { showToast(err.message || "查询谷歌实付失败", "error"); } finally { setGooglePaidRefreshing(false); } }, [googlePaidRefreshing, loadRechargeDetails, loadRechargeOverview, rechargeAppCode, showToast], ); const exportRechargeBills = useCallback(async () => { if (!rechargeAppCode || rechargeExporting) { return; } setRechargeExporting(true); try { const timestamp = new Date() .toLocaleString("sv-SE", { timeZone: "Asia/Shanghai" }) .replaceAll(/[: ]/g, "-"); await downloadCsv( () => exportFinanceRechargeBills(rechargeAppCode, rechargeDetailQuery), `充值明细-${rechargeAppCode}-${timestamp}.csv`, ); showToast("充值明细已导出", "success"); } catch (err) { showToast(err.message || "导出充值明细失败", "error"); } finally { setRechargeExporting(false); } }, [rechargeAppCode, rechargeDetailQuery, rechargeExporting, showToast]); const loadApplications = useCallback(async () => { if (!session?.canAuditApplication) { return; } setApplicationsState((current) => ({ ...current, error: "", loading: true })); try { const data = await listFinanceApplications(applicationQuery); setApplicationsState({ data, error: "", loading: false }); } catch (err) { setApplicationsState((current) => ({ ...current, error: err.message || "加载申请列表失败", loading: false, })); } }, [applicationQuery, session?.canAuditApplication]); const loadWithdrawals = useCallback(async () => { if (!session?.canViewWithdrawalApplications) { return; } setWithdrawalsState((current) => ({ ...current, error: "", loading: true })); try { const data = await listFinanceWithdrawalApplications(withdrawalQuery); setWithdrawalsState({ data, error: "", loading: false }); } catch (err) { setWithdrawalsState((current) => ({ ...current, error: err.message || "加载提现申请列表失败", loading: false, })); } }, [session?.canViewWithdrawalApplications, withdrawalQuery]); const loadCoinSellerRechargeOrders = useCallback(async () => { if (!session?.canViewCoinSellerRechargeOrders) { return; } setCoinSellerRechargeOrdersState((current) => ({ ...current, error: "", loading: true })); try { const data = await listFinanceCoinSellerRechargeOrders(coinSellerRechargeOrderQuery); setCoinSellerRechargeOrdersState({ data, error: "", loading: false }); } catch (err) { setCoinSellerRechargeOrdersState((current) => ({ ...current, error: err.message || "加载币商充值订单失败", loading: false, })); } }, [coinSellerRechargeOrderQuery, session?.canViewCoinSellerRechargeOrders]); useEffect(() => { loadOptions(); }, [loadOptions]); useEffect(() => { loadMyApplications(); }, [loadMyApplications]); useEffect(() => { loadRechargeApps(); }, [loadRechargeApps]); useEffect(() => { loadRechargeDetails(); }, [loadRechargeDetails]); useEffect(() => { loadRechargeSummary(); }, [loadRechargeSummary]); useEffect(() => { loadRechargeOverview(); }, [loadRechargeOverview]); useEffect(() => { loadRecon(); }, [loadRecon]); useEffect(() => { loadRechargeRegions(); }, [loadRechargeRegions]); useEffect(() => { loadApplications(); }, [loadApplications]); useEffect(() => { loadWithdrawals(); }, [loadWithdrawals]); useEffect(() => { loadCoinSellerRechargeOrders(); }, [loadCoinSellerRechargeOrders]); const operationOptions = useMemo( () => filterOperationsByPermissions(optionsState.data.operations, session?.permissions || []), [optionsState.data.operations, session?.permissions], ); const submitApplication = async (event) => { event.preventDefault(); if (!session?.canCreateApplication) { showToast("无发起财务申请权限", "error"); return; } const payload = buildApplicationPayload(form); const validationMessage = validateApplicationPayload(payload); if (validationMessage) { showToast(validationMessage, "warning"); return; } setSubmitting(true); try { const result = await createFinanceApplication(cleanPayload(payload)); setForm({ ...DEFAULT_APPLICATION_FORM, appCode: payload.appCode }); setCreateDialogOpen(false); showToast( result?.dingTalkNotified === false ? "申请已提交,钉钉通知未确认" : "申请已提交并通知财务", "success", ); await loadMyApplications(); if (session.canAuditApplication) { await loadApplications(); } } catch (err) { showToast(err.message || "发起申请失败", "error"); } finally { setSubmitting(false); } }; const submitAudit = async ({ decision, remark }) => { if (!auditTarget?.id || !session?.canAuditApplication) { return; } setAuditLoading(true); try { const submitter = decision === "approved" ? approveFinanceApplication : rejectFinanceApplication; await submitter(auditTarget.id, { auditRemark: remark }); showToast(decision === "approved" ? "申请已通过" : "申请已拒绝", "success"); setAuditTarget(null); await loadApplications(); } catch (err) { showToast(err.message || "审核失败", "error"); } finally { setAuditLoading(false); } }; const submitWithdrawalAudit = async ({ auditImageUrl, decision, remark }) => { if (!withdrawalAuditTarget?.id || !session?.canAuditWithdrawalApplications) { return; } setWithdrawalAuditLoading(true); try { const submitter = decision === "approved" ? approveFinanceWithdrawalApplication : rejectFinanceWithdrawalApplication; await submitter(withdrawalAuditTarget.id, cleanPayload({ auditImageUrl, auditRemark: remark })); showToast(decision === "approved" ? "提现申请已通过" : "提现申请已拒绝", "success"); setWithdrawalAuditTarget(null); await loadWithdrawals(); } catch (err) { showToast(err.message || "审核提现申请失败", "error"); } finally { setWithdrawalAuditLoading(false); } }; const submitCoinSellerRechargeOrder = async (payload) => { if (!session?.canCreateCoinSellerRechargeOrder) { showToast("无创建币商充值订单权限", "error"); return null; } setCoinSellerRechargeOrderActionLoading("create"); try { const order = await createFinanceCoinSellerRechargeOrder(cleanPayload(payload)); showToast("币商充值订单已创建", "success"); await loadCoinSellerRechargeOrders(); return order; } catch (err) { showToast(err.message || "创建币商充值订单失败", "error"); return null; } finally { setCoinSellerRechargeOrderActionLoading(""); } }; const verifyCoinSellerRechargeReceipt = async (payload) => { if (!session?.canVerifyCoinSellerRechargeOrder) { showToast("无校验币商充值订单权限", "error"); return null; } try { const verification = await verifyFinanceCoinSellerRechargeReceipt(cleanPayload(payload)); showToast(verification?.verified ? "订单号校验通过" : verification?.failureReason || "订单号校验未通过", verification?.verified ? "success" : "error"); return verification; } catch (err) { showToast(err.message || "校验订单号失败", "error"); return null; } }; const verifyCoinSellerRechargeOrder = async (order) => { if (!order?.id || !session?.canVerifyCoinSellerRechargeOrder) { return; } setCoinSellerRechargeOrderActionLoading(`verify:${order.id}`); try { await verifyFinanceCoinSellerRechargeOrder(order.id); showToast("订单校验已完成", "success"); await loadCoinSellerRechargeOrders(); } catch (err) { showToast(err.message || "校验币商充值订单失败", "error"); } finally { setCoinSellerRechargeOrderActionLoading(""); } }; const grantCoinSellerRechargeOrder = async (order) => { if (!order?.id || !session?.canGrantCoinSellerRechargeOrder) { return; } setCoinSellerRechargeOrderActionLoading(`grant:${order.id}`); try { await grantFinanceCoinSellerRechargeOrder(order.id); showToast("币商充值已发放", "success"); await loadCoinSellerRechargeOrders(); } catch (err) { showToast(err.message || "发放币商充值失败", "error"); } finally { setCoinSellerRechargeOrderActionLoading(""); } }; const updateFilters = (nextFilters) => { setFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 })); }; const updateRechargeDetailFilters = (nextFilters) => { setRechargeDetailFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 })); }; const updateMyFilters = (nextFilters) => { setMyFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 })); }; const updateWithdrawalFilters = (nextFilters) => { setWithdrawalFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 })); }; const updateCoinSellerRechargeOrderFilters = (nextFilters) => { setCoinSellerRechargeOrderFilters((current) => ({ ...current, ...nextFilters, page: nextFilters.page || 1 })); }; const closeCreateDialog = (_, reason) => { if (submitting || reason === "backdropClick") { return; } setCreateDialogOpen(false); }; if (sessionState.loading) { return ; } if (sessionState.error) { return ; } if (!session?.canAccessFinance) { return ; } const presets = timePresets(); const activePreset = presets.find( (preset) => String(preset.range.startMs || "") === String(rechargeDetailFilters.timeRange.startMs || "") && String(preset.range.endMs || "") === String(rechargeDetailFilters.timeRange.endMs || ""), ); const isFinanceView = FINANCE_VIEWS.has(activeView); const financeToolbar = isFinanceView && session.canViewAppRechargeDetails ? ( <>
{presets.map((preset) => ( ))}
updateRechargeDetailFilters({ timeRange })} />
{[ ["usd", "USD"], ["coin", "金币"], ].map(([value, label]) => ( ))}
) : null; const appBar = isFinanceView && session.canViewAppRechargeDetails ? (
{rechargeApps.map((app) => ( ))}
) : null; return ( {activeView === "overview" && session.canViewAppRechargeDetails ? ( { updateRechargeDetailFilters({ paidState: "unsynced", rechargeType: "" }); setActiveView("rechargeDetails"); }} onOpenApp={(appCode) => { updateRechargeDetailFilters({ appCode, regionId: "" }); setActiveView("rechargeDetails"); }} onOpenWithdrawals={() => setActiveView("withdrawals")} /> ) : activeView === "coinSellerRechargeOrders" && session.canViewCoinSellerRechargeOrders ? ( ) : activeView === "recon" && session.canViewAppRechargeDetails ? ( setReconMonthOffset((current) => Math.min(0, current + delta))} /> ) : activeView === "rechargeDetails" && session.canViewAppRechargeDetails ? ( { loadRechargeDetails(); loadRechargeSummary(); loadRechargeOverview(); }} /> ) : activeView === "applications" && session.canAuditApplication ? ( ) : activeView === "withdrawals" && session.canViewWithdrawalApplications ? ( ) : ( setCreateDialogOpen(true)} onFiltersChange={updateMyFilters} onReload={loadMyApplications} /> )} 发起申请 setAuditTarget(null)} onSubmit={submitAudit} /> setWithdrawalAuditTarget(null)} onSubmit={submitWithdrawalAudit} /> ); } function StateScreen({ actionHref = "", actionText = "", title }) { return (

{title}

{actionHref ? {actionText} : null}
); } function cleanPayload(payload) { return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== "")); }