Skip to content

Commit a21b750

Browse files
authored
增加自定义首页 (#462)
* feat: 增加自定义首页
1 parent 858f48c commit a21b750

File tree

3 files changed

+120
-3
lines changed

3 files changed

+120
-3
lines changed

frontend/src/main.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { store } from "./store";
1010
import { Provider } from "react-redux";
1111
import theme from "./theme";
1212
import {errorConfigStore} from "@/utils/errorConfigStore.ts";
13+
import { setCachedHomePageUrl, getCachedHomePageUrl } from "@/utils/systemParam";
1314
import "@/i18n";
1415

1516
function showLoadingUI() {
@@ -43,10 +44,84 @@ function showLoadingUI() {
4344
`;
4445
}
4546

47+
/**
48+
* 从 localStorage 读取 JWT token
49+
*/
50+
function getAuthToken(): string | null {
51+
const session = localStorage.getItem('session');
52+
if (session) {
53+
try {
54+
return JSON.parse(session).token || null;
55+
} catch {
56+
return null;
57+
}
58+
}
59+
return null;
60+
}
61+
62+
/**
63+
* 自定义首页URL重定向
64+
* 在任何渲染之前检查系统参数 sys.home.page.url,若已配置则立即跳转,确保无闪烁。
65+
* 使用原始 fetch 但携带 JWT token,避免已登录用户仍收到 401。
66+
*/
67+
async function checkHomePageRedirect(): Promise<{ redirected: boolean; authNeeded: boolean }> {
68+
if (window.location.pathname !== '/') {
69+
return { redirected: false, authNeeded: false };
70+
}
71+
72+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
73+
const token = getAuthToken();
74+
if (token) {
75+
headers['Authorization'] = `Bearer ${token}`;
76+
}
77+
78+
try {
79+
const response = await fetch('/api/sys-param/sys.home.page.url', {
80+
method: 'GET',
81+
credentials: 'include',
82+
headers,
83+
});
84+
if (response.ok) {
85+
const result = await response.json();
86+
const url = result?.data?.paramValue?.trim();
87+
if (url) {
88+
setCachedHomePageUrl(url);
89+
window.location.replace(url);
90+
return { redirected: true, authNeeded: false };
91+
}
92+
// 参数存在但值为空 → 管理员已清除,清掉缓存
93+
setCachedHomePageUrl(null);
94+
} else if (response.status === 401) {
95+
// 未登录,尝试从缓存读取
96+
const cachedUrl = getCachedHomePageUrl();
97+
if (cachedUrl) {
98+
window.location.replace(cachedUrl);
99+
return { redirected: true, authNeeded: false };
100+
}
101+
// 未登录且无缓存,需要弹出登录框
102+
return { redirected: false, authNeeded: true };
103+
}
104+
} catch {
105+
// 网络错误等,尝试从缓存读取
106+
const cachedUrl = getCachedHomePageUrl();
107+
if (cachedUrl) {
108+
window.location.replace(cachedUrl);
109+
return { redirected: true, authNeeded: false };
110+
}
111+
}
112+
return { redirected: false, authNeeded: false };
113+
}
114+
46115
async function bootstrap() {
47116
const container = document.getElementById("root");
48117
if (!container) return;
49118

119+
// 在任何 UI 渲染之前检查自定义首页重定向
120+
const { redirected, authNeeded } = await checkHomePageRedirect();
121+
if (redirected) {
122+
return;
123+
}
124+
50125
showLoadingUI();
51126

52127
try {
@@ -72,6 +147,13 @@ async function bootstrap() {
72147
</Provider>
73148
</StrictMode>
74149
);
150+
151+
// 未登录且无缓存时,等 React 挂载后弹出登录框
152+
if (authNeeded) {
153+
setTimeout(() => {
154+
window.dispatchEvent(new CustomEvent('show-login'));
155+
}, 500);
156+
}
75157
}
76158

77159
bootstrap();

frontend/src/pages/Layout/Header.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { User, Globe, LogIn, UserPlus, Sparkles, Shield } from "lucide-react"
2-
import { memo, useState, useEffect } from "react";
2+
import { memo, useState, useEffect, useCallback } from "react";
33
import { NavLink } from "react-router";
44
import { Button, Dropdown, message } from "antd"
55
import type { MenuProps } from 'antd'
66
import { LoginDialog } from "./LoginDialog"
77
import { SignupDialog } from "./SignupDialog"
88
import { post, get } from "@/utils/request.ts";
9+
import { getCachedHomePageUrl, setCachedHomePageUrl } from "@/utils/systemParam";
10+
import { getHomePageUrl } from "@/utils/systemParam";
911
import { useTranslation } from "react-i18next";
1012
import i18n from "@/i18n";
1113

@@ -121,6 +123,19 @@ export function Header() {
121123
setSignupOpen(true);
122124
};
123125

126+
const handleHomeClick = useCallback((e: React.MouseEvent) => {
127+
const homeUrl = getCachedHomePageUrl();
128+
if (homeUrl) {
129+
e.preventDefault();
130+
window.location.href = homeUrl;
131+
}
132+
}, []);
133+
134+
// 已登录时后台刷新缓存,保持与后端同步
135+
useEffect(() => {
136+
getHomePageUrl().then(url => setCachedHomePageUrl(url)).catch(() => {});
137+
}, []);
138+
124139
// 检测是否在 ME 环境
125140
const isSSOAvailable = () => {
126141
const hostname = window.location.hostname;
@@ -269,7 +284,7 @@ export function Header() {
269284
<div className="flex h-14 items-center justify-between px-6">
270285
<div className="flex items-center gap-2">
271286
<div className="flex items-center gap-2">
272-
<NavLink to="/" className="flex items-center gap-2 cursor-pointer">
287+
<NavLink to="/" onClick={handleHomeClick} className="flex items-center gap-2 cursor-pointer">
273288
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center">
274289
<Sparkles className="w-5 h-5 text-white" />
275290
</div>

frontend/src/utils/systemParam.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
/**
2-
* System Parameter API
32
* 系统参数 API 接口
43
*/
4+
5+
// localStorage 缓存 key
6+
const HOME_PAGE_URL_CACHE_KEY = 'datamate:homePageUrl';
7+
8+
/**
9+
* 将首页URL写入缓存
10+
*/
11+
export function setCachedHomePageUrl(url: string | null) {
12+
if (url) {
13+
localStorage.setItem(HOME_PAGE_URL_CACHE_KEY, url);
14+
} else {
15+
localStorage.removeItem(HOME_PAGE_URL_CACHE_KEY);
16+
}
17+
}
18+
19+
/**
20+
* 同步读取缓存的首页URL
21+
*/
22+
export function getCachedHomePageUrl(): string | null {
23+
return localStorage.getItem(HOME_PAGE_URL_CACHE_KEY);
24+
}
525
import { get } from '@/utils/request';
626

727
export interface SysParam {

0 commit comments

Comments
 (0)