Compare commits

...

2 Commits

Author SHA1 Message Date
9898a5ea38 生产管理系统 404页面兜底 2025-11-03 14:51:06 +08:00
73c41b76ab 生产管理系统 - 路由错误修复 2025-11-03 11:30:06 +08:00
11 changed files with 1438 additions and 1400 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ import { useAuth } from '@/components/auth/AuthContext';
import { authReducer, initialAuthState, AuthState, AuthAction } from './authReducer'; import { authReducer, initialAuthState, AuthState, AuthAction } from './authReducer';
import { getCaptchaApiV1AuthCaptchaGet, loginApiV1AuthLoginPost } from '@/lib/api/sdk.gen'; import { getCaptchaApiV1AuthCaptchaGet, loginApiV1AuthLoginPost } from '@/lib/api/sdk.gen';
import type { CaptchaResponse } from '@/lib/api/types.gen'; import type { CaptchaResponse } from '@/lib/api/types.gen';
import {PERSONAL_CELTRAL_PAGE} from "@/config/constants"
interface LoginFormProps { interface LoginFormProps {
onRegisterClick: () => void; onRegisterClick: () => void;
} }
@@ -150,7 +150,7 @@ export function LoginForm({ onRegisterClick }: LoginFormProps) {
login(userData); login(userData);
toast.success('登录成功!正在跳转...'); toast.success('登录成功!正在跳转...');
// 跳转到个人中心页面 // 跳转到个人中心页面
window.location.href = '/central-config/personal-center/personal-info'; window.location.href = PERSONAL_CELTRAL_PAGE;
} else { } else {
dispatch({ type: 'SET_ERROR', payload: '登录失败,请检查用户名和密码' }); dispatch({ type: 'SET_ERROR', payload: '登录失败,请检查用户名和密码' });
toast.error('登录失败,请检查用户名和密码'); toast.error('登录失败,请检查用户名和密码');
@@ -279,6 +279,7 @@ export function LoginForm({ onRegisterClick }: LoginFormProps) {
value={state.passwordForm.captcha} value={state.passwordForm.captcha}
onChange={(value) => dispatch({ type: 'UPDATE_PASSWORD_FORM', payload: { captcha: value } })} onChange={(value) => dispatch({ type: 'UPDATE_PASSWORD_FORM', payload: { captcha: value } })}
onCaptchaChange={(captchaData) => setPasswordCaptchaData(captchaData)} onCaptchaChange={(captchaData) => setPasswordCaptchaData(captchaData)}
instanceId="password-login"
className="mt-2" className="mt-2"
/> />
</div> </div>
@@ -357,6 +358,7 @@ export function LoginForm({ onRegisterClick }: LoginFormProps) {
<CaptchaInput <CaptchaInput
value={state.phoneForm.captcha} value={state.phoneForm.captcha}
onChange={(value) => dispatch({ type: 'UPDATE_PHONE_FORM', payload: { captcha: value } })} onChange={(value) => dispatch({ type: 'UPDATE_PHONE_FORM', payload: { captcha: value } })}
instanceId="phone-login"
className="mt-2" className="mt-2"
/> />
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -17,13 +17,9 @@ export default function NotFound() {
}; };
return ( return (
<div className="min-h-screen relative flex items-center justify-center"> <Card className="backdrop-blur-md absolute inset-x-6 top-6 bottom-6
flex-1 min-h-0">
{/* 主要内容 */} <CardHeader className="text-center p-0">
<div className="relative z-10 w-full max-w-2xl mx-auto p-6">
{/* 404 状态卡片 */}
<Card className="backdrop-blur-md bg-background/80 border-border/50 shadow-2xl">
<CardHeader className="text-center pb-2">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="relative"> <div className="relative">
<div className="text-8xl font-bold text-destructive animate-pulse">404</div> <div className="text-8xl font-bold text-destructive animate-pulse">404</div>
@@ -43,7 +39,7 @@ export default function NotFound() {
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-6"> <CardContent className="space-y-6 p-0">
{/* 错误信息提示 */} {/* 错误信息提示 */}
<Alert> <Alert>
<Search className="h-4 w-4" /> <Search className="h-4 w-4" />
@@ -100,7 +96,7 @@ export default function NotFound() {
</div> </div>
</CardContent> </CardContent>
<CardFooter className="flex flex-col space-y-4 pt-6 border-t"> <CardFooter className="flex flex-col space-y-4 pt-6 border-t p-0">
{/* 帮助信息 */} {/* 帮助信息 */}
<div className="text-center text-sm text-muted-foreground"> <div className="text-center text-sm text-muted-foreground">
<p></p> <p></p>
@@ -113,7 +109,5 @@ export default function NotFound() {
</div> </div>
</CardFooter> </CardFooter>
</Card> </Card>
</div>
</div>
); );
} }

View File

@@ -4,7 +4,7 @@
export default function HomePage() { export default function HomePage() {
return ( return (
<div> <div>
</div> </div>
); );
} }

View File

@@ -236,24 +236,54 @@ export function AuthProvider({ children }: AuthProviderProps) {
} }
}; };
// 初始化时检查 localStorage并验证用户 // 统一的认证初始化和路由监听
React.useEffect(() => { React.useEffect(() => {
// 检查是否有存储的用户信息和 token并设置 cookie // 处理当前路径的逻辑
const storedUser = localStorage.getItem('user'); const handleCurrentPath = () => {
if (storedUser) { const currentPath = typeof window !== 'undefined' ? window.location.pathname : '';
try {
const userData = JSON.parse(storedUser);
if (userData.token && !document.cookie.includes('auth-token')) {
setTokenCookie(userData.token);
console.log('🔄 初始化时设置cookie');
}
} catch (error) {
console.error('解析存储用户信息失败:', error);
localStorage.removeItem('user');
}
}
validateUser(); // 如果在登录页面,跳过验证,直接设置为非加载状态
if (currentPath.startsWith('/login')) {
console.log('📄 检测到登录页面,跳过用户验证');
stopTokenRefresh(); // 停止任何正在进行的 token 刷新
setLoading(false);
return;
}
// 检查是否有存储的用户信息和 token并设置 cookie
const storedUser = localStorage.getItem('user');
if (storedUser) {
try {
const userData = JSON.parse(storedUser);
if (userData.token && !document.cookie.includes('auth-token')) {
setTokenCookie(userData.token);
console.log('🔄 初始化时设置cookie');
}
} catch (error) {
console.error('解析存储用户信息失败:', error);
localStorage.removeItem('user');
}
}
// 只有在非登录页面才进行用户验证
validateUser();
};
// 立即处理当前路径
handleCurrentPath();
// 监听路由变化
const handlePathChange = () => {
console.log('🔄 路由变化,重新检查认证状态');
handleCurrentPath();
};
// 监听 popstate 事件(浏览器前进后退)
window.addEventListener('popstate', handlePathChange);
return () => {
window.removeEventListener('popstate', handlePathChange);
};
}, []); }, []);
// 清理定时器 // 清理定时器

