import Add from "@mui/icons-material/Add";
import CodeOutlined from "@mui/icons-material/CodeOutlined";
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
import EditOutlined from "@mui/icons-material/EditOutlined";
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
import SportsEsportsOutlined from "@mui/icons-material/SportsEsportsOutlined";
import SyncOutlined from "@mui/icons-material/SyncOutlined";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
import Checkbox from "@mui/material/Checkbox";
import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField";
import { AdminFormDialog, AdminFormFieldGrid, AdminFormSection } 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 { JsonEditorField } from "@/shared/ui/JsonEditorField.jsx";
import {
AdminActionIconButton,
AdminListBody,
AdminListPage,
AdminListToolbar,
AdminRowActions,
} from "@/shared/ui/AdminListLayout.jsx";
import { createOptionsColumnFilter } from "@/shared/ui/tableFilters.js";
import { UploadField } from "@/shared/ui/UploadField.jsx";
import { formatMillis } from "@/shared/utils/time.js";
import { useGamesPage } from "@/features/games/hooks/useGamesPage.js";
import styles from "@/features/games/games.module.css";
const statusOptions = [
["", "全部状态"],
["active", "Active"],
["maintenance", "Maintenance"],
["disabled", "Disabled"],
];
const orientationOptions = [
["portrait", "Portrait"],
["landscape", "Landscape"],
];
const launchModeOptions = [
["full_screen", "全屏"],
["half_screen", "半屏"],
["three_quarter_screen", "3/4屏"],
];
// 这里的值必须和 game-service 的 adapter_type 白名单一致;新增厂商先后端登记,再放到后台可选项。
const adapterTypeOptions = [
["yomi_v4", "小游 Yomi V4"],
["leadercc_v1", "灵仙 LeaderCC V1"],
["zeeone_v1", "ZeeOne V1"],
["baishun_v1", "百顺 BAISHUN V1"],
["vivagames_v1", "VIVAGAMES V1"],
["reyou_v1", "热游 Reyou V1"],
];
const baseColumns = [
{
key: "game",
label: "游戏",
width: "minmax(290px, 1.5fr)",
render: (game) => ,
},
{
key: "platform",
label: "平台",
width: "minmax(160px, 0.75fr)",
render: (game) => (
{game.platformCode}
{game.providerGameId}
),
},
{
key: "launch",
label: "启动",
width: "minmax(150px, 0.75fr)",
render: (game) => (
{launchModeLabel(game.launchMode)}
{game.orientation} · 安全高 {formatNumber(game.safeHeight)}
),
},
{
key: "coin",
label: "最低金币",
width: "minmax(116px, 0.55fr)",
render: (game) => formatNumber(game.minCoin),
},
{
key: "status",
label: "状态",
width: "minmax(120px, 0.6fr)",
},
{
key: "updatedAt",
label: "更新时间",
width: "minmax(170px, 0.9fr)",
render: (game) => formatMillis(game.updatedAtMs || game.createdAtMs),
},
{
key: "actions",
label: "操作",
width: "minmax(112px, 0.5fr)",
},
];
export function GameListPage() {
const page = useGamesPage();
const items = page.data.items || [];
const platformFilterOptions = [["", "全部平台"], ...page.platformOptions];
const columns = baseColumns.map((column) => {
if (column.key === "platform") {
return {
...column,
filter: createOptionsColumnFilter({
options: platformFilterOptions,
placeholder: "搜索平台",
value: page.platformCode,
onChange: page.setPlatformCode,
}),
};
}
if (column.key === "status") {
return {
...column,
filter: createOptionsColumnFilter({
options: statusOptions,
placeholder: "搜索状态",
value: page.status,
onChange: page.setStatus,
}),
render: (game) => ,
};
}
if (column.key === "actions") {
return {
...column,
render: (game) => ,
};
}
return column;
});
return (
{page.abilities.canUpdate ? (
) : null}
{page.abilities.canUpdate ? (
) : null}
{page.abilities.canUpdate ? (
) : null}
{page.abilities.canCreate ? (
) : null}
>
}
/>
game.gameId}
/>
);
}
function GameIdentity({ game }) {
return (
{game.iconUrl ? (

) : (
)}
{game.gameName || game.gameId}
{game.gameId}
);
}
function GameStatusSwitch({ page, game }) {
const disabled = !page.abilities.canStatus || page.loadingAction === `status-${game.gameId}`;
return (
page.changeGameStatus(game, event.target.checked)}
/>
);
}
function GameRowActions({ page, game }) {
return (
page.openEditGame(game)}
>
page.deleteGame(game)}
>
);
}
function GameFormDialog({ form, loading, mode, open, page, onClose, onSubmit }) {
const disabled = mode === "edit" ? !page.abilities.canUpdate : !page.abilities.canCreate;
return (
page.setGameForm({ ...form, gameId: event.target.value })}
/>
0}
value={form.platformCode}
onChange={(event) => page.setGameForm({ ...form, platformCode: event.target.value })}
>
{page.platformOptions.map(([value, label]) => (
))}
page.setGameForm({ ...form, providerGameId: event.target.value })}
/>
page.setGameForm({ ...form, gameName: event.target.value })}
/>
page.setGameForm({ ...form, category: event.target.value })}
/>
page.setGameForm({ ...form, launchMode: event.target.value })}
>
{launchModeOptions.map(([value, label]) => (
))}
page.setGameForm({ ...form, orientation: event.target.value })}
>
{orientationOptions.map(([value, label]) => (
))}
page.setGameForm({ ...form, safeHeight: event.target.value })}
/>
page.setGameForm({ ...form, gameUrl: event.target.value })}
/>
page.setGameForm({ ...form, minCoin: event.target.value })}
/>
page.setGameForm({ ...form, sortOrder: event.target.value })}
/>
page.setGameForm({
...form,
status: checked ? "active" : form.status === "disabled" ? "disabled" : "maintenance",
})
}
/>
page.setGameForm({ ...form, iconUrl })}
/>
page.setGameForm({ ...form, coverUrl })}
/>
page.setGameForm({ ...form, tagsText: event.target.value })}
/>
);
}
function BridgeScriptDialog({ form, loading, open, page, onClose, onSubmit }) {
const disabled = !page.abilities.canUpdate;
const submitDisabled = disabled || loading || !form.bridgeScriptUrl.trim() || !form.bridgeScriptVersion.trim();
return (
page.setBridgeScriptForm({ ...form, bridgeScriptVersion: event.target.value })
}
/>
page.setBridgeScriptForm({ ...form, bridgeScriptUrl: event.target.value })}
/>
);
}
function PlatformFormDialog({ form, loading, mode, open, page, onClose, onSubmit }) {
const isLeaderCC = form.adapterType === "leadercc_v1";
return (
page.setPlatformForm({ ...form, platformCode: event.target.value })}
/>
page.setPlatformForm({ ...form, platformName: event.target.value })}
/>
page.setPlatformForm({
...form,
status: checked ? "active" : form.status === "disabled" ? "disabled" : "maintenance",
})
}
/>
page.setPlatformForm({ ...form, sortOrder: event.target.value })}
/>
{/* 适配器决定服务端启动参数、签名/解密和错误码映射。 */}
page.setPlatformForm({ ...form, adapterType: event.target.value })}
>
{adapterTypeOptions.map(([value, label]) => (
))}
{!isLeaderCC ? (
page.setPlatformForm({ ...form, apiBaseUrl: event.target.value })}
/>
) : null}
{/* 后台直接回显当前厂商 key;清空保存时后端保持旧密钥,输入新值才轮换。 */}
page.setPlatformForm({ ...form, callbackSecret: event.target.value })}
/>
{!isLeaderCC ? (
page.setPlatformForm({ ...form, callbackIpWhitelistText: event.target.value })
}
/>
) : null}
{/* 厂商差异字段放 JSON:如 uid_mode、default_lang、token_ttl_seconds、game_level_uid。 */}
page.setPlatformForm({ ...form, adapterConfigJson })}
/>
);
}
function PlatformManageDialog({ open, page, onClose }) {
const platforms = page.platformData.items || [];
return (
{
event.preventDefault();
onClose();
}}
>
{platforms.map((platform) => (
{platform.platformName || platform.platformCode}
{platform.platformCode} / {platform.adapterType || "未配置"}
{platform.apiBaseUrl ? {platform.apiBaseUrl} : null}
密钥:{platform.callbackSecret || "未配置"}
{platform.callbackSecretSet ? "密钥已配置" : "未配置密钥"}
page.openEditPlatform(platform)}
>
page.fetchPlatformGames(platform)}
>
))}
);
}
function ProviderGameListDialog({ open, page, onClose }) {
const games = page.syncCandidates || [];
const selected = new Set(page.selectedSyncGameIds || []);
return (
{page.allSyncGamesSelected ? "取消全选" : "全选"}
}
>
{games.length > 0 ? (
games.map((game) => (
page.toggleSyncGame(game.providerGameId)}
/>
{game.providerGameId}
默认下架
{game.launchUrl ? (
{game.launchUrl}
) : null}
page.importSingleSyncGame(game)}
>
))
) : (
暂无可添加游戏
)}
);
}
function SwitchOnlyField({ checked, checkedLabel, disabled, label, onChange, uncheckedLabel }) {
return (
onChange(event.target.checked, event)}
/>
);
}
function formatNumber(value) {
return Number(value || 0).toLocaleString();
}
function launchModeLabel(value) {
return launchModeOptions.find(([mode]) => mode === value)?.[1] || "全屏";
}