277 lines
8.7 KiB
JavaScript
277 lines
8.7 KiB
JavaScript
import {
|
|
AdminFilterResetButton,
|
|
AdminListBody,
|
|
AdminListPage,
|
|
AdminListToolbar,
|
|
} from "@/shared/ui/AdminListLayout.jsx";
|
|
import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx";
|
|
import { DataState } from "@/shared/ui/DataState.jsx";
|
|
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
|
import { TimeRangeFilter } from "@/shared/ui/TimeRangeFilter.jsx";
|
|
import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js";
|
|
import { formatMillis } from "@/shared/utils/time.js";
|
|
import { useHostWithdrawalsPage } from "@/features/host-org/hooks/useHostWithdrawalsPage.js";
|
|
import styles from "@/features/operations/operations.module.css";
|
|
|
|
const recordTypeOptions = [
|
|
["", "全部"],
|
|
["salary_transfer", "工资转币商"],
|
|
["withdrawal", "提现申请"],
|
|
];
|
|
|
|
const identityOptions = [
|
|
["", "全部"],
|
|
["host", "Host"],
|
|
["agency", "Agency"],
|
|
["bd", "BD"],
|
|
["bd_leader_admin", "BD Leader/Admin"],
|
|
];
|
|
|
|
const statusOptions = [
|
|
["", "全部"],
|
|
["completed", "已完成"],
|
|
["pending", "待审核"],
|
|
["approved", "已通过"],
|
|
["rejected", "已拒绝"],
|
|
];
|
|
|
|
const recordTypeLabels = Object.fromEntries(recordTypeOptions.filter(([value]) => value));
|
|
const identityLabels = Object.fromEntries(identityOptions.filter(([value]) => value));
|
|
const statusLabels = Object.fromEntries(statusOptions.filter(([value]) => value));
|
|
|
|
const baseColumns = [
|
|
{
|
|
key: "createdAtMs",
|
|
label: "时间",
|
|
render: (item) => formatMillis(item.createdAtMs),
|
|
width: "minmax(170px, 0.8fr)",
|
|
},
|
|
{
|
|
key: "recordType",
|
|
label: "类型",
|
|
render: (item) => recordTypeLabel(item.recordType),
|
|
width: "minmax(130px, 0.6fr)",
|
|
},
|
|
{
|
|
key: "identity",
|
|
label: "身份",
|
|
render: (item) => identityLabel(item.identity, item.salaryAssetType),
|
|
width: "minmax(150px, 0.65fr)",
|
|
},
|
|
{
|
|
key: "sourceUser",
|
|
label: "发起用户",
|
|
render: (item) => <AdminUserIdentity openInAppUserDetail user={item.sourceUser} userId={item.sourceUserId} />,
|
|
width: "minmax(230px, 1fr)",
|
|
},
|
|
{
|
|
key: "receiver",
|
|
label: "币商/收款",
|
|
render: (item) => <ReceiverCell item={item} />,
|
|
width: "minmax(240px, 1.05fr)",
|
|
},
|
|
{
|
|
key: "usdMinorAmount",
|
|
label: "USD金额",
|
|
render: (item) => <AmountText value={`$${formatUSDMinor(item.usdMinorAmount)}`} />,
|
|
width: "minmax(120px, 0.55fr)",
|
|
},
|
|
{
|
|
key: "coinAmount",
|
|
label: "币商金币",
|
|
render: (item) => formatCoinAmount(item),
|
|
width: "minmax(130px, 0.6fr)",
|
|
},
|
|
{
|
|
key: "status",
|
|
label: "状态",
|
|
render: (item) => <StatusBadge status={item.status} />,
|
|
width: "minmax(110px, 0.55fr)",
|
|
},
|
|
{
|
|
key: "recordId",
|
|
label: "交易/申请ID",
|
|
render: (item) => <RecordIdCell item={item} />,
|
|
width: "minmax(260px, 1.1fr)",
|
|
},
|
|
];
|
|
|
|
export function HostWithdrawalsPage() {
|
|
const page = useHostWithdrawalsPage();
|
|
const data = page.data || { items: [], page: 1, pageSize: 50, total: 0 };
|
|
const items = data.items || [];
|
|
const total = Number(data.total || 0);
|
|
const tableColumns = baseColumns.map((column) => {
|
|
if (column.key === "recordType") {
|
|
return {
|
|
...column,
|
|
filter: createOptionsColumnFilter({
|
|
emptyLabel: "全部",
|
|
options: recordTypeOptions,
|
|
placeholder: "类型",
|
|
value: page.recordType,
|
|
onChange: page.changeRecordType,
|
|
}),
|
|
};
|
|
}
|
|
if (column.key === "identity") {
|
|
return {
|
|
...column,
|
|
filter: createOptionsColumnFilter({
|
|
emptyLabel: "全部",
|
|
options: identityOptions,
|
|
placeholder: "身份",
|
|
value: page.identity,
|
|
onChange: page.changeIdentity,
|
|
}),
|
|
};
|
|
}
|
|
if (column.key === "sourceUser") {
|
|
return {
|
|
...column,
|
|
filter: createTextColumnFilter({
|
|
activeLabel: page.keyword ? `用户 ${page.keyword}` : "",
|
|
placeholder: "用户/交易/地址",
|
|
value: page.keyword,
|
|
onChange: page.changeKeyword,
|
|
}),
|
|
};
|
|
}
|
|
if (column.key === "status") {
|
|
return {
|
|
...column,
|
|
filter: createOptionsColumnFilter({
|
|
emptyLabel: "全部",
|
|
options: statusOptions,
|
|
placeholder: "状态",
|
|
value: page.status,
|
|
onChange: page.changeStatus,
|
|
}),
|
|
};
|
|
}
|
|
return column;
|
|
});
|
|
|
|
return (
|
|
<AdminListPage>
|
|
<AdminListToolbar
|
|
filters={
|
|
<>
|
|
<TimeRangeFilter value={page.timeRange} onChange={page.changeTimeRange} />
|
|
<AdminFilterResetButton disabled={!page.hasActiveFilters} onClick={page.resetFilters} />
|
|
</>
|
|
}
|
|
/>
|
|
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
|
|
<AdminListBody>
|
|
<DataTable
|
|
columns={tableColumns}
|
|
emptyLabel="当前无主播提现记录"
|
|
items={items}
|
|
minWidth="1510px"
|
|
pagination={
|
|
total > 0
|
|
? {
|
|
page: page.page,
|
|
pageSize: data.pageSize || 50,
|
|
total,
|
|
onPageChange: page.setPage,
|
|
}
|
|
: undefined
|
|
}
|
|
rowKey={(item) => item.id}
|
|
/>
|
|
</AdminListBody>
|
|
</DataState>
|
|
</AdminListPage>
|
|
);
|
|
}
|
|
|
|
function ReceiverCell({ item }) {
|
|
if (item.recordType === "salary_transfer") {
|
|
return <AdminUserIdentity openInAppUserDetail user={item.sellerUser} userId={item.sellerUserId} />;
|
|
}
|
|
const method = String(item.withdrawMethod || "").trim();
|
|
const address = String(item.withdrawAddress || "").trim();
|
|
if (!method && !address) {
|
|
return "-";
|
|
}
|
|
return (
|
|
<span className={styles.identityText}>
|
|
<span className={styles.primaryText}>{method || "-"}</span>
|
|
{address ? <span className={styles.meta}>{address}</span> : null}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
function RecordIdCell({ item }) {
|
|
const primary = item.transactionId || item.applicationId || item.id || "-";
|
|
const secondary = item.commandId || item.freezeTransactionId || item.auditTransactionId || "";
|
|
return (
|
|
<span className={styles.identityText}>
|
|
<span className={styles.primaryText}>{primary}</span>
|
|
{secondary ? <span className={styles.meta}>{secondary}</span> : null}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
function StatusBadge({ status }) {
|
|
const normalized = String(status || "").trim();
|
|
return (
|
|
<span className={`status-badge status-badge--${statusTone(normalized)}`}>
|
|
<span className="status-point" />
|
|
{statusLabel(normalized)}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
function AmountText({ value }) {
|
|
return <span className={styles.amount}>{value}</span>;
|
|
}
|
|
|
|
function recordTypeLabel(value) {
|
|
return recordTypeLabels[value] || value || "-";
|
|
}
|
|
|
|
function identityLabel(identity, salaryAssetType) {
|
|
return identityLabels[identity] || salaryAssetLabel(salaryAssetType) || identity || "-";
|
|
}
|
|
|
|
function salaryAssetLabel(assetType) {
|
|
const labels = {
|
|
AGENCY_SALARY_USD: "Agency",
|
|
ADMIN_SALARY_USD: "BD Leader/Admin",
|
|
BD_SALARY_USD: "BD",
|
|
HOST_SALARY_USD: "Host",
|
|
};
|
|
return labels[assetType] || "";
|
|
}
|
|
|
|
function statusLabel(status) {
|
|
return statusLabels[status] || status || "-";
|
|
}
|
|
|
|
function statusTone(status) {
|
|
if (status === "completed" || status === "approved") {
|
|
return "succeeded";
|
|
}
|
|
if (status === "pending") {
|
|
return "warning";
|
|
}
|
|
if (status === "rejected") {
|
|
return "danger";
|
|
}
|
|
return "stopped";
|
|
}
|
|
|
|
function formatCoinAmount(item) {
|
|
if (item.recordType !== "salary_transfer") {
|
|
return "-";
|
|
}
|
|
return Number(item.coinAmount || 0).toLocaleString("zh-CN");
|
|
}
|
|
|
|
function formatUSDMinor(value) {
|
|
return (Number(value || 0) / 100).toFixed(2);
|
|
}
|