feat(auth): enhance authentication flow with login page configuration and navigation updates

This commit is contained in:
2026-02-24 20:03:40 +08:00
parent fcd914b3c2
commit d88195fa51
4 changed files with 70 additions and 20 deletions

View File

@@ -4,10 +4,8 @@ import { useShallow } from "zustand/shallow"
import { LogIn, LockKeyhole } from "lucide-react" import { LogIn, LockKeyhole } from "lucide-react"
export { BaseHeader } from './modules/BaseHeader' export { BaseHeader } from './modules/BaseHeader'
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useLocation } from '@tanstack/react-router'; import { useLocation, useNavigate } from '@tanstack/react-router';
const openLinkList = [
'/login'
]
type Props = { type Props = {
children?: React.ReactNode, children?: React.ReactNode,
@@ -17,16 +15,16 @@ export const AuthProvider = ({ children, mustLogin }: Props) => {
const store = useLayoutStore(useShallow(state => ({ const store = useLayoutStore(useShallow(state => ({
init: state.init, init: state.init,
me: state.me, me: state.me,
openLinkList: state.openLinkList,
}))); })));
useEffect(() => { useEffect(() => {
store.init() store.init()
}, []) }, [])
const location = useLocation() const location = useLocation()
const navigate = useNavigate();
const isOpen = useMemo(() => { const isOpen = useMemo(() => {
console.log('location.pathname', location.pathname, openLinkList) return store.openLinkList.some(item => location.pathname.startsWith(item))
return openLinkList.some(item => location.pathname.startsWith(item))
}, [location.pathname]) }, [location.pathname])
console.log('AuthProvider', { location, isOpen, me: store.me })
const loginUrl = '/root/login/?redirect=' + encodeURIComponent(window.location.href); const loginUrl = '/root/login/?redirect=' + encodeURIComponent(window.location.href);
if (mustLogin && !store.me && !isOpen) { if (mustLogin && !store.me && !isOpen) {
return ( return (
@@ -42,7 +40,8 @@ export const AuthProvider = ({ children, mustLogin }: Props) => {
<div <div
className="inline-flex items-center justify-center gap-2 w-full px-6 py-2.5 rounded-lg bg-foreground text-background text-sm font-medium transition-opacity hover:opacity-80 active:opacity-70" className="inline-flex items-center justify-center gap-2 w-full px-6 py-2.5 rounded-lg bg-foreground text-background text-sm font-medium transition-opacity hover:opacity-80 active:opacity-70"
onClick={() => { onClick={() => {
window.open(loginUrl, '_self') // window.open(loginUrl, '_blank');
navigate({ to: '/login' });
}} }}
> >
<LogIn className="w-4 h-4" /> <LogIn className="w-4 h-4" />

View File

@@ -1,5 +1,5 @@
import { Home, User, LogIn, LogOut } from 'lucide-react'; import { Home, User, LogIn, LogOut } from 'lucide-react';
import { Link } from '@tanstack/react-router' import { Link, useNavigate } from '@tanstack/react-router'
import { useLayoutStore } from '../store'; import { useLayoutStore } from '../store';
import { useShallow } from 'zustand/shallow'; import { useShallow } from 'zustand/shallow';
import { useMemo } from 'react'; import { useMemo } from 'react';
@@ -9,12 +9,12 @@ export const BaseHeader = (props: { main?: React.ComponentType | null }) => {
me: state.me, me: state.me,
clearMe: state.clearMe, clearMe: state.clearMe,
}))); })));
const navigate = useNavigate();
const meInfo = useMemo(() => { const meInfo = useMemo(() => {
if (!store.me) { if (!store.me) {
return ( return (
<button <button
onClick={() => window.open('/root/login/?redirect=' + encodeURIComponent(window.location.href), '_self')} onClick={() => navigate({ to: '/login' })}
className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer" className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors cursor-pointer"
> >
<LogIn className="w-4 h-4" /> <LogIn className="w-4 h-4" />
@@ -49,7 +49,7 @@ export const BaseHeader = (props: { main?: React.ComponentType | null }) => {
}, [store.me, store.clearMe]) }, [store.me, store.clearMe])
return ( return (
<> <>
<div className="flex gap-2 text-lg w-full h-12 items-center justify-between"> <div className="flex gap-2 text-lg w-full h-12 items-center justify-between bg-gray-200">
<div className='px-2'> <div className='px-2'>
<Link <Link
to="/" to="/"

View File

@@ -29,7 +29,8 @@ export const LoginComponent = ({ onLoginSuccess }: { onLoginSuccess: () => void
} }
export const App = () => { export const App = () => {
const store = useLayoutStore(useShallow((state) => ({ const store = useLayoutStore(useShallow((state) => ({
init: state.init init: state.init,
loginPageConfig: state.loginPageConfig,
}))); })));
useEffect(() => { useEffect(() => {
checkPluginLogin(); checkPluginLogin();
@@ -39,11 +40,42 @@ export const App = () => {
await store.init() await store.init()
navigate({ to: '/' }) navigate({ to: '/' })
}; };
return <div className='w-full h-full'> const { title, subtitle, footer } = store.loginPageConfig;
<div className='w-md mx-auto flex mt-20'> return (
<div className='w-full h-full relative overflow-hidden bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900'>
{/* 背景装饰 - 圆形光晕 */}
<div className='absolute top-1/4 -left-32 w-96 h-96 bg-purple-500/30 rounded-full blur-3xl'></div>
<div className='absolute bottom-1/4 -right-32 w-96 h-96 bg-blue-500/30 rounded-full blur-3xl'></div>
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-indigo-500/20 rounded-full blur-3xl'></div>
{/* 背景装饰 - 网格图案 */}
<div className='absolute inset-0 opacity-[0.03] bg-[linear-gradient(rgba(255,255,255,0.1)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.1)_1px,transparent_1px)] bg-[size:50px_50px]'></div>
{/* 顶部装饰文字 */}
<div className='absolute top-10 left-0 right-0 text-center'>
<h1 className='text-4xl font-bold text-white/90 tracking-wider'>{title}</h1>
<p className='mt-2 text-white/50 text-sm tracking-widest'>{subtitle}</p>
</div>
{/* 登录卡片容器 */}
<div className='w-full h-full flex items-center justify-center p-8'>
<div className='relative'>
{/* 卡片外圈光效 */}
<div className='absolute -inset-1 bg-gradient-to-r from-purple-500 via-blue-500 to-indigo-500 rounded-2xl blur opacity-30'></div>
{/* 登录组件容器 */}
<div className='relative bg-slate-900/80 backdrop-blur-xl rounded-2xl border border-white/10 shadow-2xl overflow-hidden'>
<LoginComponent onLoginSuccess={handleLoginSuccess} /> <LoginComponent onLoginSuccess={handleLoginSuccess} />
</div> </div>
</div> </div>
</div>
{/* 底部装饰 */}
<div className='absolute bottom-6 left-0 right-0 text-center'>
<p className='text-white/30 text-xs'>{footer}</p>
</div>
</div>
)
} }
export default App; export default App;

View File

@@ -27,6 +27,12 @@ export type LayoutStore = {
init: () => Promise<void>; init: () => Promise<void>;
openLinkList: string[]; openLinkList: string[];
setOpenLinkList: (openLinkList: string[]) => void; setOpenLinkList: (openLinkList: string[]) => void;
loginPageConfig: {
title: string;
subtitle: string;
footer: string;
};
setLoginPageConfig: (config: Partial<LayoutStore['loginPageConfig']>) => void;
}; };
export const useLayoutStore = create<LayoutStore>((set, get) => ({ export const useLayoutStore = create<LayoutStore>((set, get) => ({
open: false, open: false,
@@ -60,15 +66,28 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
isAdmin: false, isAdmin: false,
setIsAdmin: (isAdmin) => set({ isAdmin }), setIsAdmin: (isAdmin) => set({ isAdmin }),
init: async () => { init: async () => {
const token = await queryLogin.checkTokenValid() const token = await queryLogin.getToken();
if (token) { if (token) {
const user = await queryLogin.checkLocalUser() as UserInfo; set({ me: {} })
const me = await queryLogin.getMe();
// const user = await queryLogin.checkLocalUser() as UserInfo;
const user = me.code === 200 ? me.data : undefined;
if (user) { if (user) {
set({ me: user }); set({ me: user });
set({ isAdmin: user.orgs?.includes?.('admin') || false }); set({ isAdmin: user.orgs?.includes?.('admin') || false });
} else {
set({ me: undefined, isAdmin: false });
} }
} }
}, },
openLinkList: ['/login'], openLinkList: ['/login'],
setOpenLinkList: (openLinkList) => set({ openLinkList }), setOpenLinkList: (openLinkList) => set({ openLinkList }),
loginPageConfig: {
title: '可视化管理平台',
subtitle: '让工具和智能化触手可及',
footer: '欢迎使用可视化管理平台 · 连接您的工具',
},
setLoginPageConfig: (config) => set((state) => ({
loginPageConfig: { ...state.loginPageConfig, ...config },
})),
})); }));