生产管理系统 - 拦截器业务逻辑增强

This commit is contained in:
2025-11-01 09:32:26 +08:00
parent a1b3335664
commit e99567e6b3
6 changed files with 185 additions and 56 deletions

View File

@@ -13,28 +13,31 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { UserProfile, PasswordChange } from '@/types/profile'; import { UserProfile, PasswordChange } from '@/types/profile';
import { User, Mail, Phone, Building, Briefcase, Lock, Save, Shield } from 'lucide-react'; import { User, Mail, Phone, Building, Briefcase, Lock, Save, Shield } from 'lucide-react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useAuth } from '@/components/auth/AuthContext';
export default function PersonalInfo() { export default function PersonalInfo() {
const { user } = useAuth();
const [profile, setProfile] = useState<UserProfile>({ const [profile, setProfile] = useState<UserProfile>({
id: 'user-1', id: user?.id || '',
username: 'admin', username: user?.username || '',
name: '系统管理员', name: user?.realName || '',
email: 'admin@smart-agriculture.com', email: user?.email || '',
phone: '13800138000', phone: user?.phone || '',
avatar: '', avatar: '',
gender: 'male', gender: '',
birthday: '1990-01-01', birthday: '',
department: '技术部', department: '',
position: '系统管理员', position: '',
enterpriseId: 'ent-1', enterpriseId: user?.enterpriseId || '',
enterpriseName: '智慧农业科技有限公司', enterpriseName: user?.enterpriseName || '',
roleIds: ['role-1'], roleIds: [],
roleNames: ['超级管理员'], roleNames: user?.is_superuser ? ['超级管理员'] : ['普通用户'],
bio: '负责系统整体架构和技术管理', bio: '',
address: '北京市海淀区中关村大街1号', address: '',
createdAt: '2024-01-01T00:00:00', createdAt: user?.createdAt || '',
lastLoginTime: '2024-10-14T09:30:00', lastLoginTime: '',
lastLoginIp: '192.168.1.100', lastLoginIp: '',
}); });
const [showPasswordDialog, setShowPasswordDialog] = useState(false); const [showPasswordDialog, setShowPasswordDialog] = useState(false);
@@ -50,6 +53,24 @@ export default function PersonalInfo() {
loadProfile(); loadProfile();
}, []); }, []);
// 当用户信息变化时,更新 profile 状态
useEffect(() => {
if (user) {
setProfile(prev => ({
...prev,
id: user.id || '',
username: user.username || '',
name: user.realName || '',
email: user.email || '',
phone: user.phone || '',
enterpriseId: user.enterpriseId || '',
enterpriseName: user.enterpriseName || '',
roleNames: user.is_superuser ? ['超级管理员'] : ['普通用户'],
createdAt: user.createdAt || '',
}));
}
}, [user]);
const loadProfile = () => { const loadProfile = () => {
const data = localStorage.getItem('smart_agriculture_user_profile'); const data = localStorage.getItem('smart_agriculture_user_profile');
if (data) { if (data) {

View File

@@ -1,21 +1,119 @@
'use client';
import "@/styles/globals.css"
import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { Home, ArrowLeft, RefreshCw, Search, AlertTriangle, ExternalLink } from 'lucide-react';
export default function NotFound() { export default function NotFound() {
const handleRefresh = () => {
window.location.reload();
};
const handleGoBack = () => {
window.history.back();
};
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 via-blue-50 to-green-50"> <div className="min-h-screen relative flex items-center justify-center">
<div className="text-center">
<h1 className="text-6xl font-bold text-gray-800 mb-4">404</h1> {/* 主要内容 */}
<h2 className="text-2xl font-semibold text-gray-700 mb-4"> <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="relative">
<div className="text-8xl font-bold text-destructive animate-pulse">404</div>
<div className="absolute -top-2 -right-2">
<Badge variant="destructive" className="animate-bounce">
<AlertTriangle className="w-3 h-3 mr-1" />
Error
</Badge>
</div>
</div>
</div>
<CardTitle className="text-3xl font-bold text-foreground">
</h2> </CardTitle>
<p className="text-gray-600 mb-8"> <CardDescription className="text-lg text-muted-foreground mt-2">
访 访
</p> </CardDescription>
<a </CardHeader>
href="/"
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" <CardContent className="space-y-6">
> {/* 错误信息提示 */}
<Alert>
<Search className="h-4 w-4" />
<AlertTitle></AlertTitle>
<AlertDescription>
访URL是否正确
</AlertDescription>
</Alert>
{/* 可能的原因列表 */}
<div className="space-y-3">
<h4 className="font-semibold text-foreground flex items-center">
<span className="w-2 h-2 bg-warning rounded-full mr-2"></span>
</h4>
<div className="grid gap-2 ml-4">
<div className="flex items-center text-sm text-muted-foreground">
<span className="text-destructive mr-2"></span>
</div>
<div className="flex items-center text-sm text-muted-foreground">
<span className="text-destructive mr-2"></span>
</div>
<div className="flex items-center text-sm text-muted-foreground">
<span className="text-destructive mr-2"></span>
访
</div>
<div className="flex items-center text-sm text-muted-foreground">
<span className="text-destructive mr-2"></span>
</div>
</div>
</div>
{/* 快速操作按钮 */}
<div className="flex flex-col sm:flex-row gap-3 pt-2">
<Button asChild className="flex-1">
<Link href="/">
<Home className="w-4 h-4 mr-2" />
</a> </Link>
</Button>
<Button variant="outline" onClick={handleGoBack} className="flex-1">
<ArrowLeft className="w-4 h-4 mr-2" />
</Button>
<Button variant="outline" onClick={handleRefresh} className="flex-1">
<RefreshCw className="w-4 h-4 mr-2" />
</Button>
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-4 pt-6 border-t">
{/* 帮助信息 */}
<div className="text-center text-sm text-muted-foreground">
<p></p>
</div>
{/* 错误详情 */}
<div className="flex items-center justify-between text-xs text-muted-foreground bg-muted/50 rounded p-2">
<span>错误代码: 404</span>
<span>: {new Date().toLocaleString('zh-CN')}</span>
</div>
</CardFooter>
</Card>
</div> </div>
</div> </div>
) );
} }

View File

@@ -18,7 +18,12 @@ export default function HomePage() {
redirect('/login'); redirect('/login');
} }
// 用户已认证,显示主页内容 // 如果已认证,重定向到个人中心
if (isAuthenticated) {
redirect('/central-config/personal-center/personal-info');
}
// 兜底显示(一般不会执行到这里)
return ( return (
<div></div> <div></div>
); );

View File

@@ -65,6 +65,11 @@ export function AuthProvider({ children }: AuthProviderProps) {
localStorage.removeItem('user'); localStorage.removeItem('user');
removeTokenCookie(); // 清除 cookie removeTokenCookie(); // 清除 cookie
setLoading(false); setLoading(false);
// 跳转到登录页
if (typeof window !== 'undefined') {
window.location.href = '/login';
}
}; };
// 验证当前用户信息 // 验证当前用户信息
@@ -89,9 +94,9 @@ export function AuthProvider({ children }: AuthProviderProps) {
// 更新用户信息(可能包含最新的权限、角色等) // 更新用户信息(可能包含最新的权限、角色等)
setUser({ setUser({
...userData, ...userData,
...response.data.data, // 合并最新的用户信息 ...response.data, // 合并最新的用户信息
}); });
console.log('✅ 用户验证成功,最新用户信息:', response.data.data); console.log('✅ 用户验证成功,最新用户信息:', response.data);
} else { } else {
// Token无效清除用户信息 // Token无效清除用户信息
console.warn('⚠️ Token验证失败清除用户信息'); console.warn('⚠️ Token验证失败清除用户信息');

View File

@@ -20,8 +20,8 @@ export function ClientAuthInterceptor({ children }: ClientAuthInterceptorProps)
const currentPath = window.location.pathname; const currentPath = window.location.pathname;
// 如果已经在登录页面,不需要重定向 // 如果已经在认证页面(包括 /login 开头的所有路径),不需要重定向
if (currentPath === '/login' || currentPath === '/register') { if (currentPath.startsWith('/login')) {
console.log(`📄 已在认证页面,跳过拦截: ${currentPath}`); console.log(`📄 已在认证页面,跳过拦截: ${currentPath}`);
return; return;
} }
@@ -53,8 +53,8 @@ export function ClientAuthInterceptor({ children }: ClientAuthInterceptorProps)
); );
} }
// 如果在认证页面(登录/注册),直接渲染子组件,不需要认证检查 // 如果在认证页面(包括 /login 开头的所有路径),直接渲染子组件,不需要认证检查
if (currentPath === '/login' || currentPath === '/register') { if (currentPath.startsWith('/login')) {
return <>{children}</>; return <>{children}</>;
} }

