import CloudSyncOutlined from "@mui/icons-material/CloudSyncOutlined";
import FileDownloadOutlined from "@mui/icons-material/FileDownloadOutlined";
import RefreshOutlined from "@mui/icons-material/RefreshOutlined";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TextField from "@mui/material/TextField";
import { useState } from "react";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
import { TimeRangeFilter } from "@/shared/ui/TimeRangeFilter.jsx";
import {
EMPTY_RECHARGE_SUMMARY,
formatAmount,
formatMicroMoney,
formatTime,
formatUsdMinor,
rechargeSourceTone,
rechargeTypeLabel,
} from "../format.js";
const SOURCE_FILTERS = [
{ label: "全部来源", value: "" },
{ label: "谷歌充值", value: "google_play_recharge" },
{ label: "三方充值", value: "third_party" },
{ label: "币商充值", value: "coin_seller" },
];
const KPI_CARDS = [
{ filter: "", key: "total", label: "充值总和", tone: "total" },
{ filter: "google_play_recharge", key: "googlePlay", label: "谷歌充值", tone: "google" },
{ filter: "third_party", key: "thirdParty", label: "三方充值", tone: "thirdparty" },
{ filter: "coin_seller", key: "coinSeller", label: "币商充值", tone: "coinseller" },
];
export function FinanceRechargeDetailList({
bills,
error,
exporting,
filters,
googlePaidRefreshing,
loading,
onExport,
onFiltersChange,
onRefreshGooglePaid,
onReload,
options,
regions,
summary,
summaryLoading,
}) {
const [showCoinColumns, setShowCoinColumns] = useState(false);
const items = bills.items || [];
const page = Number(bills.page || filters.page || 1);
const pageSize = Number(bills.pageSize || 50);
const total = Number(bills.total || 0);
const hasNextPage = page * pageSize < total;
const regionOptions = regions || [];
const visibleColumnCount = showCoinColumns ? 9 : 7;
const pendingGoogleTxIds = items
.filter((item) => item.rechargeType === "google_play_recharge" && !item.paidSyncedAtMs)
.map((item) => item.transactionId)
.filter(Boolean);
return (
onFiltersChange({ rechargeType: value })}
/>
onFiltersChange({ appCode: event.target.value, regionId: "" })}
>
{options.apps.map((app) => (
))}
onFiltersChange({ regionId: event.target.value })}
>
{regionOptions.map((region) => (
))}
onFiltersChange({ rechargeType: event.target.value })}
>
{SOURCE_FILTERS.map((source) => (
))}
onFiltersChange({ timeRange })}
/>
onFiltersChange({ keyword: event.target.value })}
/>
{timePresets().map((preset) => (
))}
}
title={filters.appCode ? "查询本页谷歌订单的用户实付币种和金额" : "先选择 APP 后可查询"}
variant="outlined"
onClick={() => onRefreshGooglePaid(pendingGoogleTxIds)}
>
{googlePaidRefreshing ? "查询中…" : "查询谷歌实付"}
}
title={
filters.appCode ? "按当前筛选导出全部明细(CSV,可用 Excel 打开)" : "先选择 APP 后可导出"
}
variant="contained"
onClick={onExport}
>
{exporting ? "导出中…" : "导出报表"}
}
variant="outlined"
onClick={onReload}
>
刷新
{error ? {error}
: null}
{loading && !items.length ? : null}
{!loading || items.length ? (
充值时间
APP
充值来源
充值订单号
{showCoinColumns ? 充值金币 : null}
{showCoinColumns ? 金币换算 : null}
账单金额
用户实付
三方税率扣款
{items.length ? (
items.map((item) => (
{formatTime(item.createdAtMs)}
{appName(item, options.apps)}
{showCoinColumns ? (
{coinText(item.coinAmount)}
) : null}
{showCoinColumns ? {exchangeText(item)} : null}
{billAmountText(item)}
{userPaidText(item)}
{taxDeductionText(item)}
))
) : (
当前筛选条件下没有账单
)}
) : null}
共 {formatAmount(total)} 笔 · 第 {page} / {Math.max(1, Math.ceil(total / pageSize))} 页
);
}
function RechargeKpiBand({ activeFilter, loading, onSelect, showCoins, summary }) {
const data = summary || EMPTY_RECHARGE_SUMMARY;
const totalUsd = Number(data.total?.usdMinorAmount) || 0;
return (
{KPI_CARDS.map(({ filter, key, label, tone }) => {
const bucket = data[key] || { billCount: 0, coinAmount: 0, usdMinorAmount: 0 };
const active = activeFilter === filter;
return (
);
})}
);
}
function kpiMetaText(bucket, key, totalUsd, showCoins) {
const parts = [`${formatAmount(bucket.billCount)} 笔`];
if (key !== "total" && totalUsd > 0) {
const share = ((Number(bucket.usdMinorAmount) || 0) / totalUsd) * 100;
parts.push(`占比 ${share.toLocaleString("zh-CN", { maximumFractionDigits: 1 })}%`);
}
if (showCoins) {
parts.push(`${formatAmount(bucket.coinAmount)} 金币`);
}
return parts.join(" · ");
}
function SourceBadge({ rechargeType }) {
return (
{rechargeTypeLabel(rechargeType)}
);
}
function AmountText({ children, value }) {
const negative = hasNumber(value) && Number(value) < 0;
return {children};
}
const DAY_MS = 86_400_000;
const CHINA_OFFSET_MS = 8 * 3_600_000;
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 [
{ label: "不限", range: { endMs: "", startMs: "" } },
{ label: "今天", range: { endMs: todayStart + DAY_MS, startMs: todayStart } },
{ label: "昨天", range: { endMs: todayStart, startMs: todayStart - DAY_MS } },
{ label: "近7天", range: { endMs: todayStart + DAY_MS, startMs: todayStart - 6 * DAY_MS } },
{ label: "本月", range: { endMs: todayStart + DAY_MS, startMs: monthStart } },
];
}
function isPresetActive(timeRange, preset) {
const current = timeRange || { endMs: "", startMs: "" };
return (
String(current.startMs || "") === String(preset.range.startMs || "") &&
String(current.endMs || "") === String(preset.range.endMs || "")
);
}
function Stacked({ primary, secondary }) {
return (
{primary || "-"}
{secondary || ""}
);
}
function appName(item, apps) {
return item.appName || apps.find((app) => app.appCode === item.appCode)?.appName || item.appCode || "-";
}
function secondaryOrderNo(item) {
return [item.commandId, item.externalRef].filter(Boolean).join(" / ");
}
function coinText(value) {
const text = formatAmount(value);
return text === "-" ? "-" : `${text} 金币`;
}
function exchangeText(item) {
if (hasNumber(item.exchangeCoinAmount) || hasNumber(item.exchangeUsdMinorAmount)) {
return `${coinText(item.exchangeCoinAmount)} = ${formatUsdMinor(item.exchangeUsdMinorAmount, "USD")}`;
}
return formatUsdMinor(item.usdMinorAmount, "USD");
}
function billAmountText(item) {
if (item.billAmountText) {
return item.billAmountText;
}
const amount = hasNumber(item.billAmountMinor)
? item.billAmountMinor
: hasPositiveNumber(item.providerAmountMinor)
? item.providerAmountMinor
: item.usdMinorAmount;
return formatUsdMinor(amount, item.currencyCode || "USD");
}
function userPaidText(item) {
if (item.userPaidText) {
return item.userPaidText;
}
if (hasNonZeroNumber(item.userPaidAmountMicro) && item.userPaidCurrencyCode) {
return formatMicroMoney(item.userPaidAmountMicro, item.userPaidCurrencyCode);
}
if (isGoogleBill(item) && !item.paidSyncedAtMs) {
return "未同步(点“查询谷歌实付”)";
}
if (hasPositiveNumber(item.userPaidAmountMinor)) {
return formatUsdMinor(item.userPaidAmountMinor, item.userPaidCurrencyCode || item.currencyCode || "USD");
}
return "-";
}
function taxDeductionText(item) {
const currency = item.userPaidCurrencyCode || item.currencyCode || "USD";
const fee = hasPositiveNumber(item.providerFeeMicro) ? item.providerFeeMicro : 0;
const tax = hasPositiveNumber(item.providerTaxMicro) ? item.providerTaxMicro : 0;
const deduction = fee + tax;
if (deduction > 0) {
const parts = [formatMicroMoney(deduction, currency)];
if (hasPositiveNumber(item.userPaidAmountMicro)) {
const percent = (deduction / item.userPaidAmountMicro) * 100;
parts.push(`${percent.toLocaleString("zh-CN", { maximumFractionDigits: 2 })}%`);
}
return (
{parts.join(" / ")}
{deductionBreakdown(fee, tax, currency)}
);
}
if (item.taxDeductionText) {
return item.taxDeductionText;
}
if (isGoogleBill(item) && !item.paidSyncedAtMs) {
return "未同步";
}
return "-";
}
function deductionBreakdown(fee, tax, currency) {
const parts = [];
if (fee > 0) {
parts.push(`手续费 ${formatMicroMoney(fee, currency)}`);
}
if (tax > 0) {
parts.push(`税费 ${formatMicroMoney(tax, currency)}`);
}
return parts.join(" + ");
}
function isGoogleBill(item) {
return item.rechargeType === "google_play_recharge" || item.providerCode === "google_play";
}
function hasNumber(value) {
if (value === null || value === undefined || value === "") {
return false;
}
return Number.isFinite(Number(value));
}
function hasPositiveNumber(value) {
return hasNumber(value) && Number(value) > 0;
}
function hasNonZeroNumber(value) {
return hasNumber(value) && Number(value) !== 0;
}