app区域限制
This commit is contained in:
parent
1b9982e52c
commit
01989ee16e
@ -1130,6 +1130,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/banners": {
|
"/admin/app-config/banners": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "listBanners",
|
"operationId": "listBanners",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1152,6 +1157,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/banners/{banner_id}": {
|
"/admin/app-config/banners/{banner_id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateBanner",
|
"operationId": "updateBanner",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1194,6 +1204,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/splash-screens": {
|
"/admin/app-config/splash-screens": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "listSplashScreens",
|
"operationId": "listSplashScreens",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1216,6 +1231,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/splash-screens/{splash_id}": {
|
"/admin/app-config/splash-screens/{splash_id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateSplashScreen",
|
"operationId": "updateSplashScreen",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1258,6 +1278,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/popups": {
|
"/admin/app-config/popups": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "listPopups",
|
"operationId": "listPopups",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1280,6 +1305,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/popups/{popup_id}": {
|
"/admin/app-config/popups/{popup_id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"put": {
|
"put": {
|
||||||
"operationId": "updatePopup",
|
"operationId": "updatePopup",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1436,6 +1466,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/explore-tabs": {
|
"/admin/app-config/explore-tabs": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "listExploreTabs",
|
"operationId": "listExploreTabs",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1458,6 +1493,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/explore-tabs/{tab_id}": {
|
"/admin/app-config/explore-tabs/{tab_id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateExploreTab",
|
"operationId": "updateExploreTab",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1500,6 +1540,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/versions": {
|
"/admin/app-config/versions": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "listAppVersions",
|
"operationId": "listAppVersions",
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1522,6 +1567,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/app-config/versions/{version_id}": {
|
"/admin/app-config/versions/{version_id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/AppCodeHeader"
|
||||||
|
}
|
||||||
|
],
|
||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateAppVersion",
|
"operationId": "updateAppVersion",
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@ -1,12 +1,32 @@
|
|||||||
import { afterEach, expect, test, vi } from "vitest";
|
import { afterEach, expect, test, vi } from "vitest";
|
||||||
import { setAccessToken, setSelectedAppCode } from "@/shared/api/request";
|
import { setAccessToken, setSelectedAppCode } from "@/shared/api/request";
|
||||||
import {
|
import {
|
||||||
|
createAppVersion,
|
||||||
|
createBanner,
|
||||||
|
createExploreTab,
|
||||||
createH5Link,
|
createH5Link,
|
||||||
|
createPopup,
|
||||||
|
createSplashScreen,
|
||||||
createSystemMessagePushFanout,
|
createSystemMessagePushFanout,
|
||||||
|
deleteAppVersion,
|
||||||
|
deleteBanner,
|
||||||
|
deleteExploreTab,
|
||||||
deleteH5Link,
|
deleteH5Link,
|
||||||
|
deletePopup,
|
||||||
|
deleteSplashScreen,
|
||||||
|
listAppVersions,
|
||||||
|
listBanners,
|
||||||
|
listExploreTabs,
|
||||||
listH5Links,
|
listH5Links,
|
||||||
|
listPopups,
|
||||||
|
listSplashScreens,
|
||||||
|
updateAppVersion,
|
||||||
|
updateBanner,
|
||||||
|
updateExploreTab,
|
||||||
updateH5Link,
|
updateH5Link,
|
||||||
updateH5Links,
|
updateH5Links,
|
||||||
|
updatePopup,
|
||||||
|
updateSplashScreen,
|
||||||
} from "./api";
|
} from "./api";
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -56,6 +76,52 @@ test("H5 config API pins every request to its explicit app scope", async () => {
|
|||||||
expect(JSON.parse(String(calls[3][1]?.body))).toEqual(payload);
|
expect(JSON.parse(String(calls[3][1]?.body))).toEqual(payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("every APP config API pins reads and writes to the explicit app scope", async () => {
|
||||||
|
setSelectedAppCode("lalu");
|
||||||
|
vi.stubGlobal(
|
||||||
|
"fetch",
|
||||||
|
vi.fn(async (_url, init) => {
|
||||||
|
const method = String(init?.method || "GET");
|
||||||
|
const data = method === "GET" ? { items: [], total: 0 } : method === "DELETE" ? { deleted: true } : {};
|
||||||
|
return new Response(JSON.stringify({ code: 0, data }), { status: method === "POST" ? 201 : 200 });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const appCode = " Huwaa ";
|
||||||
|
const banner = { bannerType: "h5", coverUrl: "https://cdn.example.com/banner.png", platform: "android", status: "active" } as const;
|
||||||
|
const splash = { coverUrl: "https://cdn.example.com/splash.png", platform: "android", splashType: "h5", status: "active" } as const;
|
||||||
|
const popup = { code: "welcome", jumpType: "h5", jumpUrl: "https://h5.example.com/welcome", name: "Welcome", status: "active" } as const;
|
||||||
|
const explore = { enabled: true, h5Url: "https://h5.example.com/explore", tab: "Explore" };
|
||||||
|
const version = { buildNumber: 10, downloadUrl: "https://cdn.example.com/app.apk", forceUpdate: false, platform: "android", version: "1.0.0" } as const;
|
||||||
|
|
||||||
|
await listBanners(appCode);
|
||||||
|
await createBanner(appCode, banner);
|
||||||
|
await updateBanner(appCode, 1, banner);
|
||||||
|
await deleteBanner(appCode, 1);
|
||||||
|
await listSplashScreens(appCode);
|
||||||
|
await createSplashScreen(appCode, splash);
|
||||||
|
await updateSplashScreen(appCode, 2, splash);
|
||||||
|
await deleteSplashScreen(appCode, 2);
|
||||||
|
await listPopups(appCode);
|
||||||
|
await createPopup(appCode, popup);
|
||||||
|
await updatePopup(appCode, 3, popup);
|
||||||
|
await deletePopup(appCode, 3);
|
||||||
|
await listExploreTabs(appCode);
|
||||||
|
await createExploreTab(appCode, explore);
|
||||||
|
await updateExploreTab(appCode, 4, explore);
|
||||||
|
await deleteExploreTab(appCode, 4);
|
||||||
|
await listAppVersions(appCode);
|
||||||
|
await createAppVersion(appCode, version);
|
||||||
|
await updateAppVersion(appCode, 5, version);
|
||||||
|
await deleteAppVersion(appCode, 5);
|
||||||
|
|
||||||
|
const calls = vi.mocked(fetch).mock.calls;
|
||||||
|
expect(calls).toHaveLength(20);
|
||||||
|
calls.forEach(([, init]) => {
|
||||||
|
expect(init?.headers).toMatchObject({ "X-App-Code": "huwaa" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("system message push fanout API keeps existing operations path", async () => {
|
test("system message push fanout API keeps existing operations path", async () => {
|
||||||
vi.stubGlobal(
|
vi.stubGlobal(
|
||||||
"fetch",
|
"fetch",
|
||||||
|
|||||||
@ -100,181 +100,201 @@ export function deleteH5Link(appCode: string, key: EntityId): Promise<{ deleted:
|
|||||||
}
|
}
|
||||||
|
|
||||||
function appScopeHeaders(appCode: string) {
|
function appScopeHeaders(appCode: string) {
|
||||||
// H5 配置是强 App 隔离数据;显式固定请求头可避免用户切换 App 时全局请求上下文变化导致写入错误租户。
|
// APP 配置全部是强 App 隔离数据;显式固定请求头可避免切换 App 时全局请求上下文变化导致写入错误租户。
|
||||||
return { "X-App-Code": String(appCode || "").trim().toLowerCase() };
|
return { "X-App-Code": String(appCode || "").trim().toLowerCase() };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listExploreTabs(query: PageQuery = {}): Promise<ApiList<ExploreTabDto>> {
|
export function listExploreTabs(appCode: string, query: PageQuery = {}): Promise<ApiList<ExploreTabDto>> {
|
||||||
const endpoint = API_ENDPOINTS.listExploreTabs;
|
const endpoint = API_ENDPOINTS.listExploreTabs;
|
||||||
return apiRequest<ApiList<ExploreTabDto>>(apiEndpointPath(API_OPERATIONS.listExploreTabs), {
|
return apiRequest<ApiList<ExploreTabDto>>(apiEndpointPath(API_OPERATIONS.listExploreTabs), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
query,
|
query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExploreTab(payload: ExploreTabPayload): Promise<ExploreTabDto> {
|
export function createExploreTab(appCode: string, payload: ExploreTabPayload): Promise<ExploreTabDto> {
|
||||||
const endpoint = API_ENDPOINTS.createExploreTab;
|
const endpoint = API_ENDPOINTS.createExploreTab;
|
||||||
return apiRequest<ExploreTabDto, ExploreTabPayload>(apiEndpointPath(API_OPERATIONS.createExploreTab), {
|
return apiRequest<ExploreTabDto, ExploreTabPayload>(apiEndpointPath(API_OPERATIONS.createExploreTab), {
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateExploreTab(tabId: EntityId, payload: ExploreTabPayload): Promise<ExploreTabDto> {
|
export function updateExploreTab(appCode: string, tabId: EntityId, payload: ExploreTabPayload): Promise<ExploreTabDto> {
|
||||||
const endpoint = API_ENDPOINTS.updateExploreTab;
|
const endpoint = API_ENDPOINTS.updateExploreTab;
|
||||||
return apiRequest<ExploreTabDto, ExploreTabPayload>(
|
return apiRequest<ExploreTabDto, ExploreTabPayload>(
|
||||||
apiEndpointPath(API_OPERATIONS.updateExploreTab, { tab_id: tabId }),
|
apiEndpointPath(API_OPERATIONS.updateExploreTab, { tab_id: tabId }),
|
||||||
{
|
{
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteExploreTab(tabId: EntityId): Promise<{ deleted: boolean }> {
|
export function deleteExploreTab(appCode: string, tabId: EntityId): Promise<{ deleted: boolean }> {
|
||||||
const endpoint = API_ENDPOINTS.deleteExploreTab;
|
const endpoint = API_ENDPOINTS.deleteExploreTab;
|
||||||
return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deleteExploreTab, { tab_id: tabId }), {
|
return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deleteExploreTab, { tab_id: tabId }), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listBanners(query: PageQuery = {}): Promise<ApiList<AppBannerDto>> {
|
export function listBanners(appCode: string, query: PageQuery = {}): Promise<ApiList<AppBannerDto>> {
|
||||||
const endpoint = API_ENDPOINTS.listBanners;
|
const endpoint = API_ENDPOINTS.listBanners;
|
||||||
return apiRequest<ApiList<AppBannerDto>>(apiEndpointPath(API_OPERATIONS.listBanners), {
|
return apiRequest<ApiList<AppBannerDto>>(apiEndpointPath(API_OPERATIONS.listBanners), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
query,
|
query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBanner(payload: AppBannerPayload): Promise<AppBannerDto> {
|
export function createBanner(appCode: string, payload: AppBannerPayload): Promise<AppBannerDto> {
|
||||||
const endpoint = API_ENDPOINTS.createBanner;
|
const endpoint = API_ENDPOINTS.createBanner;
|
||||||
return apiRequest<AppBannerDto, AppBannerPayload>(apiEndpointPath(API_OPERATIONS.createBanner), {
|
return apiRequest<AppBannerDto, AppBannerPayload>(apiEndpointPath(API_OPERATIONS.createBanner), {
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateBanner(bannerId: EntityId, payload: AppBannerPayload): Promise<AppBannerDto> {
|
export function updateBanner(appCode: string, bannerId: EntityId, payload: AppBannerPayload): Promise<AppBannerDto> {
|
||||||
const endpoint = API_ENDPOINTS.updateBanner;
|
const endpoint = API_ENDPOINTS.updateBanner;
|
||||||
return apiRequest<AppBannerDto, AppBannerPayload>(
|
return apiRequest<AppBannerDto, AppBannerPayload>(
|
||||||
apiEndpointPath(API_OPERATIONS.updateBanner, { banner_id: bannerId }),
|
apiEndpointPath(API_OPERATIONS.updateBanner, { banner_id: bannerId }),
|
||||||
{
|
{
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteBanner(bannerId: EntityId): Promise<{ deleted: boolean }> {
|
export function deleteBanner(appCode: string, bannerId: EntityId): Promise<{ deleted: boolean }> {
|
||||||
const endpoint = API_ENDPOINTS.deleteBanner;
|
const endpoint = API_ENDPOINTS.deleteBanner;
|
||||||
return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deleteBanner, { banner_id: bannerId }), {
|
return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deleteBanner, { banner_id: bannerId }), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listSplashScreens(query: PageQuery = {}): Promise<ApiList<AppSplashScreenDto>> {
|
export function listSplashScreens(appCode: string, query: PageQuery = {}): Promise<ApiList<AppSplashScreenDto>> {
|
||||||
const endpoint = API_ENDPOINTS.listSplashScreens;
|
const endpoint = API_ENDPOINTS.listSplashScreens;
|
||||||
return apiRequest<ApiList<AppSplashScreenDto>>(apiEndpointPath(API_OPERATIONS.listSplashScreens), {
|
return apiRequest<ApiList<AppSplashScreenDto>>(apiEndpointPath(API_OPERATIONS.listSplashScreens), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
query,
|
query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSplashScreen(payload: AppSplashScreenPayload): Promise<AppSplashScreenDto> {
|
export function createSplashScreen(appCode: string, payload: AppSplashScreenPayload): Promise<AppSplashScreenDto> {
|
||||||
const endpoint = API_ENDPOINTS.createSplashScreen;
|
const endpoint = API_ENDPOINTS.createSplashScreen;
|
||||||
return apiRequest<AppSplashScreenDto, AppSplashScreenPayload>(apiEndpointPath(API_OPERATIONS.createSplashScreen), {
|
return apiRequest<AppSplashScreenDto, AppSplashScreenPayload>(apiEndpointPath(API_OPERATIONS.createSplashScreen), {
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateSplashScreen(splashId: EntityId, payload: AppSplashScreenPayload): Promise<AppSplashScreenDto> {
|
export function updateSplashScreen(appCode: string, splashId: EntityId, payload: AppSplashScreenPayload): Promise<AppSplashScreenDto> {
|
||||||
const endpoint = API_ENDPOINTS.updateSplashScreen;
|
const endpoint = API_ENDPOINTS.updateSplashScreen;
|
||||||
return apiRequest<AppSplashScreenDto, AppSplashScreenPayload>(
|
return apiRequest<AppSplashScreenDto, AppSplashScreenPayload>(
|
||||||
apiEndpointPath(API_OPERATIONS.updateSplashScreen, { splash_id: splashId }),
|
apiEndpointPath(API_OPERATIONS.updateSplashScreen, { splash_id: splashId }),
|
||||||
{
|
{
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteSplashScreen(splashId: EntityId): Promise<{ deleted: boolean }> {
|
export function deleteSplashScreen(appCode: string, splashId: EntityId): Promise<{ deleted: boolean }> {
|
||||||
const endpoint = API_ENDPOINTS.deleteSplashScreen;
|
const endpoint = API_ENDPOINTS.deleteSplashScreen;
|
||||||
return apiRequest<{ deleted: boolean }>(
|
return apiRequest<{ deleted: boolean }>(
|
||||||
apiEndpointPath(API_OPERATIONS.deleteSplashScreen, { splash_id: splashId }),
|
apiEndpointPath(API_OPERATIONS.deleteSplashScreen, { splash_id: splashId }),
|
||||||
{
|
{
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listPopups(query: PageQuery = {}): Promise<ApiList<AppPopupDto>> {
|
export function listPopups(appCode: string, query: PageQuery = {}): Promise<ApiList<AppPopupDto>> {
|
||||||
const endpoint = API_ENDPOINTS.listPopups;
|
const endpoint = API_ENDPOINTS.listPopups;
|
||||||
return apiRequest<ApiList<AppPopupDto>>(apiEndpointPath(API_OPERATIONS.listPopups), {
|
return apiRequest<ApiList<AppPopupDto>>(apiEndpointPath(API_OPERATIONS.listPopups), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
query,
|
query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPopup(payload: AppPopupPayload): Promise<AppPopupDto> {
|
export function createPopup(appCode: string, payload: AppPopupPayload): Promise<AppPopupDto> {
|
||||||
const endpoint = API_ENDPOINTS.createPopup;
|
const endpoint = API_ENDPOINTS.createPopup;
|
||||||
return apiRequest<AppPopupDto, AppPopupPayload>(apiEndpointPath(API_OPERATIONS.createPopup), {
|
return apiRequest<AppPopupDto, AppPopupPayload>(apiEndpointPath(API_OPERATIONS.createPopup), {
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updatePopup(popupId: EntityId, payload: AppPopupPayload): Promise<AppPopupDto> {
|
export function updatePopup(appCode: string, popupId: EntityId, payload: AppPopupPayload): Promise<AppPopupDto> {
|
||||||
const endpoint = API_ENDPOINTS.updatePopup;
|
const endpoint = API_ENDPOINTS.updatePopup;
|
||||||
return apiRequest<AppPopupDto, AppPopupPayload>(
|
return apiRequest<AppPopupDto, AppPopupPayload>(
|
||||||
apiEndpointPath(API_OPERATIONS.updatePopup, { popup_id: popupId }),
|
apiEndpointPath(API_OPERATIONS.updatePopup, { popup_id: popupId }),
|
||||||
{
|
{
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deletePopup(popupId: EntityId): Promise<{ deleted: boolean }> {
|
export function deletePopup(appCode: string, popupId: EntityId): Promise<{ deleted: boolean }> {
|
||||||
const endpoint = API_ENDPOINTS.deletePopup;
|
const endpoint = API_ENDPOINTS.deletePopup;
|
||||||
return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deletePopup, { popup_id: popupId }), {
|
return apiRequest<{ deleted: boolean }>(apiEndpointPath(API_OPERATIONS.deletePopup, { popup_id: popupId }), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listAppVersions(query: PageQuery = {}): Promise<ApiList<AppVersionDto>> {
|
export function listAppVersions(appCode: string, query: PageQuery = {}): Promise<ApiList<AppVersionDto>> {
|
||||||
const endpoint = API_ENDPOINTS.listAppVersions;
|
const endpoint = API_ENDPOINTS.listAppVersions;
|
||||||
return apiRequest<ApiList<AppVersionDto>>(apiEndpointPath(API_OPERATIONS.listAppVersions), {
|
return apiRequest<ApiList<AppVersionDto>>(apiEndpointPath(API_OPERATIONS.listAppVersions), {
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
query,
|
query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAppVersion(payload: AppVersionPayload): Promise<AppVersionDto> {
|
export function createAppVersion(appCode: string, payload: AppVersionPayload): Promise<AppVersionDto> {
|
||||||
const endpoint = API_ENDPOINTS.createAppVersion;
|
const endpoint = API_ENDPOINTS.createAppVersion;
|
||||||
return apiRequest<AppVersionDto, AppVersionPayload>(apiEndpointPath(API_OPERATIONS.createAppVersion), {
|
return apiRequest<AppVersionDto, AppVersionPayload>(apiEndpointPath(API_OPERATIONS.createAppVersion), {
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateAppVersion(versionId: EntityId, payload: AppVersionPayload): Promise<AppVersionDto> {
|
export function updateAppVersion(appCode: string, versionId: EntityId, payload: AppVersionPayload): Promise<AppVersionDto> {
|
||||||
const endpoint = API_ENDPOINTS.updateAppVersion;
|
const endpoint = API_ENDPOINTS.updateAppVersion;
|
||||||
return apiRequest<AppVersionDto, AppVersionPayload>(
|
return apiRequest<AppVersionDto, AppVersionPayload>(
|
||||||
apiEndpointPath(API_OPERATIONS.updateAppVersion, { version_id: versionId }),
|
apiEndpointPath(API_OPERATIONS.updateAppVersion, { version_id: versionId }),
|
||||||
{
|
{
|
||||||
body: payload,
|
body: payload,
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteAppVersion(versionId: EntityId): Promise<{ deleted: boolean }> {
|
export function deleteAppVersion(appCode: string, versionId: EntityId): Promise<{ deleted: boolean }> {
|
||||||
const endpoint = API_ENDPOINTS.deleteAppVersion;
|
const endpoint = API_ENDPOINTS.deleteAppVersion;
|
||||||
return apiRequest<{ deleted: boolean }>(
|
return apiRequest<{ deleted: boolean }>(
|
||||||
apiEndpointPath(API_OPERATIONS.deleteAppVersion, { version_id: versionId }),
|
apiEndpointPath(API_OPERATIONS.deleteAppVersion, { version_id: versionId }),
|
||||||
{
|
{
|
||||||
|
headers: appScopeHeaders(appCode),
|
||||||
method: endpoint.method,
|
method: endpoint.method,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useAppScope } from "@/app/app-scope/AppScopeProvider.jsx";
|
||||||
import { createAppVersion, deleteAppVersion, listAppVersions, updateAppVersion } from "@/features/app-config/api";
|
import { createAppVersion, deleteAppVersion, listAppVersions, updateAppVersion } from "@/features/app-config/api";
|
||||||
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
||||||
import { appVersionSchema } from "@/features/app-config/schema";
|
import { appVersionSchema } from "@/features/app-config/schema";
|
||||||
@ -19,6 +20,7 @@ const emptyForm = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function useAppVersionsPage() {
|
export function useAppVersionsPage() {
|
||||||
|
const { appCode } = useAppScope();
|
||||||
const abilities = useAppConfigAbilities();
|
const abilities = useAppConfigAbilities();
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@ -30,18 +32,26 @@ export function useAppVersionsPage() {
|
|||||||
const [loadingAction, setLoadingAction] = useState("");
|
const [loadingAction, setLoadingAction] = useState("");
|
||||||
|
|
||||||
const filters = useMemo(() => ({ keyword, platform }), [keyword, platform]);
|
const filters = useMemo(() => ({ keyword, platform }), [keyword, platform]);
|
||||||
const queryFn = useCallback(() => listAppVersions(filters), [filters]);
|
const queryFn = useCallback(() => listAppVersions(appCode, filters), [appCode, filters]);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
reload
|
reload
|
||||||
} = useAdminQuery(queryFn, {
|
} = useAdminQuery(queryFn, {
|
||||||
|
enabled: Boolean(appCode),
|
||||||
errorMessage: "加载版本配置失败",
|
errorMessage: "加载版本配置失败",
|
||||||
initialData: emptyData,
|
initialData: emptyData,
|
||||||
queryKey: ["app-config", "versions", filters]
|
queryKey: ["app-config", "versions", appCode, filters]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 安装包和强更策略必须按 App 隔离;切换 App 后清除旧版本编辑态,避免发布到错误租户。
|
||||||
|
setActiveAction("");
|
||||||
|
setEditingItem(null);
|
||||||
|
setFormState(emptyForm());
|
||||||
|
}, [appCode]);
|
||||||
|
|
||||||
const setForm = (patch) => {
|
const setForm = (patch) => {
|
||||||
setFormState((current) => ({ ...current, ...patch }));
|
setFormState((current) => ({ ...current, ...patch }));
|
||||||
};
|
};
|
||||||
@ -76,10 +86,10 @@ export function useAppVersionsPage() {
|
|||||||
setLoadingAction(action);
|
setLoadingAction(action);
|
||||||
try {
|
try {
|
||||||
if (editingItem) {
|
if (editingItem) {
|
||||||
await updateAppVersion(editingItem.id, payload);
|
await updateAppVersion(appCode, editingItem.id, payload);
|
||||||
showToast("版本配置已更新", "success");
|
showToast("版本配置已更新", "success");
|
||||||
} else {
|
} else {
|
||||||
await createAppVersion(payload);
|
await createAppVersion(appCode, payload);
|
||||||
showToast("版本配置已新增", "success");
|
showToast("版本配置已新增", "success");
|
||||||
}
|
}
|
||||||
closeDialog();
|
closeDialog();
|
||||||
@ -103,7 +113,7 @@ export function useAppVersionsPage() {
|
|||||||
}
|
}
|
||||||
setLoadingAction(`delete:${item.id}`);
|
setLoadingAction(`delete:${item.id}`);
|
||||||
try {
|
try {
|
||||||
await deleteAppVersion(item.id);
|
await deleteAppVersion(appCode, item.id);
|
||||||
await reload();
|
await reload();
|
||||||
showToast("版本配置已删除", "success");
|
showToast("版本配置已删除", "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useAppScope } from "@/app/app-scope/AppScopeProvider.jsx";
|
||||||
import { createBanner, deleteBanner, listBanners, updateBanner } from "@/features/app-config/api";
|
import { createBanner, deleteBanner, listBanners, updateBanner } from "@/features/app-config/api";
|
||||||
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
||||||
import { appBannerSchema } from "@/features/app-config/schema";
|
import { appBannerSchema } from "@/features/app-config/schema";
|
||||||
@ -28,6 +29,7 @@ const emptyForm = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function useBannerConfigPage() {
|
export function useBannerConfigPage() {
|
||||||
|
const { appCode } = useAppScope();
|
||||||
const abilities = useAppConfigAbilities();
|
const abilities = useAppConfigAbilities();
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@ -55,18 +57,26 @@ export function useBannerConfigPage() {
|
|||||||
}),
|
}),
|
||||||
[countryCode, displayScope, keyword, platform, regionId, status],
|
[countryCode, displayScope, keyword, platform, regionId, status],
|
||||||
);
|
);
|
||||||
const queryFn = useCallback(() => listBanners(filters), [filters]);
|
const queryFn = useCallback(() => listBanners(appCode, filters), [appCode, filters]);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
reload,
|
reload,
|
||||||
} = useAdminQuery(queryFn, {
|
} = useAdminQuery(queryFn, {
|
||||||
|
enabled: Boolean(appCode),
|
||||||
errorMessage: "加载 BANNER 配置失败",
|
errorMessage: "加载 BANNER 配置失败",
|
||||||
initialData: emptyData,
|
initialData: emptyData,
|
||||||
queryKey: ["app-config", "banners", filters],
|
queryKey: ["app-config", "banners", appCode, filters],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 切换 App 后丢弃旧租户表单;已发出的请求仍携带旧 appCode,只会回写旧租户缓存键。
|
||||||
|
setActiveAction("");
|
||||||
|
setEditingItem(null);
|
||||||
|
setFormState(emptyForm());
|
||||||
|
}, [appCode]);
|
||||||
|
|
||||||
const setForm = (patch) => {
|
const setForm = (patch) => {
|
||||||
setFormState((current) => ({ ...current, ...patch }));
|
setFormState((current) => ({ ...current, ...patch }));
|
||||||
};
|
};
|
||||||
@ -105,10 +115,10 @@ export function useBannerConfigPage() {
|
|||||||
setLoadingAction(action);
|
setLoadingAction(action);
|
||||||
try {
|
try {
|
||||||
if (editingItem) {
|
if (editingItem) {
|
||||||
await updateBanner(editingItem.id, payload);
|
await updateBanner(appCode, editingItem.id, payload);
|
||||||
showToast("BANNER 已更新", "success");
|
showToast("BANNER 已更新", "success");
|
||||||
} else {
|
} else {
|
||||||
await createBanner(payload);
|
await createBanner(appCode, payload);
|
||||||
showToast("BANNER 已新增", "success");
|
showToast("BANNER 已新增", "success");
|
||||||
}
|
}
|
||||||
closeDialog();
|
closeDialog();
|
||||||
@ -132,7 +142,7 @@ export function useBannerConfigPage() {
|
|||||||
}
|
}
|
||||||
setLoadingAction(`delete:${item.id}`);
|
setLoadingAction(`delete:${item.id}`);
|
||||||
try {
|
try {
|
||||||
await deleteBanner(item.id);
|
await deleteBanner(appCode, item.id);
|
||||||
await reload();
|
await reload();
|
||||||
showToast("BANNER 已删除", "success");
|
showToast("BANNER 已删除", "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useAppScope } from "@/app/app-scope/AppScopeProvider.jsx";
|
||||||
import { createExploreTab, deleteExploreTab, listExploreTabs, updateExploreTab } from "@/features/app-config/api";
|
import { createExploreTab, deleteExploreTab, listExploreTabs, updateExploreTab } from "@/features/app-config/api";
|
||||||
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
||||||
import { exploreTabSchema } from "@/features/app-config/schema";
|
import { exploreTabSchema } from "@/features/app-config/schema";
|
||||||
@ -17,6 +18,7 @@ const emptyForm = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function useExploreConfigPage() {
|
export function useExploreConfigPage() {
|
||||||
|
const { appCode } = useAppScope();
|
||||||
const abilities = useAppConfigAbilities();
|
const abilities = useAppConfigAbilities();
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@ -28,18 +30,26 @@ export function useExploreConfigPage() {
|
|||||||
const [loadingAction, setLoadingAction] = useState("");
|
const [loadingAction, setLoadingAction] = useState("");
|
||||||
|
|
||||||
const filters = useMemo(() => ({ enabled, keyword }), [enabled, keyword]);
|
const filters = useMemo(() => ({ enabled, keyword }), [enabled, keyword]);
|
||||||
const queryFn = useCallback(() => listExploreTabs(filters), [filters]);
|
const queryFn = useCallback(() => listExploreTabs(appCode, filters), [appCode, filters]);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
reload,
|
reload,
|
||||||
} = useAdminQuery(queryFn, {
|
} = useAdminQuery(queryFn, {
|
||||||
|
enabled: Boolean(appCode),
|
||||||
errorMessage: "加载 Explore 配置失败",
|
errorMessage: "加载 Explore 配置失败",
|
||||||
initialData: emptyData,
|
initialData: emptyData,
|
||||||
queryKey: ["app-config", "explore-tabs", filters],
|
queryKey: ["app-config", "explore-tabs", appCode, filters],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Explore tab 的文案和 H5 地址属于 App 品牌内容,切换 App 后不能保留旧租户编辑态。
|
||||||
|
setActiveAction("");
|
||||||
|
setEditingItem(null);
|
||||||
|
setFormState(emptyForm());
|
||||||
|
}, [appCode]);
|
||||||
|
|
||||||
const setForm = (patch) => {
|
const setForm = (patch) => {
|
||||||
setFormState((current) => ({ ...current, ...patch }));
|
setFormState((current) => ({ ...current, ...patch }));
|
||||||
};
|
};
|
||||||
@ -74,10 +84,10 @@ export function useExploreConfigPage() {
|
|||||||
setLoadingAction(action);
|
setLoadingAction(action);
|
||||||
try {
|
try {
|
||||||
if (editingItem) {
|
if (editingItem) {
|
||||||
await updateExploreTab(editingItem.id, payload);
|
await updateExploreTab(appCode, editingItem.id, payload);
|
||||||
showToast("Explore 配置已更新", "success");
|
showToast("Explore 配置已更新", "success");
|
||||||
} else {
|
} else {
|
||||||
await createExploreTab(payload);
|
await createExploreTab(appCode, payload);
|
||||||
showToast("Explore 配置已新增", "success");
|
showToast("Explore 配置已新增", "success");
|
||||||
}
|
}
|
||||||
closeDialog();
|
closeDialog();
|
||||||
@ -92,7 +102,7 @@ export function useExploreConfigPage() {
|
|||||||
const toggleExploreTab = async (item, nextEnabled) => {
|
const toggleExploreTab = async (item, nextEnabled) => {
|
||||||
setLoadingAction(`toggle:${item.id}`);
|
setLoadingAction(`toggle:${item.id}`);
|
||||||
try {
|
try {
|
||||||
await updateExploreTab(item.id, {
|
await updateExploreTab(appCode, item.id, {
|
||||||
enabled: nextEnabled,
|
enabled: nextEnabled,
|
||||||
h5Url: item.h5Url || "",
|
h5Url: item.h5Url || "",
|
||||||
sortOrder: item.sortOrder || 0,
|
sortOrder: item.sortOrder || 0,
|
||||||
@ -119,7 +129,7 @@ export function useExploreConfigPage() {
|
|||||||
}
|
}
|
||||||
setLoadingAction(`delete:${item.id}`);
|
setLoadingAction(`delete:${item.id}`);
|
||||||
try {
|
try {
|
||||||
await deleteExploreTab(item.id);
|
await deleteExploreTab(appCode, item.id);
|
||||||
await reload();
|
await reload();
|
||||||
showToast("Explore 配置已删除", "success");
|
showToast("Explore 配置已删除", "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useAppScope } from "@/app/app-scope/AppScopeProvider.jsx";
|
||||||
import { createPopup, deletePopup, listPopups, updatePopup } from "@/features/app-config/api";
|
import { createPopup, deletePopup, listPopups, updatePopup } from "@/features/app-config/api";
|
||||||
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
import { useAppConfigAbilities } from "@/features/app-config/permissions.js";
|
||||||
import { appPopupSchema } from "@/features/app-config/schema";
|
import { appPopupSchema } from "@/features/app-config/schema";
|
||||||
@ -23,6 +24,7 @@ const emptyForm = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function usePopupConfigPage() {
|
export function usePopupConfigPage() {
|
||||||
|
const { appCode } = useAppScope();
|
||||||
const abilities = useAppConfigAbilities();
|
const abilities = useAppConfigAbilities();
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@ -40,18 +42,26 @@ export function usePopupConfigPage() {
|
|||||||
}),
|
}),
|
||||||
[keyword, status],
|
[keyword, status],
|
||||||
);
|
);
|
||||||
const queryFn = useCallback(() => listPopups(filters), [filters]);
|
const queryFn = useCallback(() => listPopups(appCode, filters), [appCode, filters]);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
reload,
|
reload,
|
||||||
} = useAdminQuery(queryFn, {
|
} = useAdminQuery(queryFn, {
|
||||||
|
enabled: Boolean(appCode),
|
||||||
errorMessage: "加载弹窗配置失败",
|
errorMessage: "加载弹窗配置失败",
|
||||||
initialData: emptyData,
|
initialData: emptyData,
|
||||||
queryKey: ["app-config", "popups", filters],
|
queryKey: ["app-config", "popups", appCode, filters],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 弹窗 code 只在单个 App 内唯一;切换时必须丢弃旧编辑态,不能把旧租户内容复制到新租户。
|
||||||
|
setActiveAction("");
|
||||||
|
setEditingItem(null);
|
||||||
|
setFormState(emptyForm());
|
||||||
|
}, [appCode]);
|
||||||
|
|
||||||
const setForm = (patch) => {
|
const setForm = (patch) => {
|
||||||
setFormState((current) => ({ ...current, ...patch }));
|
setFormState((current) => ({ ...current, ...patch }));
|
||||||
};
|
};
|
||||||
@ -86,10 +96,10 @@ export function usePopupConfigPage() {
|
|||||||
setLoadingAction(action);
|
setLoadingAction(action);
|
||||||
try {
|
try {
|
||||||
if (editingItem) {
|
if (editingItem) {
|
||||||
await updatePopup(editingItem.id, payload);
|
await updatePopup(appCode, editingItem.id, payload);
|
||||||
showToast("弹窗配置已更新", "success");
|
showToast("弹窗配置已更新", "success");
|
||||||
} else {
|
} else {
|
||||||
await createPopup(payload);
|
await createPopup(appCode, payload);
|
||||||
showToast("弹窗配置已新增", "success");
|
showToast("弹窗配置已新增", "success");
|
||||||
}
|
}
|
||||||
closeDialog();
|
closeDialog();
|
||||||
@ -113,7 +123,7 @@ export function usePopupConfigPage() {
|
|||||||
}
|
}
|
||||||
setLoadingAction(`delete:${item.id}`);
|
setLoadingAction(`delete:${item.id}`);
|
||||||
try {
|
try {
|
||||||
await deletePopup(item.id);
|
await deletePopup(appCode, item.id);
|
||||||
await reload();
|
await reload();
|
||||||
showToast("弹窗配置已删除", "success");
|
showToast("弹窗配置已删除", "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useAppScope } from "@/app/app-scope/AppScopeProvider.jsx";
|
||||||
import {
|
import {
|
||||||
createSplashScreen,
|
createSplashScreen,
|
||||||
deleteSplashScreen,
|
deleteSplashScreen,
|
||||||
@ -32,6 +33,7 @@ const emptyForm = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function useSplashScreenConfigPage() {
|
export function useSplashScreenConfigPage() {
|
||||||
|
const { appCode } = useAppScope();
|
||||||
const abilities = useAppConfigAbilities();
|
const abilities = useAppConfigAbilities();
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@ -57,18 +59,26 @@ export function useSplashScreenConfigPage() {
|
|||||||
}),
|
}),
|
||||||
[countryCode, keyword, platform, regionId, status],
|
[countryCode, keyword, platform, regionId, status],
|
||||||
);
|
);
|
||||||
const queryFn = useCallback(() => listSplashScreens(filters), [filters]);
|
const queryFn = useCallback(() => listSplashScreens(appCode, filters), [appCode, filters]);
|
||||||
const {
|
const {
|
||||||
data = emptyData,
|
data = emptyData,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
reload,
|
reload,
|
||||||
} = useAdminQuery(queryFn, {
|
} = useAdminQuery(queryFn, {
|
||||||
|
enabled: Boolean(appCode),
|
||||||
errorMessage: "加载开屏配置失败",
|
errorMessage: "加载开屏配置失败",
|
||||||
initialData: emptyData,
|
initialData: emptyData,
|
||||||
queryKey: ["app-config", "splash-screens", filters],
|
queryKey: ["app-config", "splash-screens", appCode, filters],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 开屏配置按 App 独立;切换时关闭旧表单,避免旧图片和投放条件被带到新 App。
|
||||||
|
setActiveAction("");
|
||||||
|
setEditingItem(null);
|
||||||
|
setFormState(emptyForm());
|
||||||
|
}, [appCode]);
|
||||||
|
|
||||||
const setForm = (patch) => {
|
const setForm = (patch) => {
|
||||||
setFormState((current) => ({ ...current, ...patch }));
|
setFormState((current) => ({ ...current, ...patch }));
|
||||||
};
|
};
|
||||||
@ -106,10 +116,10 @@ export function useSplashScreenConfigPage() {
|
|||||||
setLoadingAction(action);
|
setLoadingAction(action);
|
||||||
try {
|
try {
|
||||||
if (editingItem) {
|
if (editingItem) {
|
||||||
await updateSplashScreen(editingItem.id, payload);
|
await updateSplashScreen(appCode, editingItem.id, payload);
|
||||||
showToast("开屏配置已更新", "success");
|
showToast("开屏配置已更新", "success");
|
||||||
} else {
|
} else {
|
||||||
await createSplashScreen(payload);
|
await createSplashScreen(appCode, payload);
|
||||||
showToast("开屏配置已新增", "success");
|
showToast("开屏配置已新增", "success");
|
||||||
}
|
}
|
||||||
closeDialog();
|
closeDialog();
|
||||||
@ -133,7 +143,7 @@ export function useSplashScreenConfigPage() {
|
|||||||
}
|
}
|
||||||
setLoadingAction(`delete:${item.id}`);
|
setLoadingAction(`delete:${item.id}`);
|
||||||
try {
|
try {
|
||||||
await deleteSplashScreen(item.id);
|
await deleteSplashScreen(appCode, item.id);
|
||||||
await reload();
|
await reload();
|
||||||
showToast("开屏配置已删除", "success");
|
showToast("开屏配置已删除", "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
136
src/shared/api/generated/schema.d.ts
vendored
136
src/shared/api/generated/schema.d.ts
vendored
@ -855,7 +855,9 @@ export interface paths {
|
|||||||
"/admin/app-config/banners": {
|
"/admin/app-config/banners": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -871,7 +873,9 @@ export interface paths {
|
|||||||
"/admin/app-config/banners/{banner_id}": {
|
"/admin/app-config/banners/{banner_id}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -887,7 +891,9 @@ export interface paths {
|
|||||||
"/admin/app-config/splash-screens": {
|
"/admin/app-config/splash-screens": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -903,7 +909,9 @@ export interface paths {
|
|||||||
"/admin/app-config/splash-screens/{splash_id}": {
|
"/admin/app-config/splash-screens/{splash_id}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -919,7 +927,9 @@ export interface paths {
|
|||||||
"/admin/app-config/popups": {
|
"/admin/app-config/popups": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -935,7 +945,9 @@ export interface paths {
|
|||||||
"/admin/app-config/popups/{popup_id}": {
|
"/admin/app-config/popups/{popup_id}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -987,7 +999,9 @@ export interface paths {
|
|||||||
"/admin/app-config/explore-tabs": {
|
"/admin/app-config/explore-tabs": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -1003,7 +1017,9 @@ export interface paths {
|
|||||||
"/admin/app-config/explore-tabs/{tab_id}": {
|
"/admin/app-config/explore-tabs/{tab_id}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -1019,7 +1035,9 @@ export interface paths {
|
|||||||
"/admin/app-config/versions": {
|
"/admin/app-config/versions": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -1035,7 +1053,9 @@ export interface paths {
|
|||||||
"/admin/app-config/versions/{version_id}": {
|
"/admin/app-config/versions/{version_id}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -5725,7 +5745,11 @@ export interface components {
|
|||||||
providerStatus?: string;
|
providerStatus?: string;
|
||||||
providerPayType?: string;
|
providerPayType?: string;
|
||||||
receiveAddress?: string;
|
receiveAddress?: string;
|
||||||
orderDate?: string;
|
/**
|
||||||
|
* Format: int64
|
||||||
|
* @description 订单业务时间;历史订单为 0 时回退到 createdAtMs
|
||||||
|
*/
|
||||||
|
orderDateMs?: number;
|
||||||
status: string;
|
status: string;
|
||||||
verifyStatus: string;
|
verifyStatus: string;
|
||||||
grantStatus: string;
|
grantStatus: string;
|
||||||
@ -5783,6 +5807,11 @@ export interface components {
|
|||||||
usdAmount: number;
|
usdAmount: number;
|
||||||
/** @description 补单时为 true;订单正常计入充值,但服务端强制金币数为 0 */
|
/** @description 补单时为 true;订单正常计入充值,但服务端强制金币数为 0 */
|
||||||
isMakeup?: boolean;
|
isMakeup?: boolean;
|
||||||
|
/**
|
||||||
|
* Format: int64
|
||||||
|
* @description 订单业务时间;未传时服务端默认为当前时间
|
||||||
|
*/
|
||||||
|
orderDateMs?: number;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
providerCode: "mifapay" | "v5pay" | "usdt";
|
providerCode: "mifapay" | "v5pay" | "usdt";
|
||||||
externalOrderNo: string;
|
externalOrderNo: string;
|
||||||
@ -5860,6 +5889,11 @@ export interface components {
|
|||||||
chain?: "TRON" | "BSC";
|
chain?: "TRON" | "BSC";
|
||||||
usdAmount?: number;
|
usdAmount?: number;
|
||||||
usdMinorAmount?: number;
|
usdMinorAmount?: number;
|
||||||
|
/**
|
||||||
|
* Format: int64
|
||||||
|
* @description 订单业务时间,用于三方凭证按日期查询
|
||||||
|
*/
|
||||||
|
orderDateMs?: number;
|
||||||
providerCurrencyCode?: string;
|
providerCurrencyCode?: string;
|
||||||
providerAmountMinor?: number;
|
providerAmountMinor?: number;
|
||||||
};
|
};
|
||||||
@ -7570,7 +7604,9 @@ export interface operations {
|
|||||||
listBanners: {
|
listBanners: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7582,7 +7618,9 @@ export interface operations {
|
|||||||
createBanner: {
|
createBanner: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7594,7 +7632,9 @@ export interface operations {
|
|||||||
updateBanner: {
|
updateBanner: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
banner_id: number;
|
banner_id: number;
|
||||||
};
|
};
|
||||||
@ -7608,7 +7648,9 @@ export interface operations {
|
|||||||
deleteBanner: {
|
deleteBanner: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
banner_id: number;
|
banner_id: number;
|
||||||
};
|
};
|
||||||
@ -7622,7 +7664,9 @@ export interface operations {
|
|||||||
listSplashScreens: {
|
listSplashScreens: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7634,7 +7678,9 @@ export interface operations {
|
|||||||
createSplashScreen: {
|
createSplashScreen: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7646,7 +7692,9 @@ export interface operations {
|
|||||||
updateSplashScreen: {
|
updateSplashScreen: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
splash_id: number;
|
splash_id: number;
|
||||||
};
|
};
|
||||||
@ -7660,7 +7708,9 @@ export interface operations {
|
|||||||
deleteSplashScreen: {
|
deleteSplashScreen: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
splash_id: number;
|
splash_id: number;
|
||||||
};
|
};
|
||||||
@ -7674,7 +7724,9 @@ export interface operations {
|
|||||||
listPopups: {
|
listPopups: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7686,7 +7738,9 @@ export interface operations {
|
|||||||
createPopup: {
|
createPopup: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7698,7 +7752,9 @@ export interface operations {
|
|||||||
updatePopup: {
|
updatePopup: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
popup_id: number;
|
popup_id: number;
|
||||||
};
|
};
|
||||||
@ -7712,7 +7768,9 @@ export interface operations {
|
|||||||
deletePopup: {
|
deletePopup: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
popup_id: number;
|
popup_id: number;
|
||||||
};
|
};
|
||||||
@ -7812,7 +7870,9 @@ export interface operations {
|
|||||||
listExploreTabs: {
|
listExploreTabs: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7824,7 +7884,9 @@ export interface operations {
|
|||||||
createExploreTab: {
|
createExploreTab: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7836,7 +7898,9 @@ export interface operations {
|
|||||||
updateExploreTab: {
|
updateExploreTab: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
tab_id: number;
|
tab_id: number;
|
||||||
};
|
};
|
||||||
@ -7850,7 +7914,9 @@ export interface operations {
|
|||||||
deleteExploreTab: {
|
deleteExploreTab: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
tab_id: number;
|
tab_id: number;
|
||||||
};
|
};
|
||||||
@ -7864,7 +7930,9 @@ export interface operations {
|
|||||||
listAppVersions: {
|
listAppVersions: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7876,7 +7944,9 @@ export interface operations {
|
|||||||
createAppVersion: {
|
createAppVersion: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path?: never;
|
path?: never;
|
||||||
cookie?: never;
|
cookie?: never;
|
||||||
};
|
};
|
||||||
@ -7888,7 +7958,9 @@ export interface operations {
|
|||||||
updateAppVersion: {
|
updateAppVersion: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
version_id: number;
|
version_id: number;
|
||||||
};
|
};
|
||||||
@ -7902,7 +7974,9 @@ export interface operations {
|
|||||||
deleteAppVersion: {
|
deleteAppVersion: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
header?: never;
|
header: {
|
||||||
|
"X-App-Code": components["parameters"]["AppCodeHeader"];
|
||||||
|
};
|
||||||
path: {
|
path: {
|
||||||
version_id: number;
|
version_id: number;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user