diff --git a/frontend/react-shadcn/pc/src/contexts/AuthContext.tsx b/frontend/react-shadcn/pc/src/contexts/AuthContext.tsx index 294521e..9268324 100644 --- a/frontend/react-shadcn/pc/src/contexts/AuthContext.tsx +++ b/frontend/react-shadcn/pc/src/contexts/AuthContext.tsx @@ -7,7 +7,8 @@ interface AuthContextType { token: string | null isAuthenticated: boolean isLoading: boolean - login: (email: string, password: string) => Promise + login: (account: string, password: string) => Promise + loginWithToken: (token: string) => void logout: () => void } @@ -37,29 +38,74 @@ export function AuthProvider({ children }: { children: ReactNode }) { setIsLoading(false) }, []) - const login = async (email: string, password: string) => { + const login = async (account: string, password: string) => { try { - const response = await apiClient.login({ email, password }) + const response = await apiClient.login({ account, password }) if (response.success && response.token) { setToken(response.token) - // Mock user data - in real app, fetch from API - const userData: User = { - id: 1, - username: email.split('@')[0], - email, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + // 解析 JWT 获取用户信息 + try { + const payload = JSON.parse(atob(response.token.split('.')[1])) + const userData: User = { + id: payload.userId || 0, + username: payload.username || account, + email: '', + role: payload.role || 'user', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + setUser(userData) + localStorage.setItem('user', JSON.stringify(userData)) + } catch { + const userData: User = { + id: 0, + username: account, + email: '', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + setUser(userData) + localStorage.setItem('user', JSON.stringify(userData)) } - setUser(userData) - localStorage.setItem('user', JSON.stringify(userData)) } } catch (error) { throw error } } + const loginWithToken = (ssoToken: string) => { + localStorage.setItem('token', ssoToken) + setToken(ssoToken) + + // 解析 JWT 获取用户信息 + try { + const payload = JSON.parse(atob(ssoToken.split('.')[1])) + const userData: User = { + id: payload.userId || 0, + username: payload.username || '', + email: payload.email || '', + role: payload.role || 'user', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + setUser(userData) + localStorage.setItem('user', JSON.stringify(userData)) + } catch { + // JWT 解析失败,使用占位数据 + const userData: User = { + id: 0, + username: 'SSO User', + email: '', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + setUser(userData) + localStorage.setItem('user', JSON.stringify(userData)) + } + } + const logout = () => { apiClient.logout() setToken(null) @@ -73,6 +119,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { isAuthenticated: !!token, isLoading, login, + loginWithToken, logout, } diff --git a/frontend/react-shadcn/pc/src/pages/LoginPage.tsx b/frontend/react-shadcn/pc/src/pages/LoginPage.tsx index ccea283..13aa2fd 100644 --- a/frontend/react-shadcn/pc/src/pages/LoginPage.tsx +++ b/frontend/react-shadcn/pc/src/pages/LoginPage.tsx @@ -1,26 +1,56 @@ -import { useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { Mail, Lock, AlertCircle, Zap } from 'lucide-react' +import { useState, useEffect } from 'react' +import { useNavigate, useSearchParams } from 'react-router-dom' +import { UserRound, Lock, AlertCircle, Eye, EyeOff, ArrowRight, Shield, Cpu, BarChart3 } from 'lucide-react' import { useAuth } from '@/contexts/AuthContext' +import { apiClient } from '@/services/api' import { Input } from '@/components/ui/Input' import { Button } from '@/components/ui/Button' -import { Card } from '@/components/ui/Card' export function LoginPage() { - const [email, setEmail] = useState('') + const [account, setAccount] = useState('') const [password, setPassword] = useState('') + const [showPassword, setShowPassword] = useState(false) const [error, setError] = useState('') const [isLoading, setIsLoading] = useState(false) + const [ssoLoading, setSsoLoading] = useState(false) + const [mounted, setMounted] = useState(false) const navigate = useNavigate() + const [searchParams] = useSearchParams() const { login } = useAuth() + useEffect(() => { + setMounted(true) + // 检查 SSO 错误参数 + const ssoError = searchParams.get('error') + if (ssoError === 'sso_failed') { + setError('SSO 登录失败,请重试') + } + }, [searchParams]) + + const handleSSOLogin = async () => { + setSsoLoading(true) + setError('') + try { + const res = await apiClient.getSSOLoginUrl() + if (res.login_url) { + window.location.href = res.login_url + } else { + setError('获取 SSO 登录链接失败') + setSsoLoading(false) + } + } catch { + setError('SSO 服务不可用') + setSsoLoading(false) + } + } + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') setIsLoading(true) try { - await login(email, password) + await login(account, password) navigate('/dashboard') } catch (err) { setError(err instanceof Error ? err.message : '登录失败') @@ -30,106 +60,283 @@ export function LoginPage() { } return ( -
- {/* Animated background elements */} -
-
-
-
+
+ {/* Left Panel - Brand Showcase (visible from md breakpoint) */} +
+ {/* Gradient background */} +
- {/* Decorative grid lines */} -
-
-
-
-
-
+ {/* Subtle pattern overlay */} +
- {/* Main content */} -
- {/* Logo and title */} -
-
-
- + {/* Floating orbs */} +
+
+
+ + {/* Content */} +
+
+
+ +
+ + BASE +
-

- BASE. +

+ +
+

+ 构建下一代 +
+ 智能管理平台

-

管理面板登录

+

+ 安全、高效、可扩展的全栈开发脚手架,为您的业务赋能 +

+ + {/* Feature highlights */} +
+ {[ + { icon: Shield, text: 'JWT 认证与权限管理' }, + { icon: BarChart3, text: '实时数据仪表盘' }, + { icon: Cpu, text: 'Go + React 全栈架构' }, + ].map(({ icon: Icon, text }) => ( +
+
+ +
+ {text} +
+ ))} +
+
+ + {/* Bottom */} +
+

+ © 2026 Base System. All rights reserved. +

+
+
+ + {/* Right Panel - Login Form */} +
+ {/* Subtle background effects for right panel */} +
+
+
- {/* Login form */} - -
-
+
+ {/* Mobile logo - only visible below md */} +
+
+
+ +
+ + BASE + +
+
+ + {/* Form header */} +
+

+ 欢迎回来 +

+

+ 请输入您的账号信息登录系统 +

+
+ + {/* Login card */} +
+ setEmail(e.target.value)} + type="text" + label="手机号 / 用户名" + placeholder="请输入手机号或用户名" + value={account} + onChange={(e) => setAccount(e.target.value)} required - leftIcon={} + leftIcon={} disabled={isLoading} /> setPassword(e.target.value)} required leftIcon={} + rightIcon={ + + } disabled={isLoading} /> -
- {error && ( -
- - {error} -
- )} - - - -
-

- 还没有账号?{' '} - -

+ + {error} +
+ )} + + + + + {/* Divider */} +
+
+
+
+
+ + SSO 单点登录 + +
- - - {/* Footer info */} -
-

