import Add from "@mui/icons-material/Add"; import Inventory2Outlined from "@mui/icons-material/Inventory2Outlined"; import UndoOutlined from "@mui/icons-material/UndoOutlined"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; import { AdminFormDialog, AdminFormFieldGrid, AdminFormSection } from "@/shared/ui/AdminFormDialog.jsx"; import { AdminUserIdentity } from "@/shared/ui/AdminUserIdentity.jsx"; import { DataState } from "@/shared/ui/DataState.jsx"; import { DataTable } from "@/shared/ui/DataTable.jsx"; import { AdminActionIconButton, AdminListBody, AdminListPage, AdminListToolbar, AdminRowActions, } from "@/shared/ui/AdminListLayout.jsx"; import { createOptionsColumnFilter, createTextColumnFilter } from "@/shared/ui/tableFilters.js"; import { formatMillis } from "@/shared/utils/time.js"; import { GiftResourceSelectField } from "@/features/resources/components/GiftResourceSelectDrawer.jsx"; import { resourceGroupAssetLabels, resourceGrantStatusFilters, resourceGrantStatusLabels, resourceGrantSubjectLabels, resourceTypeLabels, } from "@/features/resources/constants.js"; import { useResourceGrantListPage } from "@/features/resources/hooks/useResourcePages.js"; import styles from "@/features/resources/resources.module.css"; const grantColumns = [ { key: "grant", label: "来源", width: "minmax(220px, 1fr)", render: (grant) => (
{grantSourceLabel(grant.grantSource)}
), }, { key: "target", label: "用户信息", width: "minmax(240px, 1.1fr)", render: (grant) => , }, { key: "operator", label: "操作人", width: "minmax(190px, 0.9fr)", render: (grant) => , }, { key: "subject", label: "发放方式", width: "minmax(170px, 0.75fr)", render: (grant) => , }, { key: "items", label: "发放内容", width: "minmax(320px, 1.5fr)", render: (grant) => , }, { key: "status", label: "状态", width: "minmax(100px, 0.55fr)", render: (grant) => ( {resourceGrantStatusLabels[grant.status] || grant.status || "-"} ), }, { key: "reason", label: "原因", width: "minmax(190px, 0.95fr)", render: (grant) => , }, { key: "time", label: "创建时间", width: "minmax(170px, 0.9fr)", render: (grant) => formatMillis(grant.createdAtMs), }, ]; export function ResourceGrantListPage() { const page = useResourceGrantListPage(); const items = page.data.items || []; const total = page.data.total || 0; const columns = [ ...grantColumns.map((column) => { if (column.key === "target") { return { ...column, filter: createTextColumnFilter({ placeholder: "搜索用户 ID", value: page.query, onChange: page.changeQuery, }), }; } if (column.key === "status") { return { ...column, filter: createOptionsColumnFilter({ options: resourceGrantStatusFilters, placeholder: "搜索状态", value: page.status, onChange: page.changeStatus, }), }; } return column; }), { key: "actions", label: "操作", width: "minmax(88px, 0.45fr)", render: (grant) => , }, ]; return ( ) : null } /> 0 ? { page: page.page, pageSize: page.data.pageSize || 50, total, onPageChange: page.setPage, } : undefined } rowKey={(grant) => grant.grantId} /> ); } function ResourceGrantActions({ grant, page }) { const canRevoke = page.abilities.canRevokeGrant && grant?.status === "succeeded" && grant?.grantSubjectType === "resource_group"; if (!canRevoke) { return -; } return ( page.revokeGrant(grant)} > ); } function ResourceGrantDialog({ disabled, form, giftOptions, giftTypeOptions, groupOptions, loading, onClose, onSubmit, open, optionsLoading, resourceOptions, setForm, }) { const submitDisabled = disabled || loading || optionsLoading; return ( setForm({ ...form, targetUserId: event.target.value })} /> setForm({ ...form, groupId: "", resourceId: "", resourceIds: [], subjectType: event.target.value, }) } > 资源 资源组 {form.subjectType === "resource" ? ( <> setForm({ ...form, resourceId: resourceIds[0] || "", resourceIds }) } /> setForm({ ...form, quantity: event.target.value })} /> setForm({ ...form, durationDays: event.target.value })} /> ) : ( setForm({ ...form, groupId: event.target.value })} > {groupOptions.map((group) => ( {group.name || group.groupCode || group.groupId} ))} )} setForm({ ...form, reason: event.target.value })} /> ); } function grantStatusTone(status) { if (status === "revoked") { return "stopped"; } if (status === "failed") { return "danger"; } if (status === "pending") { return "warning"; } return status || "stopped"; } const dangerActionSx = { borderColor: "var(--danger-border)", color: "var(--danger)", "&:hover": { borderColor: "var(--danger-border-strong)", backgroundColor: "var(--danger-surface)", color: "var(--danger)", }, }; function GrantItems({ grant }) { const items = grant.items || []; if (!items.length) { return "-"; } return (
{items.map((item) => ( ))}
); } function GrantItemMedia({ item }) { const snapshot = parseGrantItemSnapshot(item.resourceSnapshotJson); const walletAssetType = snapshotValue(snapshot, "walletAssetType", "wallet_asset_type", "WalletAssetType"); if (walletAssetType) { const label = resourceGroupAssetLabels[walletAssetType] || walletAssetType; const amount = snapshotValue(snapshot, "walletAssetAmount", "wallet_asset_amount", "WalletAssetAmount", "quantity"); return {`${label} ${formatNumber(amount || item.quantity)}`}; } const label = grantItemLabel(item, snapshot); const imageUrl = grantItemImageUrl(snapshot); return ( {imageUrl ? : } {label} ); } function GrantSubject({ grant }) { const subjectLabel = resourceGrantSubjectLabels[grant.grantSubjectType] || grant.grantSubjectType || "发放"; const sourceLabel = grantSourceLabel(grant.grantSource); return (
{sourceLabel === "-" ? subjectLabel : `${subjectLabel} · ${sourceLabel}`} {subjectMeta(grant)}
); } function GrantReason({ grant }) { const label = reasonLabel(grant.reason); const raw = String(grant.reason || "").trim(); return (
{label} {raw && raw !== label ? {raw} : null}
); } function GrantTargetUser({ grant }) { const user = grant.targetUser || grant.target_user || grant.user || {}; const userId = user.userId || user.user_id || grant.targetUserId; return ( ); } function GrantOperator({ grant }) { const operator = grant.operator || {}; const source = operator.source || grant.grantSource || ""; const operatorUserId = operator.userId || grant.operatorUserId; if (source === "manager_center") { return ( ); } return "-"; } const grantSourceLabels = { achievement: "成就奖励", admin: "后台发放", agency_opening: "Agency 开通", cp_weekly_rank: "CP 周榜奖励", cumulative_recharge_reward: "累充奖励", first_recharge_reward: "首充奖励", game_robot_init: "机器人初始化", growth_level: "成长等级奖励", manager_center: "经理中心", registration_reward: "注册奖励", resource_shop: "道具商店", room_rocket: "房间火箭", seven_day_checkin: "七日签到", weekly_star: "周星奖励", wheel_reward: "转盘奖励", }; const grantReasonLabels = { ...grantSourceLabels, growth_level_registration_badge: "注册成长等级徽章", "resource shop purchase": "道具商店购买", }; function grantSourceLabel(source) { const key = String(source || "").trim(); return grantSourceLabels[key] || key || "-"; } function reasonLabel(reason) { const key = String(reason || "").trim(); return grantReasonLabels[key] || key || "-"; } function subjectMeta(grant) { const subjectLabel = resourceGrantSubjectLabels[grant.grantSubjectType] || grant.grantSubjectType || "对象"; return grant.grantSubjectId ? `${subjectLabel} ID ${grant.grantSubjectId}` : "-"; } function grantItemLabel(item, snapshot = parseGrantItemSnapshot(item.resourceSnapshotJson)) { const walletAssetType = snapshotValue(snapshot, "walletAssetType", "wallet_asset_type", "WalletAssetType"); if (walletAssetType) { const label = resourceGroupAssetLabels[walletAssetType] || walletAssetType; const amount = snapshotValue(snapshot, "walletAssetAmount", "wallet_asset_amount", "WalletAssetAmount", "quantity"); return `${label} ${formatNumber(amount || item.quantity)}`; } const resourceName = snapshotValue(snapshot, "name", "Name", "resourceName", "resource_name") || `资源 ${item.resourceId || "-"}`; const resourceType = snapshotValue(snapshot, "resourceType", "resource_type", "ResourceType"); const typeLabel = resourceTypeLabels[resourceType] || resourceType || "资源"; const quantity = Number(item.quantity || 0); const parts = [`${resourceName} x${formatNumber(quantity)}`, typeLabel]; if (item.durationMs) { parts.push(formatDuration(item.durationMs)); } return parts.join(" · "); } function grantItemImageUrl(snapshot) { return imageURL( snapshotValue( snapshot, "previewUrl", "preview_url", "PreviewURL", "assetUrl", "asset_url", "AssetURL", "animationUrl", "animation_url", "AnimationURL", ), ); } function parseGrantItemSnapshot(value) { if (!value || typeof value !== "string") { return {}; } try { const parsed = JSON.parse(value); return parsed && typeof parsed === "object" ? parsed : {}; } catch { return {}; } } function snapshotValue(snapshot, ...keys) { for (const key of keys) { const value = snapshot?.[key]; if (value !== undefined && value !== null && value !== "") { return value; } } return ""; } function formatNumber(value) { return Number(value || 0).toLocaleString("zh-CN"); } function imageURL(value) { const url = String(value || "").trim(); return /\.(avif|gif|jpe?g|png|webp)(\?|#|$)/i.test(url) ? url : ""; } function formatDuration(durationMs) { const days = Number(durationMs || 0) / (24 * 60 * 60 * 1000); return Number.isInteger(days) ? `${days}天` : `${days.toFixed(1)}天`; }