feat(front-end): 依据《详细设计》文档对前端 UI 进行全面升级
- 设计系统:品牌主色统一为品红渐变 #c2185b→#e91e63,新增品牌令牌与呼吸/入场动画 - 登录页重建:内部员工/外部账号切换、账号/手机验证码 Tab、SRM 存量提示 - 注册页重建为多步向导:企业信息→联系人→AI 审核,OCR 状态、密码复杂度校验与强度条 - 小滨 AI 助手重建:品牌渐变悬浮球+呼吸光环,角色感知主动提醒(紧急/AI完成/待办/风险/建议) - 新增公开门户首页 /portal:Banner 轮播 + 三列公告区 + 免责声明 - 各角色主题色对齐文档:供应商=红、评标专家=蓝、招标专员=绿 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
import { Hexagon, ArrowRight, Sparkles, Bot } from "lucide-react";
|
||||
import { cn } from "../components/ui/utils";
|
||||
|
||||
const NAV = ["首页", "招标公告", "定标公告", "企业新闻"];
|
||||
|
||||
const BANNERS = [
|
||||
{
|
||||
title: "AI 赋能的智慧招标",
|
||||
subtitle: "全流程线上闭环,主动智能寻源,自动风险评估",
|
||||
tags: ["智能招标委托", "供应商 AI 寻源", "自动化评标辅助"],
|
||||
},
|
||||
{
|
||||
title: "一次注册,双平台通用",
|
||||
subtitle: "自动同步 SRM,建立统一供应商身份,秒级 AI 审核即时生效",
|
||||
tags: ["营业执照 OCR", "工商实时验真", "SRM 无感登录"],
|
||||
},
|
||||
{
|
||||
title: "阳光招采,全程存证",
|
||||
subtitle: "投标文件加密上链,评审操作哈希存证,围串标智能预警",
|
||||
tags: ["链上存证", "盲评脱敏", "AI 雷同检测"],
|
||||
},
|
||||
];
|
||||
|
||||
const TENDERS = [
|
||||
{ title: "年产5万吨聚丙烯项目设备采购", meta: "2026-07-01 | 货物", status: "报名中", color: "#2e7d32", dot: "#4caf50" },
|
||||
{ title: "数字化交付平台开发服务", meta: "2026-06-29 | 服务", status: "即将截止", color: "#e65100", dot: "#ff9800" },
|
||||
{ title: "2026年度防腐保温工程框架", meta: "2026-06-28 | 工程", status: "已截止", color: "#9e9e9e", dot: "#bdbdbd" },
|
||||
];
|
||||
|
||||
const AWARDS = [
|
||||
{ title: "催化剂采购项目(包1)", meta: "2026-07-01 | 中标方:滨化催化剂有限公司" },
|
||||
{ title: "厂区安防监控升级项目", meta: "2026-06-30 | 中标方:海康威视" },
|
||||
{ title: "办公楼装修工程", meta: "2026-06-28 | 中标方:滨州建工集团" },
|
||||
];
|
||||
|
||||
const NEWS = [
|
||||
{ title: "滨化集团获评“省级智能工厂”", meta: "2026-07-01 | 集团动态" },
|
||||
{ title: "2026 年供应商大会圆满举办", meta: "2026-06-25 | 合作伙伴" },
|
||||
{ title: "智慧招标平台正式上线运营", meta: "2026-06-01 | 平台公告" },
|
||||
];
|
||||
|
||||
function AnnouncementCard({
|
||||
title,
|
||||
tag,
|
||||
headerColor,
|
||||
items,
|
||||
moreBg,
|
||||
moreText,
|
||||
}: {
|
||||
title: string;
|
||||
tag: string;
|
||||
headerColor: string;
|
||||
items: { title: string; meta: string; status?: string; color?: string; dot?: string }[];
|
||||
moreBg: string;
|
||||
moreText: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col overflow-hidden rounded-2xl bg-white shadow-[0_4px_16px_rgba(0,0,0,0.06)]">
|
||||
<div className="px-5 py-3 text-white" style={{ backgroundColor: headerColor }}>
|
||||
<div className="text-base font-bold">{title}</div>
|
||||
<div className="text-[11px] text-white/80">{tag}</div>
|
||||
</div>
|
||||
<div className="flex-1 space-y-2 p-4">
|
||||
{items.map((it) => (
|
||||
<div key={it.title} className="cursor-pointer rounded-lg bg-[#fafafa] px-3 py-2.5 transition-colors hover:bg-[#f0f2f7]">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="truncate text-sm text-[#1a2332]">{it.title}</span>
|
||||
{it.status && (
|
||||
<span className="flex shrink-0 items-center gap-1 text-xs" style={{ color: it.color }}>
|
||||
<span className="size-1.5 rounded-full" style={{ backgroundColor: it.dot }} />
|
||||
{it.status}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-[#8895aa]">{it.meta}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="px-4 pb-4">
|
||||
<button
|
||||
className="w-full rounded-full py-1.5 text-xs font-bold transition-opacity hover:opacity-80"
|
||||
style={{ backgroundColor: moreBg, color: moreText }}
|
||||
>
|
||||
查看全部 →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function PortalHome() {
|
||||
const navigate = useNavigate();
|
||||
const [banner, setBanner] = useState(0);
|
||||
const [activeNav, setActiveNav] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const t = setInterval(() => setBanner((b) => (b + 1) % BANNERS.length), 5000);
|
||||
return () => clearInterval(t);
|
||||
}, []);
|
||||
|
||||
const b = BANNERS[banner];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#f5f7fa]">
|
||||
{/* 顶部导航 */}
|
||||
<header className="sticky top-0 z-30 bg-brand-gradient shadow-sm">
|
||||
<div className="mx-auto flex h-[68px] max-w-[1200px] items-center px-8">
|
||||
<Link to="/portal" className="flex items-baseline gap-2 text-white">
|
||||
<Hexagon className="size-6 self-center" />
|
||||
<span className="text-xl font-bold">智慧招标平台</span>
|
||||
<span className="hidden text-xs text-white/70 sm:inline">Smart Tendering Platform</span>
|
||||
</Link>
|
||||
<nav className="ml-12 hidden gap-1 md:flex">
|
||||
{NAV.map((n, i) => (
|
||||
<button
|
||||
key={n}
|
||||
onClick={() => setActiveNav(i)}
|
||||
className={cn(
|
||||
"group relative px-4 py-2 text-sm text-white/80 transition-colors hover:text-white",
|
||||
activeNav === i && "font-bold text-white",
|
||||
)}
|
||||
>
|
||||
{n}
|
||||
<span
|
||||
className={cn(
|
||||
"absolute inset-x-4 -bottom-0.5 h-0.5 rounded-full bg-white transition-all",
|
||||
activeNav === i ? "opacity-100" : "opacity-0 group-hover:opacity-60",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
<div className="ml-auto flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => navigate("/")}
|
||||
className="rounded-lg border border-white/60 px-4 py-1.5 text-sm text-white transition-colors hover:bg-white/15"
|
||||
>
|
||||
登录
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigate("/register")}
|
||||
className="rounded-lg bg-white px-4 py-1.5 text-sm font-bold text-primary transition-transform hover:-translate-y-0.5"
|
||||
>
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="mx-auto max-w-[1200px] px-8 pb-16">
|
||||
{/* Banner 轮播 */}
|
||||
<section className="relative mt-6 overflow-hidden rounded-2xl bg-gradient-to-br from-[#fce4ec] to-[#f3e5f5] shadow-[0_6px_24px_rgba(0,0,0,0.08)]">
|
||||
<div className="flex min-h-[280px] flex-col justify-center px-12 py-10">
|
||||
<div className="mb-3 inline-flex w-fit items-center gap-1.5 rounded-full bg-white/70 px-3 py-1 text-xs text-primary">
|
||||
<Sparkles className="size-3.5" /> AI 原生 · 全流程线上闭环
|
||||
</div>
|
||||
<h1 key={banner} className="animate-slide-up text-primary" style={{ fontSize: 34, fontWeight: 700 }}>
|
||||
{b.title}
|
||||
</h1>
|
||||
<p className="mt-3 text-lg text-[#4a5568]">{b.subtitle}</p>
|
||||
<div className="mt-3 flex flex-wrap gap-x-5 gap-y-1 text-sm text-[#8895aa]">
|
||||
{b.tags.map((t) => (
|
||||
<span key={t}>• {t}</span>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => navigate("/register")}
|
||||
className="mt-6 inline-flex w-fit items-center gap-1.5 rounded-lg bg-brand-gradient px-6 py-2.5 text-sm font-bold text-white shadow-md transition-transform hover:scale-105"
|
||||
>
|
||||
了解更多 <ArrowRight className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
{/* 指示器 */}
|
||||
<div className="absolute bottom-6 right-12 flex gap-2">
|
||||
{BANNERS.map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
aria-label={`切换到第 ${i + 1} 张`}
|
||||
onClick={() => setBanner(i)}
|
||||
className={cn("h-2 rounded-full transition-all", i === banner ? "w-6 bg-primary" : "w-2 bg-[#d1d5db]")}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 公告区 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="mb-4 flex items-center gap-2 text-[#1a2332]" style={{ fontSize: 20, fontWeight: 700 }}>
|
||||
📢 最新公告
|
||||
</h2>
|
||||
<div className="grid gap-5 md:grid-cols-3">
|
||||
<AnnouncementCard title="招标公告" tag="最新发布" headerColor="#c2185b" items={TENDERS} moreBg="#fce4ec" moreText="#c2185b" />
|
||||
<AnnouncementCard title="定标公告" tag="最新定标" headerColor="#1565c0" items={AWARDS} moreBg="#e3f2fd" moreText="#1565c0" />
|
||||
<AnnouncementCard title="企业新闻" tag="集团动态" headerColor="#e65100" items={NEWS} moreBg="#fff3e0" moreText="#e65100" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 免责声明 */}
|
||||
<section className="mt-8 rounded-2xl bg-white p-6 text-center shadow-[0_4px_16px_rgba(0,0,0,0.06)]">
|
||||
<div className="text-sm font-semibold text-[#4a5568]">免责声明</div>
|
||||
<p className="mx-auto mt-2 max-w-3xl text-xs leading-relaxed text-[#8895aa]">
|
||||
本平台所有招标信息均由滨化集团或其下属公司发布,信息真实有效。供应商应对投标行为负责,本平台保留对违规行为的处理权。
|
||||
</p>
|
||||
<p className="mt-2 text-xs text-[#8895aa]">隐私政策 | 服务协议 | 法律声明 | © 2026 滨化集团 | 鲁ICP备XXXXXXXX号</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
{/* AI 助手浮标 */}
|
||||
<button
|
||||
onClick={() => navigate("/")}
|
||||
className="group fixed bottom-8 right-8 z-50 flex size-[68px] items-center justify-center rounded-full bg-brand-gradient text-white shadow-xl transition-transform hover:scale-105"
|
||||
aria-label="小滨 AI 助手"
|
||||
>
|
||||
<span className="pointer-events-none absolute inset-0 rounded-full bg-brand-gradient animate-breathe" />
|
||||
<span className="relative flex flex-col items-center leading-none">
|
||||
<Bot className="size-5" />
|
||||
<span className="mt-0.5 text-[9px] text-white/90">小滨</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user