import Add from "@mui/icons-material/Add"; import EditOutlined from "@mui/icons-material/EditOutlined"; import Inventory2Outlined from "@mui/icons-material/Inventory2Outlined"; import RefreshOutlined from "@mui/icons-material/RefreshOutlined"; import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx"; import MenuItem from "@mui/material/MenuItem"; import Tab from "@mui/material/Tab"; import Tabs from "@mui/material/Tabs"; import TextField from "@mui/material/TextField"; import { AdminFormDialog, AdminFormFieldGrid, AdminFormSection, AdminFormSwitchField, } from "@/shared/ui/AdminFormDialog.jsx"; import { Button } from "@/shared/ui/Button.jsx"; import { DataState } from "@/shared/ui/DataState.jsx"; import { DataTable } from "@/shared/ui/DataTable.jsx"; import { AdminActionIconButton, AdminListBody, AdminListPage, AdminRowActions } from "@/shared/ui/AdminListLayout.jsx"; import { ResourceGroupSelectField } from "@/shared/ui/ResourceGroupSelectDrawer.jsx"; import { formatMillis } from "@/shared/utils/time.js"; import { levelStatusLabels, levelTrackLabels, levelTrackOptions } from "@/features/level-config/constants.js"; import { useLevelConfigPage } from "@/features/level-config/hooks/useLevelConfigPage.js"; import { ResourceSelectField } from "@/features/resources/components/ResourceSelectDrawer.jsx"; import { LevelConfigBatchDialog } from "@/features/level-config/components/LevelConfigBatchDialog.jsx"; import { levelAvatarFrameResourcesForTrack, levelBadgeResourcesForTrack, levelShortBadgeResourcesForTrack, withSelectedResource, } from "@/features/level-config/resourceFilters.js"; import styles from "@/features/level-config/level-config.module.css"; export function LevelConfigPage() { const page = useLevelConfigPage(); const trackTabs = buildTrackTabs(page.config.tracks); return (
page.changeTrack(nextTrack)} > {trackTabs.map((item) => ( {item.label} {item.ruleCount} 条规则 · {item.tierCount} 个等级层 } value={item.value} /> ))}
{page.tracks.length ? (
{page.tracks.map((track) => ( ))}
) : (
当前无数据
)}
); } function LevelTrackSection({ page, track }) { const rules = track.rules || []; const tiers = track.tiers || []; const totalRules = track.rules?.length || 0; const totalTiers = track.tiers?.length || 0; const ruleColumns = buildRuleColumns(page); const tierColumns = buildTierColumns(page); return (
{track.name || levelTrackLabels[track.track] || track.track}
{track.track} {totalRules} 条规则 {totalTiers} 个等级层 排序 {track.sortOrder || 0}
page.openTrackEdit(track)} > page.openRuleCreate(track.track)} > page.openTierCreate(track.track)} >
} onClick={() => page.openRuleBatchEdit(track)} > 批量编辑 } countLabel={`${rules.length} 条`} title="每级升级数值" /> `${rule.track}-${rule.level}`} />
} onClick={() => page.openTierBatchEdit(track)} > 批量编辑 } countLabel={`${tiers.length} 个`} title="等级层展示与礼物" /> tier.tierId || `${tier.track}-${tier.minLevel}-${tier.maxLevel}`} />
); } function TableBlockHeader({ actions, countLabel, title }) { return (
{title}
{countLabel} {actions ?
{actions}
: null}
); } function buildTrackTabs(tracks = []) { return levelTrackOptions.map(([value, label]) => { const track = tracks.find((item) => item.track === value); return { label: track?.name || label, ruleCount: track?.rules?.length || 0, tierCount: track?.tiers?.length || 0, value, }; }); } function buildRuleColumns(page) { return [ { key: "level", label: "等级", width: "minmax(180px, 0.8fr)", render: (rule) => , }, { key: "requiredValue", label: "升级数值", width: "minmax(140px, 0.65fr)", render: (rule) => formatNumber(rule.requiredValue), }, { key: "longBadge", label: "长徽章素材", width: "minmax(200px, 1fr)", render: (rule) => , }, { key: "avatarFrame", label: "等级头像框", width: "minmax(200px, 1fr)", render: (rule) => , }, { key: "shortBadge", label: "短徽章素材", width: "minmax(200px, 1fr)", render: (rule) => , }, { key: "reward", label: "升级礼物", width: "minmax(200px, 1fr)", render: (rule) => , }, { key: "status", label: "状态", width: "minmax(116px, 0.55fr)", render: (rule) => , }, { key: "sort", label: "排序", width: "minmax(90px, 0.45fr)", render: (rule) => rule.sortOrder || 0, }, { key: "updatedAt", label: "更新时间", width: "minmax(170px, 0.75fr)", render: (rule) => formatMillis(rule.updatedAtMs || rule.createdAtMs), }, { key: "actions", label: "操作", width: "minmax(76px, 0.35fr)", render: (rule) => , }, ]; } function buildTierColumns(page) { return [ { key: "range", label: "等级层", width: "minmax(220px, 1fr)", render: (tier) => , }, { key: "frame", label: "头像框", width: "minmax(200px, 0.9fr)", render: (tier) => ( ), }, { key: "badge", label: "徽章", width: "minmax(200px, 0.9fr)", render: (tier) => , }, { key: "reward", label: "升级礼物", width: "minmax(200px, 1fr)", render: (tier) => , }, { key: "status", label: "状态", width: "minmax(116px, 0.55fr)", render: (tier) => , }, { key: "updatedAt", label: "更新时间", width: "minmax(170px, 0.75fr)", render: (tier) => formatMillis(tier.updatedAtMs || tier.createdAtMs), }, { key: "actions", label: "操作", width: "minmax(76px, 0.35fr)", render: (tier) => , }, ]; } function RuleIdentity({ rule }) { return (
Lv.{rule.level}
{rule.name || `${levelTrackLabels[rule.track]} ${rule.level}级`} {levelTrackLabels[rule.track] || rule.track}
); } function TierIdentity({ tier }) { return (
{tier.minLevel}-{tier.maxLevel}
{tier.name || `${tier.minLevel}-${tier.maxLevel}级`} {levelTrackLabels[tier.track] || tier.track}
); } function RuleStatusSwitch({ page, rule }) { const checked = rule.status === "active"; return (
page.toggleRule(rule, event.target.checked)} /> {levelStatusLabel(rule.status)}
); } function TierStatusSwitch({ page, tier }) { const checked = tier.status === "active"; return (
page.toggleTier(tier, event.target.checked)} /> {levelStatusLabel(tier.status)}
); } function RuleRowActions({ page, rule }) { return ( page.openRuleEdit(rule)} > ); } function TierRowActions({ page, tier }) { return ( page.openTierEdit(tier)} > ); } function RuleFormDialog({ page }) { const open = page.activeAction === "rule-create" || page.activeAction === "rule-edit"; const editing = page.activeAction === "rule-edit"; const form = page.ruleForm; return ( page.setRuleForm((current) => ({ ...current, avatarFrameResourceId: "", longBadgeResourceId: "", shortBadgeResourceId: "", track: event.target.value, })) } > {levelTrackOptions.map(([value, label]) => ( {label} ))} page.setRuleForm((current) => ({ ...current, longBadgeResourceId: value })) } /> page.setRuleForm((current) => ({ ...current, avatarFrameResourceId: value })) } /> page.setRuleForm((current) => ({ ...current, shortBadgeResourceId: value })) } /> page.setRuleForm((current) => ({ ...current, rewardResourceGroupId: value })) } /> page.setRuleForm((current) => ({ ...current, status: checked ? "active" : "disabled" })) } /> ); } function TierFormDialog({ page }) { const open = page.activeAction === "tier-create" || page.activeAction === "tier-edit"; const editing = page.activeAction === "tier-edit"; const form = page.tierForm; return ( page.setTierForm((current) => ({ ...current, displayAvatarFrameResourceId: "", displayBadgeResourceId: "", track: event.target.value, })) } > {levelTrackOptions.map(([value, label]) => ( {label} ))} page.setTierForm((current) => ({ ...current, displayAvatarFrameResourceId: value })) } /> page.setTierForm((current) => ({ ...current, displayBadgeResourceId: value })) } /> page.setTierForm((current) => ({ ...current, rewardResourceGroupId: value })) } /> page.setTierForm((current) => ({ ...current, status: checked ? "active" : "disabled" })) } /> ); } function TrackFormDialog({ page }) { const form = page.trackForm; return ( page.setTrackForm((current) => ({ ...current, status: checked ? "active" : "disabled" })) } /> ); } function ResourceLabel({ id, resources }) { if (!id) { return 未配置; } const resource = resources.find((item) => Number(item.resourceId) === Number(id)); const preview = resourceImageURL(resource); return (
{preview ? : }
{resource?.name || resource?.resourceCode || `资源 #${id}`} {resource ? resourceMetaLabel(resource, id) : `ID ${id}`}
); } function ResourceGroupLabel({ groups, id }) { if (!id) { return 未配置; } const group = groups.find((item) => Number(item.groupId) === Number(id)); return (
{group?.name || group?.groupCode || `资源组 #${id}`} ID {id}
); } function changeField(setter, key) { return (event) => { const value = event.target.value; setter((current) => ({ ...current, [key]: value })); }; } function resourceMetaLabel(resource, id) { if (resource.resourceType === "avatar_frame") { const avatarFrameKind = resource.avatarFrameKind || avatarFrameKindFromMetadata(resource.metadataJson); const levelTrack = resource.levelTrack || levelTrackFromMetadata(resource.metadataJson); if (avatarFrameKind === "level" && levelTrack) { return `ID ${id} · 等级头像框 · ${levelTrackLabels[levelTrack] || levelTrack}`; } } const badgeForm = resource.badgeForm || badgeFormFromMetadata(resource.metadataJson); const badgeKind = resource.badgeKind || badgeKindFromMetadata(resource.metadataJson); const levelTrack = resource.levelTrack || levelTrackFromMetadata(resource.metadataJson); if (resource.resourceType === "badge" && badgeKind === "level" && levelTrack) { const badgeFormLabel = badgeForm === "tile" ? "短徽章" : "长徽章"; return `ID ${id} · ${badgeFormLabel} · ${levelTrackLabels[levelTrack] || levelTrack}`; } return `ID ${id}`; } function resourceImageURL(resource) { return imageURL(resource?.previewUrl) || imageURL(resource?.assetUrl) || imageURL(resource?.animationUrl); } function imageURL(value) { const url = String(value || "").trim(); if (!url) { return ""; } return /\.(avif|gif|jpe?g|png|webp)(\?|#|$)/i.test(url) ? url : ""; } function badgeFormFromMetadata(metadataJson) { if (!metadataJson) { return ""; } try { const metadata = JSON.parse(metadataJson); const badgeForm = String(metadata?.badge_form || "") .trim() .toLowerCase(); return badgeForm === "strip" || badgeForm === "tile" ? badgeForm : ""; } catch { return ""; } } function badgeKindFromMetadata(metadataJson) { if (!metadataJson) { return ""; } try { const metadata = JSON.parse(metadataJson); return String(metadata?.badge_kind || "") .trim() .toLowerCase() === "level" ? "level" : "normal"; } catch { return ""; } } function avatarFrameKindFromMetadata(metadataJson) { if (!metadataJson) { return ""; } try { const metadata = JSON.parse(metadataJson); const avatarFrameKind = String(metadata?.avatar_frame_kind || "") .trim() .toLowerCase(); return avatarFrameKind === "level" || avatarFrameKind === "normal" ? avatarFrameKind : ""; } catch { return ""; } } function levelTrackFromMetadata(metadataJson) { if (!metadataJson) { return ""; } try { const metadata = JSON.parse(metadataJson); const levelTrack = String(metadata?.level_track || "") .trim() .toLowerCase(); return levelTrack === "wealth" || levelTrack === "game" || levelTrack === "charm" ? levelTrack : ""; } catch { return ""; } } function levelStatusLabel(status) { return levelStatusLabels[status] || status || "-"; } function formatNumber(value) { return Number(value || 0).toLocaleString("zh-CN"); }