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

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 { User, Mail, Phone, Building, Briefcase, Lock, Save, Shield } from 'lucide-react';
import { toast } from 'sonner';
import { useAuth } from '@/components/auth/AuthContext';
export default function PersonalInfo() {
const { user } = useAuth();
const [profile, setProfile] = useState<UserProfile>({
id: 'user-1',
username: 'admin',
name: '系统管理员',
email: 'admin@smart-agriculture.com',
phone: '13800138000',
id: user?.id || '',
username: user?.username || '',
name: user?.realName || '',
email: user?.email || '',
phone: user?.phone || '',
avatar: '',
gender: 'male',
birthday: '1990-01-01',
department: '技术部',
position: '系统管理员',
enterpriseId: 'ent-1',
enterpriseName: '智慧农业科技有限公司',
roleIds: ['role-1'],
roleNames: ['超级管理员'],
bio: '负责系统整体架构和技术管理',
address: '北京市海淀区中关村大街1号',
createdAt: '2024-01-01T00:00:00',
lastLoginTime: '2024-10-14T09:30:00',
lastLoginIp: '192.168.1.100',
gender: '',
birthday: '',
department: '',
position: '',
enterpriseId: user?.enterpriseId || '',
enterpriseName: user?.enterpriseName || '',
roleIds: [],
roleNames: user?.is_superuser ? ['超级管理员'] : ['普通用户'],
bio: '',
address: '',
createdAt: user?.createdAt || '',
lastLoginTime: '',
lastLoginIp: '',
});
const [showPasswordDialog, setShowPasswordDialog] = useState(false);
@@ -50,6 +53,24 @@ export default function PersonalInfo() {
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 data = localStorage.getItem('smart_agriculture_user_profile');
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() {
const handleRefresh = () => {
window.location.reload();
};
const handleGoBack = () => {
window.history.back();
};
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="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="min-h-screen relative flex items-center justify-center">
{/* 主要内容 */}
<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>
<p className="text-gray-600 mb-8">
访
</p>
<a
href="/"
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
>
</CardTitle>
<CardDescription className="text-lg text-muted-foreground mt-2">
访
</CardDescription>
</CardHeader>
<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>
)
);
}

View File

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

View File

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

View File

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

View File

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