714 lines
30 KiB
JavaScript
714 lines
30 KiB
JavaScript
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) => <GameIdentity game={game} />,
|
||
},
|
||
{
|
||
key: "platform",
|
||
label: "平台",
|
||
width: "minmax(160px, 0.75fr)",
|
||
render: (game) => (
|
||
<div className={styles.stack}>
|
||
<span>{game.platformCode}</span>
|
||
<span className={styles.meta}>{game.providerGameId}</span>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: "launch",
|
||
label: "启动",
|
||
width: "minmax(150px, 0.75fr)",
|
||
render: (game) => (
|
||
<div className={styles.stack}>
|
||
<span>{launchModeLabel(game.launchMode)}</span>
|
||
<span className={styles.meta}>
|
||
{game.orientation} · 安全高 {formatNumber(game.safeHeight)}
|
||
</span>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
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) => <GameStatusSwitch game={game} page={page} />,
|
||
};
|
||
}
|
||
if (column.key === "actions") {
|
||
return {
|
||
...column,
|
||
render: (game) => <GameRowActions game={game} page={page} />,
|
||
};
|
||
}
|
||
return column;
|
||
});
|
||
|
||
return (
|
||
<AdminListPage>
|
||
<AdminListToolbar
|
||
actions={
|
||
<>
|
||
{page.abilities.canUpdate ? (
|
||
<AdminActionIconButton label="JS 桥接" onClick={page.openBridgeScript}>
|
||
<CodeOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
) : null}
|
||
{page.abilities.canUpdate ? (
|
||
<AdminActionIconButton label="平台配置" onClick={page.openManagePlatforms}>
|
||
<SettingsOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
) : null}
|
||
{page.abilities.canUpdate ? (
|
||
<AdminActionIconButton label="添加平台" onClick={page.openCreatePlatform}>
|
||
<SportsEsportsOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
) : null}
|
||
{page.abilities.canCreate ? (
|
||
<AdminActionIconButton label="添加游戏" primary onClick={page.openCreateGame}>
|
||
<Add fontSize="small" />
|
||
</AdminActionIconButton>
|
||
) : null}
|
||
</>
|
||
}
|
||
/>
|
||
<DataState error={page.error} loading={page.loading} onRetry={page.reload}>
|
||
<AdminListBody>
|
||
<DataTable
|
||
columns={columns}
|
||
infiniteScroll={{
|
||
doneLabel: "",
|
||
hasMore: Boolean(page.data.nextCursor),
|
||
loading: page.loadingMore,
|
||
loadingLabel: "加载中...",
|
||
onLoadMore: page.loadNextPage,
|
||
}}
|
||
items={items}
|
||
minWidth="1240px"
|
||
pagination={{
|
||
hasNextPage: Boolean(page.data.nextCursor),
|
||
itemCount: items.length,
|
||
page: page.page,
|
||
pageSize: page.pageSize,
|
||
total: page.data.total,
|
||
totalPages: page.data.totalPages,
|
||
}}
|
||
rowKey={(game) => game.gameId}
|
||
/>
|
||
</AdminListBody>
|
||
</DataState>
|
||
<GameFormDialog
|
||
form={page.gameForm}
|
||
loading={page.loadingAction === page.activeAction}
|
||
mode={page.activeAction === "edit-game" ? "edit" : "create"}
|
||
open={page.activeAction === "create-game" || page.activeAction === "edit-game"}
|
||
page={page}
|
||
onClose={page.closeAction}
|
||
onSubmit={page.submitGame}
|
||
/>
|
||
<PlatformFormDialog
|
||
form={page.platformForm}
|
||
loading={page.loadingAction === page.activeAction}
|
||
mode={page.activeAction === "edit-platform" ? "edit" : "create"}
|
||
open={page.activeAction === "create-platform" || page.activeAction === "edit-platform"}
|
||
page={page}
|
||
onClose={page.closeAction}
|
||
onSubmit={page.submitPlatform}
|
||
/>
|
||
<BridgeScriptDialog
|
||
form={page.bridgeScriptForm}
|
||
loading={page.loadingAction === "bridge-script"}
|
||
open={page.activeAction === "bridge-script"}
|
||
page={page}
|
||
onClose={page.closeAction}
|
||
onSubmit={page.submitBridgeScript}
|
||
/>
|
||
<PlatformManageDialog
|
||
open={page.activeAction === "manage-platforms"}
|
||
page={page}
|
||
onClose={page.closeAction}
|
||
/>
|
||
<ProviderGameListDialog open={page.activeAction === "sync-games"} page={page} onClose={page.closeAction} />
|
||
</AdminListPage>
|
||
);
|
||
}
|
||
|
||
function GameIdentity({ game }) {
|
||
return (
|
||
<div className={styles.identity}>
|
||
{game.iconUrl ? (
|
||
<img alt="" className={styles.icon} src={game.iconUrl} />
|
||
) : (
|
||
<span className={styles.icon} />
|
||
)}
|
||
<div className={styles.identityText}>
|
||
<span className={styles.name}>{game.gameName || game.gameId}</span>
|
||
<span className={styles.meta}>{game.gameId}</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function GameStatusSwitch({ page, game }) {
|
||
const disabled = !page.abilities.canStatus || page.loadingAction === `status-${game.gameId}`;
|
||
return (
|
||
<AdminSwitch
|
||
checked={game.status === "active"}
|
||
checkedLabel="启用"
|
||
disabled={disabled}
|
||
label={`${game.gameName || game.gameId} 状态`}
|
||
size="small"
|
||
uncheckedLabel={game.status === "disabled" ? "下架" : "维护"}
|
||
onChange={(event) => page.changeGameStatus(game, event.target.checked)}
|
||
/>
|
||
);
|
||
}
|
||
|
||
function GameRowActions({ page, game }) {
|
||
return (
|
||
<AdminRowActions>
|
||
<AdminActionIconButton
|
||
disabled={!page.abilities.canUpdate}
|
||
label="编辑游戏"
|
||
onClick={() => page.openEditGame(game)}
|
||
>
|
||
<EditOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
<AdminActionIconButton
|
||
disabled={!page.abilities.canDelete || page.loadingAction === `delete-${game.gameId}`}
|
||
label="删除游戏"
|
||
onClick={() => page.deleteGame(game)}
|
||
>
|
||
<DeleteOutlineOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
</AdminRowActions>
|
||
);
|
||
}
|
||
|
||
function GameFormDialog({ form, loading, mode, open, page, onClose, onSubmit }) {
|
||
const disabled = mode === "edit" ? !page.abilities.canUpdate : !page.abilities.canCreate;
|
||
return (
|
||
<AdminFormDialog
|
||
disabled={disabled}
|
||
loading={loading}
|
||
open={open}
|
||
size="wide"
|
||
submitLabel={mode === "edit" ? "保存游戏" : "创建游戏"}
|
||
title={mode === "edit" ? "编辑游戏" : "添加游戏"}
|
||
onClose={onClose}
|
||
onSubmit={onSubmit}
|
||
>
|
||
<AdminFormSection title="基础信息">
|
||
<AdminFormFieldGrid columns="repeat(2, minmax(0, 1fr))">
|
||
<TextField
|
||
disabled={mode === "edit"}
|
||
label="游戏 ID"
|
||
required
|
||
value={form.gameId}
|
||
onChange={(event) => page.setGameForm({ ...form, gameId: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="平台"
|
||
required
|
||
select={page.platformOptions.length > 0}
|
||
value={form.platformCode}
|
||
onChange={(event) => page.setGameForm({ ...form, platformCode: event.target.value })}
|
||
>
|
||
{page.platformOptions.map(([value, label]) => (
|
||
<MenuItem key={value} value={value}>
|
||
{label || value}
|
||
</MenuItem>
|
||
))}
|
||
</TextField>
|
||
<TextField
|
||
label="平台游戏 ID"
|
||
required
|
||
value={form.providerGameId}
|
||
onChange={(event) => page.setGameForm({ ...form, providerGameId: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="游戏名称"
|
||
required
|
||
value={form.gameName}
|
||
onChange={(event) => page.setGameForm({ ...form, gameName: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="类目"
|
||
value={form.category}
|
||
onChange={(event) => page.setGameForm({ ...form, category: event.target.value })}
|
||
/>
|
||
</AdminFormFieldGrid>
|
||
</AdminFormSection>
|
||
<AdminFormSection title="启动与状态">
|
||
<AdminFormFieldGrid columns="repeat(2, minmax(0, 1fr))">
|
||
<TextField
|
||
label="启动模式"
|
||
select
|
||
value={form.launchMode}
|
||
onChange={(event) => page.setGameForm({ ...form, launchMode: event.target.value })}
|
||
>
|
||
{launchModeOptions.map(([value, label]) => (
|
||
<MenuItem key={value} value={value}>
|
||
{label}
|
||
</MenuItem>
|
||
))}
|
||
</TextField>
|
||
<TextField
|
||
label="方向"
|
||
select
|
||
value={form.orientation}
|
||
onChange={(event) => page.setGameForm({ ...form, orientation: event.target.value })}
|
||
>
|
||
{orientationOptions.map(([value, label]) => (
|
||
<MenuItem key={value} value={value}>
|
||
{label}
|
||
</MenuItem>
|
||
))}
|
||
</TextField>
|
||
<TextField
|
||
label="安全高"
|
||
type="number"
|
||
value={form.safeHeight}
|
||
onChange={(event) => page.setGameForm({ ...form, safeHeight: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="游戏 URL"
|
||
className={styles.fullField}
|
||
minRows={2}
|
||
multiline
|
||
placeholder="填写当前游戏的 H5 测试/正式 URL"
|
||
value={form.gameUrl}
|
||
onChange={(event) => page.setGameForm({ ...form, gameUrl: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="最低金币"
|
||
type="number"
|
||
value={form.minCoin}
|
||
onChange={(event) => page.setGameForm({ ...form, minCoin: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="排序"
|
||
type="number"
|
||
value={form.sortOrder}
|
||
onChange={(event) => page.setGameForm({ ...form, sortOrder: event.target.value })}
|
||
/>
|
||
<SwitchOnlyField
|
||
checked={form.status === "active"}
|
||
checkedLabel="启用"
|
||
disabled={disabled}
|
||
label="游戏启用状态"
|
||
uncheckedLabel="维护"
|
||
onChange={(checked) =>
|
||
page.setGameForm({
|
||
...form,
|
||
status: checked ? "active" : form.status === "disabled" ? "disabled" : "maintenance",
|
||
})
|
||
}
|
||
/>
|
||
</AdminFormFieldGrid>
|
||
</AdminFormSection>
|
||
<AdminFormSection title="素材与标签">
|
||
<AdminFormFieldGrid columns="repeat(2, minmax(0, 1fr))">
|
||
<UploadField
|
||
disabled={disabled || !page.abilities.canUpload}
|
||
label="图标"
|
||
value={form.iconUrl}
|
||
onChange={(iconUrl) => page.setGameForm({ ...form, iconUrl })}
|
||
/>
|
||
<UploadField
|
||
disabled={disabled || !page.abilities.canUpload}
|
||
label="封面"
|
||
value={form.coverUrl}
|
||
onChange={(coverUrl) => page.setGameForm({ ...form, coverUrl })}
|
||
/>
|
||
<TextField
|
||
className={styles.fullField}
|
||
label="标签"
|
||
value={form.tagsText}
|
||
onChange={(event) => page.setGameForm({ ...form, tagsText: event.target.value })}
|
||
/>
|
||
</AdminFormFieldGrid>
|
||
</AdminFormSection>
|
||
</AdminFormDialog>
|
||
);
|
||
}
|
||
|
||
function BridgeScriptDialog({ form, loading, open, page, onClose, onSubmit }) {
|
||
const disabled = !page.abilities.canUpdate;
|
||
const submitDisabled = disabled || loading || !form.bridgeScriptUrl.trim() || !form.bridgeScriptVersion.trim();
|
||
return (
|
||
<AdminFormDialog
|
||
disabled={disabled}
|
||
loading={loading}
|
||
open={open}
|
||
submitDisabled={submitDisabled}
|
||
submitLabel="保存桥接"
|
||
title="JS 桥接配置"
|
||
onClose={onClose}
|
||
onSubmit={onSubmit}
|
||
>
|
||
<AdminFormSection title="通用桥接脚本">
|
||
<AdminFormFieldGrid columns="repeat(2, minmax(0, 1fr))">
|
||
<TextField
|
||
label="bridge_script_version"
|
||
required
|
||
value={form.bridgeScriptVersion}
|
||
onChange={(event) =>
|
||
page.setBridgeScriptForm({ ...form, bridgeScriptVersion: event.target.value })
|
||
}
|
||
/>
|
||
<TextField
|
||
className={styles.fullField}
|
||
label="bridge_script_url"
|
||
minRows={2}
|
||
multiline
|
||
required
|
||
value={form.bridgeScriptUrl}
|
||
onChange={(event) => page.setBridgeScriptForm({ ...form, bridgeScriptUrl: event.target.value })}
|
||
/>
|
||
</AdminFormFieldGrid>
|
||
</AdminFormSection>
|
||
</AdminFormDialog>
|
||
);
|
||
}
|
||
|
||
function PlatformFormDialog({ form, loading, mode, open, page, onClose, onSubmit }) {
|
||
const isLeaderCC = form.adapterType === "leadercc_v1";
|
||
return (
|
||
<AdminFormDialog
|
||
disabled={!page.abilities.canUpdate}
|
||
loading={loading}
|
||
open={open}
|
||
submitLabel={mode === "edit" ? "保存平台" : "创建平台"}
|
||
title={mode === "edit" ? "编辑平台" : "添加平台"}
|
||
onClose={onClose}
|
||
onSubmit={onSubmit}
|
||
>
|
||
<AdminFormSection title="平台信息">
|
||
<AdminFormFieldGrid>
|
||
<TextField
|
||
disabled={mode === "edit"}
|
||
label="平台 Code"
|
||
required
|
||
value={form.platformCode}
|
||
onChange={(event) => page.setPlatformForm({ ...form, platformCode: event.target.value })}
|
||
/>
|
||
<TextField
|
||
label="平台名称"
|
||
required
|
||
value={form.platformName}
|
||
onChange={(event) => page.setPlatformForm({ ...form, platformName: event.target.value })}
|
||
/>
|
||
<SwitchOnlyField
|
||
checked={form.status === "active"}
|
||
checkedLabel="启用"
|
||
disabled={!page.abilities.canUpdate}
|
||
label="游戏平台启用状态"
|
||
uncheckedLabel="维护"
|
||
onChange={(checked) =>
|
||
page.setPlatformForm({
|
||
...form,
|
||
status: checked ? "active" : form.status === "disabled" ? "disabled" : "maintenance",
|
||
})
|
||
}
|
||
/>
|
||
<TextField
|
||
label="排序"
|
||
type="number"
|
||
value={form.sortOrder}
|
||
onChange={(event) => page.setPlatformForm({ ...form, sortOrder: event.target.value })}
|
||
/>
|
||
</AdminFormFieldGrid>
|
||
</AdminFormSection>
|
||
<AdminFormSection title="接入配置">
|
||
<AdminFormFieldGrid columns="repeat(2, minmax(0, 1fr))">
|
||
{/* 适配器决定服务端启动参数、签名/解密和错误码映射。 */}
|
||
<TextField
|
||
label="适配器"
|
||
select
|
||
value={form.adapterType}
|
||
onChange={(event) => page.setPlatformForm({ ...form, adapterType: event.target.value })}
|
||
>
|
||
{adapterTypeOptions.map(([value, label]) => (
|
||
<MenuItem key={value} value={value}>
|
||
{label}
|
||
</MenuItem>
|
||
))}
|
||
</TextField>
|
||
{!isLeaderCC ? (
|
||
<TextField
|
||
label="H5/Auth/API URL"
|
||
required={form.adapterType === "yomi_v4" || form.adapterType === "baishun_v1"}
|
||
value={form.apiBaseUrl}
|
||
onChange={(event) => page.setPlatformForm({ ...form, apiBaseUrl: event.target.value })}
|
||
/>
|
||
) : null}
|
||
{/* 后台直接回显当前厂商 key;清空保存时后端保持旧密钥,输入新值才轮换。 */}
|
||
<TextField
|
||
label="回调密钥"
|
||
value={form.callbackSecret}
|
||
onChange={(event) => page.setPlatformForm({ ...form, callbackSecret: event.target.value })}
|
||
/>
|
||
{!isLeaderCC ? (
|
||
<TextField
|
||
label="IP 白名单"
|
||
minRows={3}
|
||
multiline
|
||
value={form.callbackIpWhitelistText}
|
||
onChange={(event) =>
|
||
page.setPlatformForm({ ...form, callbackIpWhitelistText: event.target.value })
|
||
}
|
||
/>
|
||
) : null}
|
||
{/* 厂商差异字段放 JSON:如 uid_mode、default_lang、token_ttl_seconds、game_level_uid。 */}
|
||
<JsonEditorField
|
||
className={styles.fullField}
|
||
label="适配器配置 JSON"
|
||
minRows={5}
|
||
value={form.adapterConfigJson}
|
||
onChange={(adapterConfigJson) => page.setPlatformForm({ ...form, adapterConfigJson })}
|
||
/>
|
||
</AdminFormFieldGrid>
|
||
</AdminFormSection>
|
||
</AdminFormDialog>
|
||
);
|
||
}
|
||
|
||
function PlatformManageDialog({ open, page, onClose }) {
|
||
const platforms = page.platformData.items || [];
|
||
return (
|
||
<AdminFormDialog
|
||
disabled={false}
|
||
loading={false}
|
||
open={open}
|
||
submitLabel="关闭"
|
||
title="平台配置"
|
||
onClose={onClose}
|
||
onSubmit={(event) => {
|
||
event.preventDefault();
|
||
onClose();
|
||
}}
|
||
>
|
||
<div className={styles.platformList}>
|
||
{platforms.map((platform) => (
|
||
<div className={styles.platformRow} key={platform.platformCode}>
|
||
<div className={styles.stack}>
|
||
<span className={styles.name}>{platform.platformName || platform.platformCode}</span>
|
||
<span className={styles.meta}>
|
||
{platform.platformCode} / {platform.adapterType || "未配置"}
|
||
</span>
|
||
{platform.apiBaseUrl ? <span className={styles.meta}>{platform.apiBaseUrl}</span> : null}
|
||
<span className={`${styles.meta} ${styles.secretMeta}`}>
|
||
密钥:{platform.callbackSecret || "未配置"}
|
||
</span>
|
||
</div>
|
||
<div className={styles.platformActions}>
|
||
<span className={styles.meta}>
|
||
{platform.callbackSecretSet ? "密钥已配置" : "未配置密钥"}
|
||
</span>
|
||
<AdminActionIconButton
|
||
disabled={!page.abilities.canUpdate}
|
||
label="编辑平台"
|
||
onClick={() => page.openEditPlatform(platform)}
|
||
>
|
||
<EditOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
<AdminActionIconButton
|
||
disabled={
|
||
!page.abilities.canUpdate ||
|
||
page.loadingAction === `fetch-games-${platform.platformCode}`
|
||
}
|
||
label="获取游戏列表"
|
||
onClick={() => page.fetchPlatformGames(platform)}
|
||
>
|
||
<SyncOutlined fontSize="small" />
|
||
</AdminActionIconButton>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</AdminFormDialog>
|
||
);
|
||
}
|
||
|
||
function ProviderGameListDialog({ open, page, onClose }) {
|
||
const games = page.syncCandidates || [];
|
||
const selected = new Set(page.selectedSyncGameIds || []);
|
||
return (
|
||
<AdminFormDialog
|
||
loading={page.loadingAction === "import-sync-games"}
|
||
open={open}
|
||
size="wide"
|
||
submitDisabled={selected.size === 0 || page.loadingAction === "import-sync-games"}
|
||
submitLabel={`添加选中 (${selected.size})`}
|
||
title={`厂商游戏列表 ${page.syncPlatform?.platformName || page.syncPlatform?.platformCode || ""}`}
|
||
onClose={onClose}
|
||
onSubmit={page.importSelectedSyncGames}
|
||
>
|
||
<AdminFormSection
|
||
title="选择要添加的游戏"
|
||
actions={
|
||
<Button disabled={games.length === 0} onClick={page.toggleAllSyncGames}>
|
||
{page.allSyncGamesSelected ? "取消全选" : "全选"}
|
||
</Button>
|
||
}
|
||
>
|
||
<div className={styles.syncGameList}>
|
||
{games.length > 0 ? (
|
||
games.map((game) => (
|
||
<div className={styles.syncGameRow} key={game.providerGameId || game.gameId}>
|
||
<Checkbox
|
||
checked={selected.has(game.providerGameId)}
|
||
inputProps={{ "aria-label": `${game.gameName || game.gameId} 选择状态` }}
|
||
onChange={() => page.toggleSyncGame(game.providerGameId)}
|
||
/>
|
||
<GameIdentity game={game} />
|
||
<div className={styles.syncGameMeta}>
|
||
<span className={styles.meta}>{game.providerGameId}</span>
|
||
<span className={styles.meta}>默认下架</span>
|
||
{game.launchUrl ? (
|
||
<span className={`${styles.meta} ${styles.syncGameURL}`}>{game.launchUrl}</span>
|
||
) : null}
|
||
</div>
|
||
<AdminActionIconButton
|
||
disabled={page.loadingAction === `import-sync-game-${game.providerGameId}`}
|
||
label="添加单个游戏"
|
||
onClick={() => page.importSingleSyncGame(game)}
|
||
>
|
||
<Add fontSize="small" />
|
||
</AdminActionIconButton>
|
||
</div>
|
||
))
|
||
) : (
|
||
<span className={styles.meta}>暂无可添加游戏</span>
|
||
)}
|
||
</div>
|
||
</AdminFormSection>
|
||
</AdminFormDialog>
|
||
);
|
||
}
|
||
|
||
function SwitchOnlyField({ checked, checkedLabel, disabled, label, onChange, uncheckedLabel }) {
|
||
return (
|
||
<span className={styles.switchOnlyField}>
|
||
<AdminSwitch
|
||
checked={checked}
|
||
checkedLabel={checkedLabel}
|
||
disabled={disabled}
|
||
label={label}
|
||
uncheckedLabel={uncheckedLabel}
|
||
onChange={(event) => onChange(event.target.checked, event)}
|
||
/>
|
||
</span>
|
||
);
|
||
}
|
||
|
||
function formatNumber(value) {
|
||
return Number(value || 0).toLocaleString();
|
||
}
|
||
|
||
function launchModeLabel(value) {
|
||
return launchModeOptions.find(([mode]) => mode === value)?.[1] || "全屏";
|
||
}
|