import Add from "@mui/icons-material/Add";
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";
import ArrowUpwardOutlined from "@mui/icons-material/ArrowUpwardOutlined";
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
import MonetizationOnOutlined from "@mui/icons-material/MonetizationOnOutlined";
import ReceiptLongOutlined from "@mui/icons-material/ReceiptLongOutlined";
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
import SwapVertOutlined from "@mui/icons-material/SwapVertOutlined";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
import TextField from "@mui/material/TextField";
import {
AdminFormAmountField,
AdminFormFieldGrid,
AdminFormReadOnlyField,
AdminFormSection,
} from "@/shared/ui/AdminFormDialog.jsx";
import { formatMillis } from "@/shared/utils/time.js";
import { DataState } from "@/shared/ui/DataState.jsx";
import { AdminActionIconButton, AdminRowActions } from "@/shared/ui/AdminListLayout.jsx";
import { InlineEditableCell } from "@/shared/ui/InlineEditableCell.jsx";
import { SideDrawer } from "@/shared/ui/SideDrawer.jsx";
import { coinSellerStatusFilters } from "@/features/host-org/constants.js";
import { HostOrgActionModal } from "@/features/host-org/components/HostOrgActionModal.jsx";
import { HostOrgTable } from "@/features/host-org/components/HostOrgTable.jsx";
import { HostOrgToolbar } from "@/features/host-org/components/HostOrgToolbar.jsx";
import { useHostCoinSellersPage } from "@/features/host-org/hooks/useHostCoinSellersPage.js";
import { CoinSellerLedgerTable } from "@/features/operations/components/CoinSellerLedgerTable.jsx";
import { createRegionColumnFilter } from "@/shared/ui/RegionFilterControl.jsx";
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
import styles from "@/features/host-org/host-org.module.css";
const baseColumns = [
{
key: "seller",
label: "币商",
render: (item) => ,
width: "minmax(220px, 1.3fr)",
},
{
key: "regionId",
label: "区域",
render: (item, _index, context) => regionName(item, context?.regionOptions),
width: "minmax(140px, 0.8fr)",
},
{
key: "contact",
label: "联系方式",
render: (item, _index, context) => ,
width: "minmax(170px, 1fr)",
},
{
key: "merchantBalance",
label: "币商余额",
sortable: true,
sortField: "merchant_balance",
render: (item) => formatNumber(item.merchantBalance),
width: "minmax(120px, 0.8fr)",
},
{
key: "totalRechargeUsdt",
label: "累充USDT",
sortable: true,
sortField: "total_recharge_usdt",
render: (item) => formatUSDTMicro(item.totalRechargeUsdtMicro),
width: "minmax(130px, 0.8fr)",
},
{
key: "updatedAtMs",
label: "更新时间",
render: (item) => formatMillis(item.updatedAtMs),
width: "minmax(170px, 1fr)",
},
];
export function HostCoinSellersPage() {
const page = useHostCoinSellersPage();
const items = page.data.items || [];
const total = page.data.total || 0;
const createDisabled = !page.abilities.canCreate;
const updateDisabled = !page.abilities.canUpdate;
const columns = [
...baseColumns.slice(0, 2),
{
key: "status",
label: "状态",
render: (item) => ,
width: "minmax(90px, 0.6fr)",
},
...baseColumns.slice(2),
page.abilities.canStockCredit || page.abilities.canLedger
? {
key: "actions",
label: "操作",
render: (item) => ,
width: "minmax(90px, 0.5fr)",
}
: null,
]
.filter(Boolean)
.map((column) => {
if (column.key === "seller") {
return {
...column,
filter: createTextColumnFilter({
placeholder: "搜索用户 ID、展示 ID、用户名",
value: page.query,
onChange: page.changeQuery,
}),
};
}
if (column.key === "status") {
return {
...column,
filter: createOptionsColumnFilter({
options: coinSellerStatusFilters,
placeholder: "搜索状态",
value: page.status,
onChange: page.changeStatus,
}),
};
}
if (column.sortable) {
return {
...column,
header: (
),
};
}
return column;
});
const regionFilter = createRegionColumnFilter({
loading: page.loadingRegions,
options: page.regionOptions,
value: page.regionId,
onChange: page.changeRegionId,
});
const leadingActions = [
page.abilities.canExchangeRate
? { icon: , label: "工资兑换比例", onClick: page.openRateSettings }
: null,
].filter(Boolean);
const toolbarActions = [
page.abilities.canCreate
? { icon: , label: "添加币商", onClick: page.openCreateSeller, variant: "primary" }
: null,
].filter(Boolean);
return (
0
? {
page: page.page,
pageSize: page.data.pageSize || 50,
total,
onPageChange: page.setPage,
}
: undefined
}
regionFilter={regionFilter}
rowKey={(item) => item.userId}
/>
page.setCreateForm({ ...page.createForm, targetUserId: event.target.value })}
/>
page.setCreateForm({ ...page.createForm, contact: event.target.value })}
/>
page.setEditForm({ ...page.editForm, contact: event.target.value })}
/>
page.setStockForm({
...page.stockForm,
type: event.target.value,
rechargeAmount: "",
})
}
>
page.setStockForm({ ...page.stockForm, coinAmount: event.target.value })
}
/>
{page.stockForm.type === "usdt_purchase" ? (
page.setStockForm({ ...page.stockForm, rechargeAmount: event.target.value })
}
/>
) : null}
page.setStockForm({ ...page.stockForm, reason: event.target.value })}
/>
{page.selectedSeller ? (
) : null}
>
}
open={page.activeAction === "rates"}
title="币商工资兑换比例"
width="wide"
onClose={page.closeAction}
>
);
}
function SellerIdentity({ item }) {
const name = item.username || "-";
const meta = item.displayUserId || item.userId;
return (
{item.avatar ? (

) : (
{name.slice(0, 1).toUpperCase()}
)}
{name}
{meta}
);
}
function SellerStatusSwitch({ item, page }) {
return (
page.toggleSeller(item, event.target.checked)}
/>
);
}
function SellerContact({ item, page }) {
const contact = String(item.contact || "").trim();
return (
String(value ?? "").trim()}
saving={Boolean(page?.contactSavingIds?.[item.userId])}
value={contact}
variant="link"
width="100%"
onSave={(nextValue) => page.saveSellerContact(item, nextValue)}
/>
);
}
function SellerActions({ item, page }) {
return (
{page.abilities.canLedger ? (
page.openSellerLedger(item)}>
) : null}
{page.abilities.canStockCredit ? (
page.openStockCredit(item)}
>
) : null}
);
}
function sellerDrawerTitle(seller) {
if (!seller) {
return "";
}
const name = seller.username || "-";
const displayId = seller.displayUserId || seller.userId || "";
return displayId ? `${name} / ${displayId}` : name;
}
function regionName(item, regionOptions = []) {
if (item.regionName) {
return item.regionName;
}
const option = regionOptions.find((region) => String(region.value) === String(item.regionId));
return option?.name || item.regionId || "-";
}
function formatNumber(value) {
return Number(value || 0).toLocaleString("zh-CN");
}
function formatUSDTMicro(value) {
const amount = Number(value || 0);
if (!Number.isFinite(amount) || amount <= 0) {
return "0";
}
const whole = Math.trunc(amount / 1_000_000);
const fraction = Math.trunc(Math.abs(amount % 1_000_000));
const trimmedFraction = String(fraction).padStart(6, "0").replace(/0+$/, "");
return trimmedFraction ? `${whole}.${trimmedFraction}` : String(whole);
}
function SortHeader({ field, label, onToggle, sortBy, sortDirection }) {
const active = sortBy === field;
const SortIcon =
active && sortDirection === "asc"
? ArrowUpwardOutlined
: active && sortDirection === "desc"
? ArrowDownwardOutlined
: SwapVertOutlined;
const directionLabel = sortDirection === "asc" ? "升序" : "降序";
const nextDirectionLabel = active && sortDirection === "desc" ? "升序" : "降序";
return (
);
}
const coinLedgerActionSx = {
borderColor: "rgba(37, 99, 235, 0.28)",
backgroundColor: "rgba(37, 99, 235, 0.08)",
color: "#1d4ed8",
"&:hover": {
borderColor: "rgba(37, 99, 235, 0.48)",
backgroundColor: "rgba(37, 99, 235, 0.14)",
color: "#1e40af",
},
"& .MuiSvgIcon-root": {
color: "#2563eb",
},
};
const coinCreditActionSx = {
borderColor: "rgba(245, 158, 11, 0.45)",
backgroundColor: "rgba(251, 191, 36, 0.16)",
color: "#b45309",
"&:hover": {
borderColor: "rgba(245, 158, 11, 0.72)",
backgroundColor: "rgba(251, 191, 36, 0.24)",
color: "#92400e",
},
"& .MuiSvgIcon-root": {
color: "#f59e0b",
},
};