304 lines
9.1 KiB
JavaScript
304 lines
9.1 KiB
JavaScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import {
|
|
listGameCatalog,
|
|
listGamePlatforms,
|
|
setGameStatus,
|
|
upsertGameCatalog,
|
|
upsertGamePlatform,
|
|
} from "@/features/games/api";
|
|
import { useGameAbilities } from "@/features/games/permissions.js";
|
|
import { useAdminQuery } from "@/shared/hooks/useAdminQuery.js";
|
|
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
|
|
|
const defaultGameForm = {
|
|
gameId: "",
|
|
platformCode: "",
|
|
providerGameId: "",
|
|
gameName: "",
|
|
category: "casual",
|
|
iconUrl: "",
|
|
coverUrl: "",
|
|
launchMode: "h5_popup",
|
|
orientation: "portrait",
|
|
minCoin: 0,
|
|
status: "active",
|
|
sortOrder: 0,
|
|
tagsText: "",
|
|
};
|
|
|
|
const defaultPlatformForm = {
|
|
platformCode: "",
|
|
platformName: "",
|
|
status: "active",
|
|
apiBaseUrl: "",
|
|
sortOrder: 0,
|
|
};
|
|
|
|
const emptyCatalog = { items: [], pageSize: 50 };
|
|
const emptyPlatforms = { items: [] };
|
|
const defaultPageSize = 50;
|
|
|
|
export function useGamesPage() {
|
|
const abilities = useGameAbilities();
|
|
const { showToast } = useToast();
|
|
const [platformCode, setPlatformCode] = useState("");
|
|
const [status, setStatus] = useState("");
|
|
const [page, setPage] = useState(1);
|
|
const [pageCursors, setPageCursors] = useState({ 1: "" });
|
|
const [pageSize, setPageSize] = useState(defaultPageSize);
|
|
const [activeAction, setActiveAction] = useState("");
|
|
const [editingGameId, setEditingGameId] = useState("");
|
|
const [editingPlatformCode, setEditingPlatformCode] = useState("");
|
|
const [gameForm, setGameForm] = useState(defaultGameForm);
|
|
const [platformForm, setPlatformForm] = useState(defaultPlatformForm);
|
|
const [loadingAction, setLoadingAction] = useState("");
|
|
|
|
const platformsQueryFn = useCallback(() => listGamePlatforms(), []);
|
|
const {
|
|
data: platformData = emptyPlatforms,
|
|
error: platformError,
|
|
loading: platformsLoading,
|
|
reload: reloadPlatforms,
|
|
} = useAdminQuery(platformsQueryFn, {
|
|
errorMessage: "加载游戏平台失败",
|
|
initialData: emptyPlatforms,
|
|
queryKey: ["games", "platforms"],
|
|
});
|
|
|
|
const catalogQueryFn = useCallback(
|
|
() => listGameCatalog({ cursor: pageCursors[page] || "", platformCode, status, pageSize }),
|
|
[page, pageCursors, pageSize, platformCode, status],
|
|
);
|
|
const {
|
|
data = emptyCatalog,
|
|
error,
|
|
loading,
|
|
reload,
|
|
} = useAdminQuery(catalogQueryFn, {
|
|
errorMessage: "加载游戏列表失败",
|
|
initialData: emptyCatalog,
|
|
keepPreviousData: true,
|
|
queryKey: ["games", "catalog", platformCode, status, pageSize, page, pageCursors[page] || ""],
|
|
});
|
|
|
|
const platformOptions = useMemo(
|
|
() => (platformData.items || []).map((platform) => [platform.platformCode, platform.platformName]),
|
|
[platformData.items],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!gameForm.platformCode && platformOptions.length > 0 && activeAction === "create-game") {
|
|
setGameForm((current) => ({ ...current, platformCode: platformOptions[0][0] }));
|
|
}
|
|
}, [activeAction, gameForm.platformCode, platformOptions]);
|
|
|
|
const resetCatalogPage = () => {
|
|
setPage(1);
|
|
setPageCursors({ 1: "" });
|
|
};
|
|
|
|
const changePlatformCode = (value) => {
|
|
setPlatformCode(value);
|
|
resetCatalogPage();
|
|
};
|
|
|
|
const changeStatus = (value) => {
|
|
setStatus(value);
|
|
resetCatalogPage();
|
|
};
|
|
|
|
const changePageSize = (value) => {
|
|
setPageSize(value);
|
|
resetCatalogPage();
|
|
};
|
|
|
|
const changePage = (nextPage) => {
|
|
if (nextPage < 1 || nextPage === page) {
|
|
return;
|
|
}
|
|
if (nextPage === page + 1) {
|
|
if (!data.nextCursor) {
|
|
return;
|
|
}
|
|
setPageCursors((current) => ({ ...current, [nextPage]: data.nextCursor }));
|
|
}
|
|
setPage(nextPage);
|
|
};
|
|
|
|
const resetFilters = () => {
|
|
setPlatformCode("");
|
|
setStatus("");
|
|
setPageSize(defaultPageSize);
|
|
resetCatalogPage();
|
|
};
|
|
|
|
const openCreateGame = () => {
|
|
setEditingGameId("");
|
|
setGameForm({ ...defaultGameForm, platformCode: platformOptions[0]?.[0] || "" });
|
|
setActiveAction("create-game");
|
|
};
|
|
|
|
const openEditGame = (game) => {
|
|
setEditingGameId(game.gameId);
|
|
setGameForm(gameToForm(game));
|
|
setActiveAction("edit-game");
|
|
};
|
|
|
|
const openCreatePlatform = () => {
|
|
setEditingPlatformCode("");
|
|
setPlatformForm(defaultPlatformForm);
|
|
setActiveAction("create-platform");
|
|
};
|
|
|
|
const openEditPlatform = (platform) => {
|
|
setEditingPlatformCode(platform.platformCode);
|
|
setPlatformForm(platformToForm(platform));
|
|
setActiveAction("edit-platform");
|
|
};
|
|
|
|
const closeAction = () => {
|
|
setActiveAction("");
|
|
setEditingGameId("");
|
|
setEditingPlatformCode("");
|
|
};
|
|
|
|
const submitGame = async (event) => {
|
|
event.preventDefault();
|
|
const payload = gamePayload(gameForm);
|
|
setLoadingAction(activeAction);
|
|
try {
|
|
await upsertGameCatalog(payload, editingGameId);
|
|
await reload();
|
|
showToast(editingGameId ? "游戏已更新" : "游戏已创建", "success");
|
|
closeAction();
|
|
} catch (err) {
|
|
showToast(err.message || "保存游戏失败", "error");
|
|
} finally {
|
|
setLoadingAction("");
|
|
}
|
|
};
|
|
|
|
const submitPlatform = async (event) => {
|
|
event.preventDefault();
|
|
const payload = platformPayload(platformForm);
|
|
setLoadingAction(activeAction);
|
|
try {
|
|
await upsertGamePlatform(payload, editingPlatformCode);
|
|
await reloadPlatforms();
|
|
await reload();
|
|
showToast(editingPlatformCode ? "平台已更新" : "平台已创建", "success");
|
|
closeAction();
|
|
} catch (err) {
|
|
showToast(err.message || "保存平台失败", "error");
|
|
} finally {
|
|
setLoadingAction("");
|
|
}
|
|
};
|
|
|
|
const changeGameStatus = async (game, checked) => {
|
|
const nextStatus = checked ? "active" : "maintenance";
|
|
setLoadingAction(`status-${game.gameId}`);
|
|
try {
|
|
await setGameStatus(game.gameId, nextStatus);
|
|
await reload();
|
|
showToast("游戏状态已更新", "success");
|
|
} catch (err) {
|
|
showToast(err.message || "状态更新失败", "error");
|
|
} finally {
|
|
setLoadingAction("");
|
|
}
|
|
};
|
|
|
|
return {
|
|
abilities,
|
|
activeAction,
|
|
changeGameStatus,
|
|
closeAction,
|
|
data,
|
|
error: error || platformError,
|
|
gameForm,
|
|
loading: loading || platformsLoading,
|
|
loadingAction,
|
|
page,
|
|
openCreateGame,
|
|
openCreatePlatform,
|
|
openEditGame,
|
|
openEditPlatform,
|
|
pageSize,
|
|
platformCode,
|
|
platformData,
|
|
platformForm,
|
|
platformOptions,
|
|
reload,
|
|
resetFilters,
|
|
changePage,
|
|
changePageSize,
|
|
setGameForm,
|
|
setPlatformCode: changePlatformCode,
|
|
setPlatformForm,
|
|
setStatus: changeStatus,
|
|
status,
|
|
submitGame,
|
|
submitPlatform,
|
|
};
|
|
}
|
|
|
|
function gameToForm(game) {
|
|
return {
|
|
gameId: game.gameId || "",
|
|
platformCode: game.platformCode || "",
|
|
providerGameId: game.providerGameId || "",
|
|
gameName: game.gameName || "",
|
|
category: game.category || "casual",
|
|
iconUrl: game.iconUrl || "",
|
|
coverUrl: game.coverUrl || "",
|
|
launchMode: game.launchMode || "h5_popup",
|
|
orientation: game.orientation || "portrait",
|
|
minCoin: Number(game.minCoin || 0),
|
|
status: game.status || "active",
|
|
sortOrder: Number(game.sortOrder || 0),
|
|
tagsText: (game.tags || []).join(", "),
|
|
};
|
|
}
|
|
|
|
function platformToForm(platform) {
|
|
return {
|
|
platformCode: platform.platformCode || "",
|
|
platformName: platform.platformName || "",
|
|
status: platform.status || "active",
|
|
apiBaseUrl: platform.apiBaseUrl || "",
|
|
sortOrder: Number(platform.sortOrder || 0),
|
|
};
|
|
}
|
|
|
|
function gamePayload(form) {
|
|
return {
|
|
gameId: form.gameId.trim(),
|
|
platformCode: form.platformCode.trim(),
|
|
providerGameId: form.providerGameId.trim(),
|
|
gameName: form.gameName.trim(),
|
|
category: form.category.trim(),
|
|
iconUrl: form.iconUrl.trim(),
|
|
coverUrl: form.coverUrl.trim(),
|
|
launchMode: form.launchMode.trim() || "h5_popup",
|
|
orientation: form.orientation.trim() || "portrait",
|
|
minCoin: Number(form.minCoin || 0),
|
|
status: form.status.trim() || "active",
|
|
sortOrder: Number(form.sortOrder || 0),
|
|
tags: form.tagsText
|
|
.split(",")
|
|
.map((tag) => tag.trim())
|
|
.filter(Boolean),
|
|
};
|
|
}
|
|
|
|
function platformPayload(form) {
|
|
return {
|
|
platformCode: form.platformCode.trim(),
|
|
platformName: form.platformName.trim(),
|
|
status: form.status.trim() || "active",
|
|
apiBaseUrl: form.apiBaseUrl.trim(),
|
|
sortOrder: Number(form.sortOrder || 0),
|
|
};
|
|
}
|