218 lines
6.5 KiB
JavaScript
218 lines
6.5 KiB
JavaScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import { useAppScope } from "@/app/app-scope/AppScopeProvider.jsx";
|
|
import {
|
|
createSplashScreen,
|
|
deleteSplashScreen,
|
|
listSplashScreens,
|
|
updateSplashScreen,
|
|
} from "@/features/app-config/api";
|
|
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
|
import { appSplashScreenSchema } from "@/features/app-config/schema";
|
|
import { useCountryOptions } from "@/features/host-org/hooks/useCountryOptions.js";
|
|
import { parseForm } from "@/shared/forms/validation";
|
|
import { useAdminQuery } from "@/shared/hooks/useAdminQuery.js";
|
|
import { useRegionOptions } from "@/shared/hooks/useRegionOptions.js";
|
|
import { useConfirm } from "@/shared/ui/ConfirmProvider.jsx";
|
|
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
|
|
|
const emptyData = { items: [], total: 0 };
|
|
|
|
const emptyForm = () => ({
|
|
countryCode: "",
|
|
coverUrl: "",
|
|
description: "",
|
|
displayDurationMs: "3000",
|
|
endsAtMs: "",
|
|
param: "",
|
|
platform: "android",
|
|
regionId: "",
|
|
sortOrder: "0",
|
|
splashType: "h5",
|
|
startsAtMs: "",
|
|
status: "active",
|
|
});
|
|
|
|
export function useSplashScreenConfigPage() {
|
|
const { appCode } = useAppScope();
|
|
const abilities = useAppConfigAbilities();
|
|
const confirm = useConfirm();
|
|
const { showToast } = useToast();
|
|
const { countryOptions, loadingCountries } = useCountryOptions();
|
|
const { loadingRegions, regionOptions } = useRegionOptions();
|
|
const [keyword, setKeyword] = useState("");
|
|
const [status, setStatus] = useState("");
|
|
const [platform, setPlatform] = useState("");
|
|
const [regionId, setRegionId] = useState("");
|
|
const [countryCode, setCountryCode] = useState("");
|
|
const [activeAction, setActiveAction] = useState("");
|
|
const [editingItem, setEditingItem] = useState(null);
|
|
const [form, setFormState] = useState(emptyForm);
|
|
const [loadingAction, setLoadingAction] = useState("");
|
|
|
|
const filters = useMemo(
|
|
() => ({
|
|
countryCode,
|
|
keyword,
|
|
platform,
|
|
regionId,
|
|
status,
|
|
}),
|
|
[countryCode, keyword, platform, regionId, status],
|
|
);
|
|
const queryFn = useCallback(() => listSplashScreens(appCode, filters), [appCode, filters]);
|
|
const {
|
|
data = emptyData,
|
|
error,
|
|
loading,
|
|
reload,
|
|
} = useAdminQuery(queryFn, {
|
|
enabled: Boolean(appCode),
|
|
errorMessage: "加载开屏配置失败",
|
|
initialData: emptyData,
|
|
queryKey: ["app-config", "splash-screens", appCode, filters],
|
|
});
|
|
|
|
useEffect(() => {
|
|
// 开屏配置按 App 独立;切换时关闭旧表单,避免旧图片和投放条件被带到新 App。
|
|
setActiveAction("");
|
|
setEditingItem(null);
|
|
setFormState(emptyForm());
|
|
}, [appCode]);
|
|
|
|
const setForm = (patch) => {
|
|
setFormState((current) => ({ ...current, ...patch }));
|
|
};
|
|
|
|
const openCreate = () => {
|
|
setEditingItem(null);
|
|
setFormState(emptyForm());
|
|
setActiveAction("create");
|
|
};
|
|
|
|
const openEdit = (item) => {
|
|
setEditingItem(item);
|
|
setFormState(formFromSplashScreen(item));
|
|
setActiveAction("edit");
|
|
};
|
|
|
|
const closeDialog = () => {
|
|
setActiveAction("");
|
|
setEditingItem(null);
|
|
setFormState(emptyForm());
|
|
};
|
|
|
|
const resetFilters = () => {
|
|
setKeyword("");
|
|
setStatus("");
|
|
setPlatform("");
|
|
setRegionId("");
|
|
setCountryCode("");
|
|
};
|
|
|
|
const submitSplashScreen = async (event) => {
|
|
event.preventDefault();
|
|
const payload = parseForm(appSplashScreenSchema, normalizeForm(form));
|
|
const action = editingItem ? "edit" : "create";
|
|
setLoadingAction(action);
|
|
try {
|
|
if (editingItem) {
|
|
await updateSplashScreen(appCode, editingItem.id, payload);
|
|
showToast("开屏配置已更新", "success");
|
|
} else {
|
|
await createSplashScreen(appCode, payload);
|
|
showToast("开屏配置已新增", "success");
|
|
}
|
|
closeDialog();
|
|
await reload();
|
|
} catch (err) {
|
|
showToast(err.message || "操作失败", "error");
|
|
} finally {
|
|
setLoadingAction("");
|
|
}
|
|
};
|
|
|
|
const removeSplashScreen = async (item) => {
|
|
const ok = await confirm({
|
|
confirmText: "删除",
|
|
message: item.description || item.param || `开屏配置 ${item.id}`,
|
|
title: "删除开屏配置",
|
|
tone: "danger",
|
|
});
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
setLoadingAction(`delete:${item.id}`);
|
|
try {
|
|
await deleteSplashScreen(appCode, item.id);
|
|
await reload();
|
|
showToast("开屏配置已删除", "success");
|
|
} catch (err) {
|
|
showToast(err.message || "删除失败", "error");
|
|
} finally {
|
|
setLoadingAction("");
|
|
}
|
|
};
|
|
|
|
return {
|
|
abilities,
|
|
activeAction,
|
|
closeDialog,
|
|
countryCode,
|
|
countryOptions,
|
|
data,
|
|
editingItem,
|
|
error,
|
|
form,
|
|
keyword,
|
|
loading,
|
|
loadingAction,
|
|
loadingCountries,
|
|
loadingRegions,
|
|
openCreate,
|
|
openEdit,
|
|
platform,
|
|
regionId,
|
|
regionOptions,
|
|
reload,
|
|
removeSplashScreen,
|
|
resetFilters,
|
|
setCountryCode,
|
|
setForm,
|
|
setKeyword,
|
|
setPlatform,
|
|
setRegionId,
|
|
setStatus,
|
|
status,
|
|
submitSplashScreen,
|
|
};
|
|
}
|
|
|
|
function normalizeForm(form) {
|
|
return {
|
|
...form,
|
|
countryCode: form.countryCode || "",
|
|
displayDurationMs: form.displayDurationMs || 3000,
|
|
endsAtMs: form.endsAtMs || 0,
|
|
regionId: form.regionId || 0,
|
|
sortOrder: form.sortOrder || 0,
|
|
startsAtMs: form.startsAtMs || 0,
|
|
};
|
|
}
|
|
|
|
function formFromSplashScreen(item) {
|
|
return {
|
|
countryCode: item.countryCode || "",
|
|
coverUrl: item.coverUrl || "",
|
|
description: item.description || "",
|
|
displayDurationMs: String(item.displayDurationMs || 3000),
|
|
endsAtMs: item.endsAtMs || "",
|
|
param: item.param || "",
|
|
platform: item.platform || "android",
|
|
regionId: item.regionId ? String(item.regionId) : "",
|
|
sortOrder: String(item.sortOrder || 0),
|
|
splashType: item.splashType || "h5",
|
|
startsAtMs: item.startsAtMs || "",
|
|
status: item.status || "active",
|
|
};
|
|
}
|