From f401a5a10484c7dbd45760d5b0e7ec63cc6da879 Mon Sep 17 00:00:00 2001 From: imtim Date: Tue, 12 May 2026 09:38:51 +0800 Subject: [PATCH] Improve frontend layout and auth routing --- frontend/src/app/dashboard/page.tsx | 112 ++++++++++++--- frontend/src/app/globals.css | 60 ++++++-- frontend/src/app/login/page.tsx | 71 +++++++-- frontend/src/app/page.tsx | 150 +++++++++++++------- frontend/src/components/header.tsx | 35 +++-- frontend/src/components/layout.tsx | 6 +- frontend/src/components/protected-route.tsx | 16 ++- frontend/src/lib/auth-store.ts | 22 ++- 8 files changed, 347 insertions(+), 125 deletions(-) diff --git a/frontend/src/app/dashboard/page.tsx b/frontend/src/app/dashboard/page.tsx index 165217b..80552a9 100644 --- a/frontend/src/app/dashboard/page.tsx +++ b/frontend/src/app/dashboard/page.tsx @@ -3,20 +3,41 @@ import { useEffect, Suspense } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useAuthStore } from '@/lib/auth-store'; -import { getUserInfo } from '@/lib/api'; import Layout from '@/components/layout'; +import { Activity, BadgeCheck, Boxes, ShieldCheck } from 'lucide-react'; + +const cards = [ + { + title: '欢迎回来', + value: 'IMTIM Portal', + description: '您已成功登录企业门户', + icon: ShieldCheck, + }, + { + title: '账户状态', + value: '已激活', + description: '当前账号可正常访问系统', + icon: BadgeCheck, + }, + { + title: '系统版本', + value: '1.0.0', + description: '基础登录门户服务', + icon: Boxes, + }, +]; function DashboardContent() { - const { login, initialize } = useAuthStore(); + const { user, isAuthenticated, isInitialized, login, initialize } = useAuthStore(); const router = useRouter(); const searchParams = useSearchParams(); + const token = searchParams.get('token'); useEffect(() => { initialize(); }, [initialize]); useEffect(() => { - const token = searchParams.get('token'); if (token) { const fetchUser = async () => { try { @@ -39,25 +60,74 @@ function DashboardContent() { }; fetchUser(); } - }, [searchParams, login, router]); + }, [token, login, router]); + + useEffect(() => { + if (isInitialized && !isAuthenticated && !token) { + router.replace('/login'); + } + }, [isInitialized, isAuthenticated, token, router]); + + if (token || !isInitialized || !isAuthenticated) { + return ( +
+
+ 加载中... +
+
+ ); + } return ( -
-

仪表板

-
-
-

欢迎

-

- 您已成功登录 IMTIM Portal +

+
+
+

控制台

+

仪表板

+

+ {user?.username ? `${user.username},查看登录状态、账户可用性和基础系统信息。` : '查看登录状态、账户可用性和基础系统信息。'}

-
-

账户状态

-

已激活

+
+ + 在线
-
-

系统信息

-

版本 1.0.0

+
+ +
+ {cards.map((card) => { + const Icon = card.icon; + return ( +
+
+ + + + {card.title === '账户状态' ? ( + + Active + + ) : null} +
+

{card.title}

+

{card.value}

+

{card.description}

+
+ ); + })} +
+ +
+
+
+

认证流程

+

+ OAuth 回调、用户信息获取和本地会话写入均已就绪。 +

+
+ + Ready +
@@ -67,7 +137,13 @@ function DashboardContent() { export default function DashboardPage() { return ( - 加载中...
}> + + 加载中... +
+ } + > diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index f1ee6bc..96e9cfe 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -1,34 +1,66 @@ @import "tailwindcss"; :root { - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); + --background: oklch(0.985 0.006 247.858); + --foreground: oklch(0.21 0.034 264.665); --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); + --card-foreground: oklch(0.21 0.034 264.665); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); + --popover-foreground: oklch(0.21 0.034 264.665); + --primary: oklch(0.52 0.19 253.3); --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); + --secondary: oklch(0.96 0.013 247.858); + --secondary-foreground: oklch(0.279 0.041 260.031); + --muted: oklch(0.96 0.013 247.858); + --muted-foreground: oklch(0.446 0.043 257.281); + --accent: oklch(0.94 0.025 255.585); + --accent-foreground: oklch(0.279 0.041 260.031); --destructive: oklch(0.577 0.245 27.325); --destructive-foreground: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); + --border: oklch(0.9 0.022 252.894); + --input: oklch(0.9 0.022 252.894); + --ring: oklch(0.52 0.19 253.3); --radius: 0.5rem; } +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); +} + * { border-color: var(--border); } +html { + min-width: 320px; + scroll-behavior: smooth; +} + body { background-color: var(--background); color: var(--foreground); font-family: system-ui, -apple-system, sans-serif; } + +button, +a { + -webkit-tap-highlight-color: transparent; +} diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 60fe13a..2c0a99b 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -4,18 +4,22 @@ import { useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { useAuthStore } from '@/lib/auth-store'; import { getLoginUrl } from '@/lib/api'; -import { LogIn } from 'lucide-react'; +import { BadgeCheck, KeyRound, LogIn } from 'lucide-react'; import Layout from '@/components/layout'; export default function LoginPage() { - const { isAuthenticated } = useAuthStore(); + const { isAuthenticated, isInitialized, initialize } = useAuthStore(); const router = useRouter(); useEffect(() => { - if (isAuthenticated) { - router.push('/dashboard'); + initialize(); + }, [initialize]); + + useEffect(() => { + if (isInitialized && isAuthenticated) { + router.replace('/dashboard'); } - }, [isAuthenticated, router]); + }, [isInitialized, isAuthenticated, router]); const handleLogin = async () => { try { @@ -28,25 +32,66 @@ export default function LoginPage() { return ( -
-
-
-
-

登录

-

+

+
+

统一身份认证

+

+ 通过 Casdoor 安全接入 IMTIM Portal +

+
+
+
+ + + +
+

单点登录入口

+

+ 使用统一账号完成认证,减少重复登录和权限分散。 +

+
+
+
+
+
+ + + +
+

认证状态同步

+

+ 登录成功后自动写入会话并跳转到控制台。 +

+
+
+
+
+
+ +
+
+
+
+ +
+

登录

+

使用 Casdoor 账号登录 IMTIM Portal

+

+ 登录即表示将跳转到 Casdoor 完成身份验证 +

-
+
); diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 9f87232..c2bdac6 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -3,71 +3,121 @@ import { useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { useAuthStore } from '@/lib/auth-store'; -import { ArrowRight, Shield, Zap, Users } from 'lucide-react'; +import { ArrowRight, CheckCircle2, Shield, Users, Zap } from 'lucide-react'; import Layout from '@/components/layout'; +const features = [ + { + title: '安全可靠', + description: '基于 OAuth 2.0 的企业级认证,支持统一身份入口和多因素认证。', + icon: Shield, + }, + { + title: '快速部署', + description: '面向容器化交付,适合快速接入现有服务并降低运维成本。', + icon: Zap, + }, + { + title: '易于扩展', + description: '模块化架构设计,便于扩展自定义业务逻辑和第三方集成。', + icon: Users, + }, +]; + +const metrics = [ + ['OAuth 2.0', '认证协议'], + ['Docker', '部署方式'], + ['SSO', '统一登录'], +]; + export default function Home() { - const { isAuthenticated, initialize } = useAuthStore(); + const { initialize } = useAuthStore(); const router = useRouter(); useEffect(() => { initialize(); }, [initialize]); - useEffect(() => { - if (isAuthenticated) { - router.push('/dashboard'); - } - }, [isAuthenticated, router]); - return ( -
- {/* Hero Section */} -
-
-

- IMTIM Portal -

-

+

+
+
+
+
+ + 企业登录基座 +
+

+ IMTIM Portal +

+

企业级门户登录基座应用,安全、高效、易于扩展 -

- +

+
+ + +
+
+ +
+
+
+
+

身份服务状态

+

运行正常

+
+ + Active + +
+
+ {metrics.map(([value, label]) => ( +
+

{value}

+

{label}

+
+ ))} +
+
+
- {/* Features Section */} -
-
-

核心特性

-
-
- -

安全可靠

-

- 基于 OAuth 2.0 的企业级认证,支持多因素认证 -

-
-
- -

快速部署

-

- Docker 一键部署,开箱即用,降低运维成本 -

-
-
- -

易于扩展

-

- 模块化架构设计,支持自定义业务逻辑和第三方集成 -

-
+
+
+
+

核心特性

+

+ 围绕认证、部署和扩展体验整理基础能力,适合作为内部系统统一入口。 +

+
+
+ {features.map((feature) => { + const Icon = feature.icon; + return ( +
+
+ +
+

{feature.title}

+

+ {feature.description} +

+
+ ); + })}
diff --git a/frontend/src/components/header.tsx b/frontend/src/components/header.tsx index 68260af..1138706 100644 --- a/frontend/src/components/header.tsx +++ b/frontend/src/components/header.tsx @@ -1,7 +1,7 @@ import Link from 'next/link'; import { useAuthStore } from '@/lib/auth-store'; import { getLoginUrl } from '@/lib/api'; -import { LogOut, User } from 'lucide-react'; +import { LogIn, LogOut, User } from 'lucide-react'; import * as Avatar from '@radix-ui/react-avatar'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; @@ -23,18 +23,23 @@ export default function Header() { }; return ( -
-
- - IMTIM Portal +
+
+ + + IM + + + IMTIM Portal + -