- © 2026 Base System. All rights reserved. -

+ {/* SSO Buttons */} +
+ + + + + +
+ + {/* Register link */} +

+ 还没有账号?{' '} + +

+
+ + {/* Mobile footer */} +
+

+ © 2026 Base System. All rights reserved. +

+
- - {/* Corner decorations */} -
-
-
-
) } diff --git a/frontend/react-shadcn/pc/src/types/index.ts b/frontend/react-shadcn/pc/src/types/index.ts index b3a3658..00f7015 100644 --- a/frontend/react-shadcn/pc/src/types/index.ts +++ b/frontend/react-shadcn/pc/src/types/index.ts @@ -20,6 +20,9 @@ export interface User { email: string phone?: string avatar?: string + role?: string + source?: string + remark?: string createdAt: string updatedAt: string } @@ -28,15 +31,15 @@ export interface UserInfo extends User {} // Request Types export interface LoginRequest { - email: string + account: string password: string } export interface RegisterRequest { username: string - email: string password: string - phone?: string + phone: string + email?: string } export interface CreateUserRequest { @@ -44,6 +47,8 @@ export interface CreateUserRequest { email: string password: string phone?: string + role?: string + remark?: string } export interface UpdateUserRequest { @@ -51,6 +56,8 @@ export interface UpdateUserRequest { email?: string phone?: string avatar?: string + role?: string + remark?: string } export interface UserListRequest {