View File

@@ -2,7 +2,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { User, UserCircle, LogOut } from 'lucide-react'; import { User, UserCircle, LogOut } from 'lucide-react';
import { useAuth } from './auth/AuthContext'; import { useAuth } from '@/components/auth/AuthContext';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
@@ -12,7 +12,7 @@ interface UserProfileProps {
} }
export function UserProfile({ onProfileClick }: UserProfileProps) { export function UserProfile({ onProfileClick }: UserProfileProps) {
const { authState, logout } = useAuth(); const { user, logout } = useAuth();
const [showUserMenu, setShowUserMenu] = useState(false); const [showUserMenu, setShowUserMenu] = useState(false);
const handleProfileClick = () => { const handleProfileClick = () => {
@@ -32,7 +32,7 @@ export function UserProfile({ onProfileClick }: UserProfileProps) {
<PopoverTrigger asChild> <PopoverTrigger asChild>
<Button variant="ghost" className="gap-2"> <Button variant="ghost" className="gap-2">
<User className="w-5 h-5" /> <User className="w-5 h-5" />
<span className="text-sm hidden md:inline">{authState.user?.realName || '用户'}</span> <span className="text-sm hidden md:inline">{user?.realName || '用户'}</span>
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-72 p-0" align="end"> <PopoverContent className="w-72 p-0" align="end">
@@ -42,8 +42,8 @@ export function UserProfile({ onProfileClick }: UserProfileProps) {
<UserCircle className="w-6 h-6" /> <UserCircle className="w-6 h-6" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h4 className="mb-1">{authState.user?.realName}</h4> <h4 className="mb-1">{user?.realName}</h4>
<p className="text-xs text-muted-foreground">{authState.user?.role === 'admin' ? '系统管理员' : '普通用户'}</p> <p className="text-xs text-muted-foreground">{user?.is_superuser ? '系统管理员' : '普通用户'}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -51,30 +51,30 @@ export function UserProfile({ onProfileClick }: UserProfileProps) {
<div className="p-3 space-y-2 text-sm"> <div className="p-3 space-y-2 text-sm">
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">:</span> <span className="text-muted-foreground">:</span>
<span>{authState.user?.username}</span> <span>{user?.username}</span>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">:</span> <span className="text-muted-foreground">:</span>
<span>{authState.user?.phone}</span> <span>{user?.phone}</span>
</div> </div>
{authState.user?.enterpriseName && ( {user?.enterpriseName && (
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">:</span> <span className="text-muted-foreground">:</span>
<span className="truncate max-w-[140px]" title={authState.user?.enterpriseName}> <span className="truncate max-w-[140px]" title={user?.enterpriseName}>
{authState.user?.enterpriseName} {user?.enterpriseName}
</span> </span>
</div> </div>
)} )}
{authState.user?.department && ( {user?.department && (
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">:</span> <span className="text-muted-foreground">:</span>
<span>{authState.user?.department}</span> <span>{user?.department}</span>
</div> </div>
)} )}
{authState.user?.lastLoginTime && ( {user?.lastLoginTime && (
<div className="flex justify-between text-xs"> <div className="flex justify-between text-xs">
<span className="text-muted-foreground">:</span> <span className="text-muted-foreground">:</span>
<span className="text-muted-foreground">{authState.user?.lastLoginTime}</span> <span className="text-muted-foreground">{user?.lastLoginTime}</span>
</div> </div>
)} )}
</div> </div>