View File

@@ -52,11 +52,6 @@ export function ClientAuthInterceptor({ children }: ClientAuthInterceptorProps)
return <>{children}</>; return <>{children}</>;
} }
// 如果未认证且不在认证页面,显示跳转状态
if (!isAuthenticated) {
return <LoadingScreen variant="redirect" />;
}
// 认证通过,渲染子组件 // 认证通过,渲染子组件
return <>{children}</>; return <>{children}</>;
} }

View File

@@ -5,10 +5,9 @@ import { Sprout, Map, Clipboard, Package, Brain, Droplets, Settings } from 'luci
import { MessageBell } from './components/MessageBell'; import { MessageBell } from './components/MessageBell';
import { UserProfile } from './components/UserProfile'; import { UserProfile } from './components/UserProfile';
import { ThemeToggle } from './ThemeToggle'; import { ThemeToggle } from './ThemeToggle';
import { AuthProvider } from './components/auth/AuthContext';
import { useElementHeight } from '@/hooks/useElementHeight'; import { useElementHeight } from '@/hooks/useElementHeight';
import { useViewHeight } from '@/hooks/useViewHeight'; import { useViewHeight } from '@/hooks/useViewHeight';
import { usePathname } from 'next/navigation'; import { usePathname, useRouter } from 'next/navigation';
import { useRef, useEffect, useState } from 'react'; import { useRef, useEffect, useState } from 'react';
// 注释掉 Accordion 相关导入,因为不再需要二级菜单 // 注释掉 Accordion 相关导入,因为不再需要二级菜单
// import { // import {
@@ -69,6 +68,7 @@ const Navbar1 = ({ navbarData }: Navbar1Props) => {
const menu = navbarData.menu const menu = navbarData.menu
const auth = navbarData.auth const auth = navbarData.auth
const pathname = usePathname() const pathname = usePathname()
const router = useRouter()
const containerStyle = { const containerStyle = {
maxWidth:"100%",marginLeft:"0px",marginRight:"0px",paddingLeft:"1rem",paddingRight:"0rem" maxWidth:"100%",marginLeft:"0px",marginRight:"0px",paddingLeft:"1rem",paddingRight:"0rem"
} }
@@ -107,8 +107,7 @@ const Navbar1 = ({ navbarData }: Navbar1Props) => {
}; };
return ( return (
<AuthProvider> <section className="py-4" ref={elementRef}>
<section className="py-4" ref={elementRef}>
<div className="container" style = {containerStyle}> <div className="container" style = {containerStyle}>
{/* Desktop Menu */} {/* Desktop Menu */}
<nav className="hidden justify-between lg:flex"> <nav className="hidden justify-between lg:flex">
@@ -151,7 +150,7 @@ const Navbar1 = ({ navbarData }: Navbar1Props) => {
`}</style> `}</style>
<NavigationMenu> <NavigationMenu>
<NavigationMenuList className="flex gap-1 min-w-max"> <NavigationMenuList className="flex gap-1 min-w-max">
{menu.map((item) => renderMenuItem(item, isMenuActive))} {menu.map((item) => renderMenuItem(item, isMenuActive, router))}
</NavigationMenuList> </NavigationMenuList>
</NavigationMenu> </NavigationMenu>
</div> </div>
@@ -195,7 +194,7 @@ const Navbar1 = ({ navbarData }: Navbar1Props) => {
<div className="flex flex-col gap-6 p-4"> <div className="flex flex-col gap-6 p-4">
{/* 简化移动端菜单,不再使用 Accordion */} {/* 简化移动端菜单,不再使用 Accordion */}
<div className="flex w-full flex-col gap-4"> <div className="flex w-full flex-col gap-4">
{menu.map((item) => renderMobileMenuItem(item, isMenuActive))} {menu.map((item) => renderMobileMenuItem(item, isMenuActive, router))}
</div> </div>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
@@ -216,11 +215,10 @@ const Navbar1 = ({ navbarData }: Navbar1Props) => {
</div> </div>
</div> </div>
</section> </section>
</AuthProvider>
); );
}; };
const renderMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean) => { const renderMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean, router: any) => {
// 注释掉二级菜单相关代码,项目不需要二级菜单 // 注释掉二级菜单相关代码,项目不需要二级菜单
// if (item.items) { // if (item.items) {
// return ( // return (
@@ -238,14 +236,20 @@ const renderMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean)
// ); // );
// } // }
const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
router.push(item.url);
};
return ( return (
<NavigationMenuItem key={item.title}> <NavigationMenuItem key={item.title}>
<NavigationMenuLink <NavigationMenuLink
href={item.url} href={item.url}
onClick={handleClick}
data-menu-item="true" data-menu-item="true"
data-menu-url={item.url} data-menu-url={item.url}
className={` className={`
inline-flex h-10 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium gap-2 whitespace-nowrap relative inline-flex h-10 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium gap-2 whitespace-nowrap relative cursor-pointer
${isMenuActive(item.url) ${isMenuActive(item.url)
? 'bg-primary/10' ? 'bg-primary/10'
: 'bg-background hover:bg-muted hover:text-accent-foreground' : 'bg-background hover:bg-muted hover:text-accent-foreground'
@@ -275,7 +279,7 @@ const renderMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean)
); );
}; };
const renderMobileMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean) => { const renderMobileMenuItem = (item: MenuItem, isMenuActive: (url: string) => boolean, router: any) => {
// 注释掉移动端二级菜单相关代码 // 注释掉移动端二级菜单相关代码
// if (item.items) { // if (item.items) {
// return ( // return (
@@ -293,12 +297,17 @@ const renderMobileMenuItem = (item: MenuItem, isMenuActive: (url: string) => boo
// ); // );
// } // }
const handleClick = (e: React.MouseEvent) => {
e.preventDefault();
router.push(item.url);
};
return ( return (
<a <div
key={item.title} key={item.title}
href={item.url} onClick={handleClick}
className={` className={`
text-md font-semibold flex items-center gap-2 p-2 rounded-md transition-colors text-md font-semibold flex items-center gap-2 p-2 rounded-md transition-colors cursor-pointer
${isMenuActive(item.url) ${isMenuActive(item.url)
? 'bg-primary/10 text-primary' ? 'bg-primary/10 text-primary'
: 'hover:bg-muted hover:text-accent-foreground' : 'hover:bg-muted hover:text-accent-foreground'
@@ -316,7 +325,7 @@ const renderMobileMenuItem = (item: MenuItem, isMenuActive: (url: string) => boo
<span className={isMenuActive(item.url) ? 'text-primary' : ''}> <span className={isMenuActive(item.url) ? 'text-primary' : ''}>
{item.title} {item.title}
</span> </span>
</a> </div>
); );
}; };

View File

@@ -63,7 +63,7 @@ export function MainContent({
return ( return (
<div className="flex-1 flex flex-col bg-background h-full"> <div className="flex-1 flex flex-col bg-background h-full">
<div className="flex-1 p-6 min-h-0 content-container"> <div className="flex-1 p-6 min-h-0 content-container relative">
{children} {children}
</div> </div>
</div> </div>

View File

@@ -2,7 +2,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Bell, CheckCircle, X } from 'lucide-react'; import { Bell, CheckCircle, X } from 'lucide-react';
import { useAuth } from './auth/AuthContext';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Badge } from './ui/badge'; import { Badge } from './ui/badge';
import { Button } from './ui/button'; import { Button } from './ui/button';
@@ -25,7 +24,6 @@ interface MessageBellProps {
} }
export function MessageBell({ onMessageClick }: MessageBellProps) { export function MessageBell({ onMessageClick }: MessageBellProps) {
const { authState } = useAuth();
const [showMessages, setShowMessages] = useState(false); const [showMessages, setShowMessages] = useState(false);
const [showMessageDetail, setShowMessageDetail] = useState(false); const [showMessageDetail, setShowMessageDetail] = useState(false);
const [selectedMessage, setSelectedMessage] = useState<Message | null>(null); const [selectedMessage, setSelectedMessage] = useState<Message | null>(null);

View File

@@ -54,4 +54,7 @@ export const THEME = {
warning: '#f59e0b', warning: '#f59e0b',
error: '#ef4444' error: '#ef4444'
} }
} }
// 默认有权限的首页 个人中心
export const PERSONAL_CELTRAL_PAGE = "central-config/personal-center/personal-info"