Files
STP/front-end/src/app/pages/PortalHome.tsx
T
vame 41fc6e86dd feat(frontend): 对齐万华门户风格并更新全量截图文档
- 新增门户导航子页面(交易信息、政策法规、操作指南、常见问题、采购平台)并统一门户壳层样式
- 按万华站点风格重构首页与登录页(导航、轮播、公告、登录区)
- 优化小滨助手:首页/登录页接入、可拖拽吸边、圆形图标、圆形外框与切换动画优化
- 重新生成全量页面截图并更新《滨化智慧招标平台-前端界面截图汇总.docx》

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 15:35:20 +08:00

222 lines
9.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from "react";
import { useNavigate } from "react-router";
import { UserRound, Building2, Briefcase, Users } from "lucide-react";
import { cn } from "../components/ui/utils";
import { PortalShell } from "./portal/PortalShell";
import { XiaobinAssistant } from "../components/XiaobinAssistant";
type Notice = { title: string; date: string };
const ROLES = [
{ key: "purchaser", label: "采购人", en: "Purchaser", icon: Building2 },
{ key: "supplier", label: "供应商", en: "Supplier", icon: Briefcase },
{ key: "expert", label: "专家", en: "Expert", icon: UserRound },
{ key: "other", label: "其他", en: "Other", icon: Users },
];
const NOTICE_TABS = ["招标公告", "资格预审公告", "中标候选人公示", "结果公告", "终止公告"];
const BANNERS = [
{
title: "廉洁过节",
subtitle: "阳光招采 · 公开透明 · 全流程可追溯",
style: "from-[#1f4d8f] via-[#2e6dc2] to-[#5c8fe0]",
},
{
title: "上线试运行",
subtitle: "智慧招标平台已上线,欢迎采购人、供应商、专家使用",
style: "from-[#0d5f89] via-[#0f7da8] to-[#4ba6c9]",
},
{
title: "轮播图片2",
subtitle: "统一门户、统一身份、统一公告发布",
style: "from-[#2f5f3f] via-[#3b8460] to-[#66a37f]",
},
];
const NOTICE_DATA: Record<string, Notice[]> = {
: [
{ title: "[公开招标] 2026年地脚螺栓、不锈钢及五金紧固件框架招标", date: "2026-07-13" },
{ title: "[公开招标] 万华莱州100MW分散式风电项目EPC工程总承包[变更公告]", date: "2026-07-13" },
{ title: "[公开招标] 2026年橡胶密封件框架招标O型圈", date: "2026-07-13" },
{ title: "[公开招标] 万华旋塞阀框架包一 金属旋塞阀、包二 衬氟旋塞阀", date: "2026-07-10" },
{ title: "[公开招标] 改性TPU熟化料仓", date: "2026-07-09" },
],
: [
{ title: "滨州园区危化品运输服务资格预审公告", date: "2026-07-11" },
{ title: "公辅系统维保服务资格预审公告", date: "2026-07-10" },
{ title: "智慧园区网络安全服务资格预审公告", date: "2026-07-08" },
],
: [
{ title: "[公开招标] 牵引车租赁服务框架中标候选人公示", date: "2026-07-07" },
{ title: "[公开招标] 电池材料项目地质勘察工程中标候选人公示", date: "2026-07-06" },
{ title: "[公开招标] 四轮防爆巡检机器人系统中标候选人公示", date: "2026-06-26" },
],
: [
{ title: "[公开招标] HDPE管道管件框架招标中标结果公告", date: "2026-07-07" },
{ title: "[公开招标] 万华起重框架招标包一 单梁起重机中标结果公告", date: "2026-07-02" },
{ title: "[公开招标] 万华海阳绿电直连光伏组件项目中标结果公告", date: "2026-06-30" },
],
: [
{ title: "国标切断双偏心蝶阀框架招标废标公告", date: "2026-07-06" },
{ title: "TPU四期熟化料仓招标废标公告", date: "2026-07-01" },
{ title: "万华滨州碳酸锂项目冷却窑废标公告", date: "2026-06-24" },
],
};
function NoticeList({ items }: { items: Notice[] }) {
return (
<ul className="space-y-1.5">
{items.map((item) => (
<li
key={item.title}
className="flex items-center gap-3 border-b border-dashed border-[#e5e7eb] py-1.5 text-sm"
>
<span className="line-clamp-1 flex-1 text-[#1f2937] hover:text-primary">{item.title}</span>
<span className="shrink-0 text-xs text-[#6b7280]">{item.date}</span>
</li>
))}
</ul>
);
}
export function PortalHome() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState(NOTICE_TABS[0]);
const [activeBanner, setActiveBanner] = useState(0);
const banner = BANNERS[activeBanner];
return (
<PortalShell active="home">
<section className="grid grid-cols-12 gap-4">
<div className="col-span-12 border bg-white lg:col-span-3">
<div className="border-b bg-[#f8fafc] px-4 py-3">
<h2 className="text-base font-semibold"></h2>
<p className="mt-1 text-xs text-[#6b7280]">
<button onClick={() => navigate("/register")} className="text-primary hover:underline"></button>
</p>
</div>
<div className="grid grid-cols-2 gap-2 p-4">
{ROLES.map((r) => {
const Icon = r.icon;
return (
<button
key={r.key}
onClick={() => navigate("/")}
className="flex flex-col items-center justify-center border border-[#d9dee7] px-3 py-3.5 text-center hover:border-primary hover:bg-[#f8fbff]"
>
<Icon className="size-5 text-primary" />
<span className="mt-2 text-sm">{r.label}</span>
<span className="text-[11px] text-[#6b7280]">{r.en}</span>
</button>
);
})}
</div>
<div className="space-y-3 px-4 pb-4">
<input
placeholder="账号 / 手机号"
className="h-9 w-full border border-[#d9dee7] bg-white px-3 text-sm outline-none focus:border-primary"
/>
<input
type="password"
placeholder="密码"
className="h-9 w-full border border-[#d9dee7] bg-white px-3 text-sm outline-none focus:border-primary"
/>
<button
onClick={() => navigate("/")}
className="h-9 w-full bg-primary text-sm font-medium text-white hover:bg-[#0f4ea6]"
>
</button>
</div>
</div>
<div className="col-span-12 border bg-white lg:col-span-9">
<div className="relative h-[310px] overflow-hidden">
<div className={cn("absolute inset-0 bg-gradient-to-br", banner.style)} />
<div className="absolute inset-0 bg-black/20" />
<div className="relative flex h-full flex-col justify-end p-7 text-white">
<div className="text-3xl font-bold tracking-wide">{banner.title}</div>
<div className="mt-2 text-sm text-white/90">{banner.subtitle}</div>
</div>
<div className="absolute left-5 top-5 rounded bg-black/35 px-2 py-1 text-xs text-white/90">
</div>
<div className="absolute bottom-4 right-5 flex gap-2">
{BANNERS.map((b, idx) => (
<button
key={b.title}
onClick={() => setActiveBanner(idx)}
className={cn(
"rounded px-2.5 py-1 text-xs",
idx === activeBanner ? "bg-white text-[#1f2937]" : "bg-black/35 text-white",
)}
>
{b.title}
</button>
))}
</div>
</div>
</div>
</section>
<section className="border bg-white">
<div className="border-b bg-[#f8fafc] px-4 py-3">
<h2 className="text-base font-semibold"></h2>
</div>
<div className="border-b px-3">
<div className="flex flex-wrap items-center">
{NOTICE_TABS.map((tab) => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
className={cn(
"relative px-4 py-3 text-sm",
activeTab === tab ? "font-semibold text-primary" : "text-[#4b5563] hover:text-primary",
)}
>
{tab}
{activeTab === tab && <span className="absolute inset-x-2 bottom-0 h-0.5 bg-primary" />}
</button>
))}
</div>
</div>
<div className="p-4">
<NoticeList items={NOTICE_DATA[activeTab]} />
</div>
</section>
<section className="grid grid-cols-12 gap-4">
<div className="col-span-12 border bg-white lg:col-span-6">
<div className="border-b bg-[#f8fafc] px-4 py-3 text-sm font-semibold"></div>
<div className="p-4">
<NoticeList
items={[
{ title: "万华蓬莱园区高温管道耐火衬里类物资供应商招募", date: "2026-07-10" },
{ title: "万华化学桨叶冷却机类供应商招募", date: "2026-07-10" },
{ title: "万华化学洗车平台服务类供应商招募", date: "2026-06-25" },
]}
/>
</div>
</div>
<div className="col-span-12 border bg-white lg:col-span-6">
<div className="border-b bg-[#f8fafc] px-4 py-3 text-sm font-semibold"> / </div>
<div className="grid grid-cols-2 gap-2 p-4 text-sm">
{[
"系统使用常见问题处理",
"常见问题帮助",
"供应商注册操作指南",
"投标文件制作软件操作手册",
"标证通申请-投标单位操作指南",
"浏览器切换IE模式",
].map((x) => (
<button key={x} className="border px-3 py-2 text-left hover:border-primary hover:bg-[#f8fbff]">
{x}
</button>
))}
</div>
</div>
</section>
<XiaobinAssistant pageType="portal" />
</PortalShell>
);
}