feat: 登录页改用 SSR 预取机构列表,消除首屏闪烁
**问题** - 登录页机构下拉在客户端 useEffect 中拉取,首屏会闪现"正在加载机构列表" - 浏览器端多一跳 fetch 请求,体感延迟明显 **改动** - page.tsx → 服务端组件(async),SSR 时内网直连 /api/v1/organizations - login-form.tsx → 客户端组件,接收 initialOrgs,保留表单交互 - useEffect 降级为仅当 SSR 为空时的客户端兜底 - force-dynamic + cache: no-store 保证机构变化即时生效 **验证** - 本地 next start:首屏 HTML 直接含机构名,无占位符 - 构建路由从 ○ Static 变为 ƒ Dynamic
This commit is contained in:
@@ -0,0 +1,225 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
import type { Organization } from "@/stores/auth";
|
||||||
|
import api from "@/lib/api";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import {
|
||||||
|
Shield,
|
||||||
|
Building2,
|
||||||
|
Sparkles,
|
||||||
|
BookOpen,
|
||||||
|
FileText,
|
||||||
|
Brain,
|
||||||
|
GraduationCap,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
export default function LoginForm({ initialOrgs }: { initialOrgs: Organization[] }) {
|
||||||
|
const [email, setEmail] = useState("fazhiribao@govai.gov.cn");
|
||||||
|
const [password, setPassword] = useState("admin123");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [errorMsg, setErrorMsg] = useState("");
|
||||||
|
const [orgs, setOrgs] = useState<Organization[]>(initialOrgs);
|
||||||
|
const [selectedOrg, setSelectedOrg] = useState(initialOrgs[0]?.id ?? "");
|
||||||
|
const { login, switchOrg } = useAuthStore();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// SSR 已注入机构时直接使用,无需再请求;
|
||||||
|
// 仅当服务端取数失败(首屏机构为空)时,客户端兜底拉取一次
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialOrgs.length > 0) return;
|
||||||
|
api
|
||||||
|
.get<Organization[]>("/api/v1/organizations")
|
||||||
|
.then((data) => {
|
||||||
|
setOrgs(data);
|
||||||
|
if (data.length > 0) setSelectedOrg(data[0].id);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}, [initialOrgs.length]);
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!email || !password) {
|
||||||
|
setErrorMsg("请输入邮箱和密码");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!selectedOrg) {
|
||||||
|
setErrorMsg("请选择所属机构");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setErrorMsg("");
|
||||||
|
try {
|
||||||
|
await login(email, password, selectedOrg);
|
||||||
|
const user = useAuthStore.getState().user;
|
||||||
|
// 平台管理员不绑定机构,登录后保留 super_admin 身份;
|
||||||
|
// 仅机构管理员在所选机构与自身归属不一致时才触发切换
|
||||||
|
if (user && user.role === "admin" && user.org_id !== selectedOrg) {
|
||||||
|
await switchOrg(selectedOrg);
|
||||||
|
}
|
||||||
|
router.push(user?.role === "super_admin" ? "/platform/overview" : "/store");
|
||||||
|
} catch (err) {
|
||||||
|
setErrorMsg(err instanceof Error ? err.message : "登录失败,请检查账号和密码");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{ icon: Sparkles, text: "AI 驱动的智能办公" },
|
||||||
|
{ icon: FileText, text: "一键生成公文与报告" },
|
||||||
|
{ icon: BookOpen, text: "智能知识库问答" },
|
||||||
|
{ icon: Brain, text: "多场景 AI 应用中心" },
|
||||||
|
{ icon: GraduationCap, text: "支持多机构独立部署" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-blue-950 via-blue-900 to-blue-800 px-6 py-12">
|
||||||
|
<div className="flex w-full max-w-[1000px] items-center gap-16 lg:gap-20">
|
||||||
|
{/* 左侧品牌区 - 桌面端显示 */}
|
||||||
|
<div className="hidden lg:flex lg:flex-1 flex-col">
|
||||||
|
<div className="max-w-md">
|
||||||
|
<div className="flex items-center gap-3 mb-8">
|
||||||
|
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-white/10 backdrop-blur-sm border border-white/20">
|
||||||
|
<Shield className="h-8 w-8 text-white" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-bold text-white tracking-tight">AI 智能应用平台</h1>
|
||||||
|
<p className="text-blue-200/80 text-sm mt-0.5">提升效能 · 赋能智慧办公</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-blue-100/70 text-lg leading-relaxed mb-10">
|
||||||
|
面向政务与高校场景的一站式 AI
|
||||||
|
应用平台,集成文档生成、智能问答、数据分析等核心能力,助力组织数智化转型。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{features.map(({ icon: Icon, text }) => (
|
||||||
|
<div key={text} className="flex items-center gap-4">
|
||||||
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-white/10 backdrop-blur-sm">
|
||||||
|
<Icon className="h-5 w-5 text-blue-200" />
|
||||||
|
</div>
|
||||||
|
<span className="text-blue-100/90 text-base">{text}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 右侧登录区 */}
|
||||||
|
<div className="flex w-full lg:w-auto lg:shrink-0 items-center justify-center">
|
||||||
|
<div className="w-full max-w-[460px] rounded-2xl bg-white shadow-2xl border border-white/20 overflow-hidden">
|
||||||
|
{/* 移动端标题 - 仅在小屏显示 */}
|
||||||
|
<div className="lg:hidden bg-gradient-to-r from-blue-900 to-blue-800 px-8 pt-8 pb-6 text-center">
|
||||||
|
<Shield className="h-10 w-10 text-white mx-auto mb-3" />
|
||||||
|
<h1 className="text-xl font-bold text-white">AI 智能应用平台</h1>
|
||||||
|
<p className="text-blue-200/80 text-sm mt-1">提升效能 · 赋能智慧办公</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 表单区域 */}
|
||||||
|
<div className="px-8 sm:px-10 py-8 sm:py-10">
|
||||||
|
<h2 className="hidden lg:block text-2xl font-semibold text-gray-900 mb-1">
|
||||||
|
欢迎登录
|
||||||
|
</h2>
|
||||||
|
<p className="hidden lg:block text-sm text-gray-500 mb-8">请选择机构并输入账号密码</p>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
|
{errorMsg && (
|
||||||
|
<div className="rounded-lg bg-red-50 border border-red-200 px-4 py-3 text-sm text-red-700">
|
||||||
|
{errorMsg}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="org" className="text-sm font-medium text-gray-700">
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Building2 className="h-4 w-4 text-gray-400" />
|
||||||
|
所属机构
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
{orgs.length > 0 ? (
|
||||||
|
<select
|
||||||
|
id="org"
|
||||||
|
value={selectedOrg}
|
||||||
|
onChange={(e) => setSelectedOrg(e.target.value)}
|
||||||
|
className="flex h-11 w-full rounded-xl border border-gray-200 bg-gray-50/50 px-4 py-2 text-sm shadow-sm transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-400 hover:border-gray-300"
|
||||||
|
>
|
||||||
|
{orgs.map((org) => (
|
||||||
|
<option key={org.id} value={org.id}>
|
||||||
|
{org.short_name || org.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<Input disabled placeholder="正在加载机构列表..." className="h-11 rounded-xl" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="email" className="text-sm font-medium text-gray-700">
|
||||||
|
账号
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="your@gov.cn"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
autoComplete="email"
|
||||||
|
required
|
||||||
|
className="h-11 rounded-xl border-gray-200 bg-gray-50/50 px-4 focus:ring-2 focus:ring-blue-500/40 focus:border-blue-400 hover:border-gray-300"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="password" className="text-sm font-medium text-gray-700">
|
||||||
|
密码
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
autoComplete="current-password"
|
||||||
|
required
|
||||||
|
className="h-11 rounded-xl border-gray-200 bg-gray-50/50 px-4 focus:ring-2 focus:ring-blue-500/40 focus:border-blue-400 hover:border-gray-300"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full h-12 text-base font-medium rounded-xl bg-blue-900 hover:bg-blue-800 transition-all duration-200 shadow-lg shadow-blue-900/25 hover:shadow-xl hover:shadow-blue-900/30 mt-2"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "登录中..." : "登 录"}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="mt-6 text-center text-sm text-gray-500">
|
||||||
|
还没有账号?{" "}
|
||||||
|
<Link
|
||||||
|
href="/register"
|
||||||
|
className="text-blue-600 font-medium hover:text-blue-700 underline-offset-4 hover:underline"
|
||||||
|
>
|
||||||
|
申请注册
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 text-center text-xs text-gray-400 border-t border-gray-100 pt-5">
|
||||||
|
本系统仅限授权人员使用 · 数据安全等级:机构内部
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,222 +1,27 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { useAuthStore } from "@/stores/auth";
|
|
||||||
import type { Organization } from "@/stores/auth";
|
import type { Organization } from "@/stores/auth";
|
||||||
import api from "@/lib/api";
|
import LoginForm from "./login-form";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
|
||||||
Shield,
|
|
||||||
Building2,
|
|
||||||
Sparkles,
|
|
||||||
BookOpen,
|
|
||||||
FileText,
|
|
||||||
Brain,
|
|
||||||
GraduationCap,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
export default function LoginPage() {
|
// 登录页改为服务端渲染:SSR 阶段在 web 服务端内网直连 API 取机构列表,
|
||||||
const [email, setEmail] = useState("fazhiribao@govai.gov.cn");
|
// 首屏 HTML 即带机构数据,消除客户端"正在加载机构列表"的闪烁。
|
||||||
const [password, setPassword] = useState("admin123");
|
// no-store 保证机构增删改即时生效,无需重新构建或重启。
|
||||||
const [loading, setLoading] = useState(false);
|
export const dynamic = "force-dynamic";
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
|
||||||
const [orgs, setOrgs] = useState<Organization[]>([]);
|
|
||||||
const [selectedOrg, setSelectedOrg] = useState("");
|
|
||||||
const { login, switchOrg } = useAuthStore();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
async function getOrganizations(): Promise<Organization[]> {
|
||||||
api
|
const base = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080";
|
||||||
.get<Organization[]>("/api/v1/organizations")
|
|
||||||
.then((data) => {
|
|
||||||
setOrgs(data);
|
|
||||||
if (data.length > 0) setSelectedOrg(data[0].id);
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!email || !password) {
|
|
||||||
setErrorMsg("请输入邮箱和密码");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!selectedOrg) {
|
|
||||||
setErrorMsg("请选择所属机构");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
setErrorMsg("");
|
|
||||||
try {
|
try {
|
||||||
await login(email, password, selectedOrg);
|
const res = await fetch(`${base}/api/v1/organizations`, {
|
||||||
const user = useAuthStore.getState().user;
|
cache: "no-store",
|
||||||
// 平台管理员不绑定机构,登录后保留 super_admin 身份;
|
headers: { "Content-Type": "application/json" },
|
||||||
// 仅机构管理员在所选机构与自身归属不一致时才触发切换
|
});
|
||||||
if (user && user.role === "admin" && user.org_id !== selectedOrg) {
|
if (!res.ok) return [];
|
||||||
await switchOrg(selectedOrg);
|
const json = (await res.json()) as { code: number; data: Organization[] };
|
||||||
|
return json.code === 0 && Array.isArray(json.data) ? json.data : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
router.push(user?.role === "super_admin" ? "/platform/overview" : "/store");
|
|
||||||
} catch (err) {
|
|
||||||
setErrorMsg(err instanceof Error ? err.message : "登录失败,请检查账号和密码");
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const features = [
|
export default async function LoginPage() {
|
||||||
{ icon: Sparkles, text: "AI 驱动的智能办公" },
|
const orgs = await getOrganizations();
|
||||||
{ icon: FileText, text: "一键生成公文与报告" },
|
return <LoginForm initialOrgs={orgs} />;
|
||||||
{ icon: BookOpen, text: "智能知识库问答" },
|
|
||||||
{ icon: Brain, text: "多场景 AI 应用中心" },
|
|
||||||
{ icon: GraduationCap, text: "支持多机构独立部署" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-blue-950 via-blue-900 to-blue-800 px-6 py-12">
|
|
||||||
<div className="flex w-full max-w-[1000px] items-center gap-16 lg:gap-20">
|
|
||||||
{/* 左侧品牌区 - 桌面端显示 */}
|
|
||||||
<div className="hidden lg:flex lg:flex-1 flex-col">
|
|
||||||
<div className="max-w-md">
|
|
||||||
<div className="flex items-center gap-3 mb-8">
|
|
||||||
<div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-white/10 backdrop-blur-sm border border-white/20">
|
|
||||||
<Shield className="h-8 w-8 text-white" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-bold text-white tracking-tight">AI 智能应用平台</h1>
|
|
||||||
<p className="text-blue-200/80 text-sm mt-0.5">提升效能 · 赋能智慧办公</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-blue-100/70 text-lg leading-relaxed mb-10">
|
|
||||||
面向政务与高校场景的一站式 AI
|
|
||||||
应用平台,集成文档生成、智能问答、数据分析等核心能力,助力组织数智化转型。
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
{features.map(({ icon: Icon, text }) => (
|
|
||||||
<div key={text} className="flex items-center gap-4">
|
|
||||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-white/10 backdrop-blur-sm">
|
|
||||||
<Icon className="h-5 w-5 text-blue-200" />
|
|
||||||
</div>
|
|
||||||
<span className="text-blue-100/90 text-base">{text}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 右侧登录区 */}
|
|
||||||
<div className="flex w-full lg:w-auto lg:shrink-0 items-center justify-center">
|
|
||||||
<div className="w-full max-w-[460px] rounded-2xl bg-white shadow-2xl border border-white/20 overflow-hidden">
|
|
||||||
{/* 移动端标题 - 仅在小屏显示 */}
|
|
||||||
<div className="lg:hidden bg-gradient-to-r from-blue-900 to-blue-800 px-8 pt-8 pb-6 text-center">
|
|
||||||
<Shield className="h-10 w-10 text-white mx-auto mb-3" />
|
|
||||||
<h1 className="text-xl font-bold text-white">AI 智能应用平台</h1>
|
|
||||||
<p className="text-blue-200/80 text-sm mt-1">提升效能 · 赋能智慧办公</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 表单区域 */}
|
|
||||||
<div className="px-8 sm:px-10 py-8 sm:py-10">
|
|
||||||
<h2 className="hidden lg:block text-2xl font-semibold text-gray-900 mb-1">
|
|
||||||
欢迎登录
|
|
||||||
</h2>
|
|
||||||
<p className="hidden lg:block text-sm text-gray-500 mb-8">请选择机构并输入账号密码</p>
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
|
||||||
{errorMsg && (
|
|
||||||
<div className="rounded-lg bg-red-50 border border-red-200 px-4 py-3 text-sm text-red-700">
|
|
||||||
{errorMsg}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="org" className="text-sm font-medium text-gray-700">
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
<Building2 className="h-4 w-4 text-gray-400" />
|
|
||||||
所属机构
|
|
||||||
</span>
|
|
||||||
</Label>
|
|
||||||
{orgs.length > 0 ? (
|
|
||||||
<select
|
|
||||||
id="org"
|
|
||||||
value={selectedOrg}
|
|
||||||
onChange={(e) => setSelectedOrg(e.target.value)}
|
|
||||||
className="flex h-11 w-full rounded-xl border border-gray-200 bg-gray-50/50 px-4 py-2 text-sm shadow-sm transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-400 hover:border-gray-300"
|
|
||||||
>
|
|
||||||
{orgs.map((org) => (
|
|
||||||
<option key={org.id} value={org.id}>
|
|
||||||
{org.short_name || org.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
) : (
|
|
||||||
<Input disabled placeholder="正在加载机构列表..." className="h-11 rounded-xl" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="email" className="text-sm font-medium text-gray-700">
|
|
||||||
账号
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
placeholder="your@gov.cn"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
autoComplete="email"
|
|
||||||
required
|
|
||||||
className="h-11 rounded-xl border-gray-200 bg-gray-50/50 px-4 focus:ring-2 focus:ring-blue-500/40 focus:border-blue-400 hover:border-gray-300"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="password" className="text-sm font-medium text-gray-700">
|
|
||||||
密码
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="password"
|
|
||||||
type="password"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
autoComplete="current-password"
|
|
||||||
required
|
|
||||||
className="h-11 rounded-xl border-gray-200 bg-gray-50/50 px-4 focus:ring-2 focus:ring-blue-500/40 focus:border-blue-400 hover:border-gray-300"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
className="w-full h-12 text-base font-medium rounded-xl bg-blue-900 hover:bg-blue-800 transition-all duration-200 shadow-lg shadow-blue-900/25 hover:shadow-xl hover:shadow-blue-900/30 mt-2"
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
{loading ? "登录中..." : "登 录"}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div className="mt-6 text-center text-sm text-gray-500">
|
|
||||||
还没有账号?{" "}
|
|
||||||
<Link
|
|
||||||
href="/register"
|
|
||||||
className="text-blue-600 font-medium hover:text-blue-700 underline-offset-4 hover:underline"
|
|
||||||
>
|
|
||||||
申请注册
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-8 text-center text-xs text-gray-400 border-t border-gray-100 pt-5">
|
|
||||||
本系统仅限授权人员使用 · 数据安全等级:机构内部
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user