Files
oneos-/src/App.tsx
kkfluous ba2d4cc73c
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix: Use BASE_URL for image paths to support /info/ subpath
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:29:29 +08:00

583 lines
28 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.
/**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import React, { useState, useEffect } from 'react';
const BASE = import.meta.env.BASE_URL;
import {
ShieldCheck,
LayoutDashboard,
Smartphone,
BarChart3,
Settings,
FileText,
Truck,
ClipboardCheck,
AlertCircle,
Database,
ChevronRight,
Menu,
X,
ArrowRight,
CheckCircle2,
Zap
} from 'lucide-react';
import { motion } from 'motion/react';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// --- Components ---
const Navbar = () => {
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 20);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<nav className={cn(
"fixed top-0 left-0 right-0 z-50 transition-all duration-300 border-b",
isScrolled ? "bg-white/80 backdrop-blur-md py-3 border-slate-200" : "bg-transparent py-5 border-transparent"
)}>
<div className="max-w-7xl mx-auto px-6 flex items-center justify-between relative h-16 md:h-20">
{/* Left: Logo */}
<div className="flex items-center">
<div className="h-10 md:h-12 flex items-center justify-center overflow-hidden">
<img src={`${BASE}assets/screenshots/logo.png`} alt="Logo" className="h-full w-auto object-contain" referrerPolicy="no-referrer" />
</div>
</div>
{/* Center: Title */}
<div className="absolute left-1/2 -translate-x-1/2 flex items-center whitespace-nowrap">
<span className="text-xl md:text-2xl font-bold tracking-tight text-slate-900">OneOS <span className="text-brand">Solutions</span></span>
</div>
{/* Right: Spacer to maintain layout if needed, but not strictly necessary with absolute center */}
<div className="hidden md:block w-12" />
</div>
</nav>
);
};
const Hero = ({ title, subtitle, badge }: { title: string, subtitle: string, badge: string }) => (
<section className="relative pt-32 pb-20 md:pt-48 md:pb-32 overflow-hidden">
{/* Premium Mesh Gradient Background */}
<div className="absolute inset-0 -z-10">
{/* Base Layer - Very subtle green tint */}
<div className="absolute inset-0 bg-emerald-50/20" />
{/* Mesh Blobs - Slightly more vibrant but still soft */}
<div className="absolute top-[-10%] left-[-10%] w-[70%] h-[70%] bg-emerald-200/40 rounded-full blur-[120px] animate-pulse" style={{ animationDuration: '10s' }} />
<div className="absolute bottom-[-10%] right-[-10%] w-[70%] h-[70%] bg-teal-200/40 rounded-full blur-[120px] animate-pulse" style={{ animationDuration: '15s' }} />
<div className="absolute top-[20%] right-[10%] w-[50%] h-[50%] bg-emerald-100/30 rounded-full blur-[100px]" />
{/* Subtle overall gradient overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-emerald-50/30 via-transparent to-white" />
{/* Noise Texture Overlay */}
<div className="absolute inset-0 opacity-[0.03] pointer-events-none" style={{ backgroundImage: 'url("https://grainy-gradients.vercel.app/noise.svg")' }} />
{/* Geometric Grid */}
<div className="absolute inset-0 opacity-[0.1]" style={{
backgroundImage: `linear-gradient(#065f46 1px, transparent 1px), linear-gradient(90deg, #065f46 1px, transparent 1px)`,
backgroundSize: '60px 60px'
}} />
{/* Central Spotlight */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full max-w-6xl h-full bg-[radial-gradient(circle_at_50%_40%,#fff_0%,transparent_80%)]" />
{/* Accent Lines */}
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-brand/20 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-emerald-100 to-transparent" />
</div>
<div className="max-w-7xl mx-auto px-6 text-center relative">
{/* Floating Decorative Elements */}
<motion.div
animate={{ y: [0, -20, 0] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
className="absolute -top-20 -left-20 w-40 h-40 bg-brand/5 rounded-3xl rotate-12 blur-xl hidden lg:block"
/>
<motion.div
animate={{ y: [0, 20, 0] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut" }}
className="absolute top-60 -right-20 w-56 h-56 bg-teal-500/5 rounded-full blur-3xl hidden lg:block"
/>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/80 backdrop-blur-sm border border-brand-muted shadow-sm mb-8">
<span className="w-2 h-2 rounded-full bg-brand animate-pulse" />
<span className="text-brand text-xs font-bold uppercase tracking-wider">
{badge}
</span>
</div>
<h1 className="text-5xl md:text-7xl font-black text-slate-900 mb-8 leading-[1.1] tracking-tight">
{title}
</h1>
<p className="text-xl text-slate-600 max-w-3xl mx-auto mb-10 leading-relaxed">
{subtitle}
</p>
</motion.div>
</div>
</section>
);
const FeatureCard = ({ icon: Icon, title, description, delay }: { icon: any, title: string, description: string, delay: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay }}
className="bg-white p-8 rounded-3xl border border-slate-100 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all group"
>
<div className="w-14 h-14 bg-brand-light text-brand rounded-2xl flex items-center justify-center mb-6 group-hover:bg-brand group-hover:text-white transition-colors">
<Icon size={28} />
</div>
<h3 className="text-xl font-bold text-slate-900 mb-3">{title}</h3>
<p className="text-slate-600 leading-relaxed">{description}</p>
</motion.div>
);
const SectionHeading = ({ title, subtitle, centered = true }: { title: string, subtitle?: string, centered?: boolean }) => (
<div className={cn("mb-16", centered ? "text-center" : "text-left")}>
<h2 className="text-3xl md:text-4xl font-black text-slate-900 mb-4 tracking-tight">{title}</h2>
{subtitle && <p className="text-lg text-slate-500 max-w-2xl mx-auto">{subtitle}</p>}
</div>
);
const ScreenshotPlaceholder = ({ label, description, src, type = "web", onClick }: { label: string, description: string, src?: string, type?: "web" | "mobile", onClick?: () => void }) => (
<div
onClick={onClick}
className={cn(
"relative bg-slate-100 rounded-2xl overflow-hidden border border-slate-200 group cursor-pointer shadow-sm transition-all hover:ring-2 hover:ring-brand/50",
type === "mobile" ? "aspect-[9/19.5] w-full" : "aspect-video w-full"
)}
>
{src ? (
<img
src={src}
alt={label}
referrerPolicy="no-referrer"
className="w-full h-full object-cover object-top group-hover:scale-105 transition-transform duration-500"
/>
) : (
<div className="absolute inset-0 flex flex-col items-center justify-center p-6 text-center">
<div className="w-16 h-16 bg-white rounded-full flex items-center justify-center text-slate-400 mb-4 shadow-sm group-hover:scale-110 transition-transform">
{type === "mobile" ? <Smartphone size={32} /> : <LayoutDashboard size={32} />}
</div>
<h4 className="font-bold text-slate-900 mb-2">{label}</h4>
<p className="text-xs text-slate-500">{description}</p>
</div>
)}
<div className="absolute inset-0 bg-brand/0 group-hover:bg-brand/5 transition-colors pointer-events-none" />
<div className="absolute bottom-4 right-4 bg-white/90 backdrop-blur px-3 py-1 rounded-full text-[10px] font-bold text-slate-600 border border-white/50 shadow-sm z-10">
{type === "mobile" ? "移动端界面" : "Web端界面"}
</div>
</div>
);
const Lightbox = ({ src, onClose }: { src: string | null, onClose: () => void }) => {
if (!src) return null;
return (
<div
className="fixed inset-0 z-[100] bg-slate-900/90 backdrop-blur-sm flex items-center justify-center p-4 md:p-10 cursor-zoom-out"
onClick={onClose}
>
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
className="relative max-w-5xl w-full max-h-full flex items-center justify-center"
onClick={(e) => e.stopPropagation()}
>
<img
src={src}
alt="Large view"
className="max-w-full max-h-[90vh] object-contain rounded-xl shadow-2xl"
referrerPolicy="no-referrer"
/>
<button
onClick={onClose}
className="absolute -top-12 right-0 text-white hover:text-brand-accent transition-colors p-2"
>
<X size={32} />
</button>
</motion.div>
</div>
);
};
// --- Main Content Sections ---
const AssetManagementSystem = () => {
const [selectedImage, setSelectedImage] = useState<string | null>(null);
return (
<div className="space-y-20 pb-20">
<Lightbox src={selectedImage} onClose={() => setSelectedImage(null)} />
<Hero
badge="OneOS 核心产品"
title="企业级资产管理中枢"
subtitle="打通资产从“采购入库”到“报废处置”的全生命周期数据链,实现资产的可视化、精细化、智能化管理。"
/>
{/* Value Highlights */}
<section className="max-w-7xl mx-auto px-6">
<SectionHeading title="核心价值亮点" subtitle="不仅仅是台账记录,更是企业资产运营的决策大脑" />
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<FeatureCard
icon={ClipboardCheck}
title="全生命周期闭环"
description="覆盖采购、验收、领用、调拨、维修、盘点、折旧至报废全流程,消除管理盲区。"
delay={0.1}
/>
<FeatureCard
icon={BarChart3}
title="实时可视化大屏"
description="构建可视化运营视图,聚合核心数据分析。直观呈现资产分布与利用效率,支撑资源优化配置,驱动资产价值最大化。"
delay={0.2}
/>
<FeatureCard
icon={AlertCircle}
title="智能预警与运维"
description="支持设置维保提醒、闲置预警、超期预警等规则引擎,变被动维修为主动预防。"
delay={0.3}
/>
<FeatureCard
icon={Smartphone}
title="多端协同集成"
description="支持PC管理后台 + 移动端扫码操作可对接企业现有ERP、财务及OA系统。"
delay={0.4}
/>
</div>
</section>
{/* Architecture */}
<section className="bg-slate-50 py-24">
<div className="max-w-7xl mx-auto px-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
<div>
<SectionHeading
centered={false}
title="系统架构概览"
subtitle="基于微服务架构依托OneOS平台强大的底层能力确保系统的高可用性与扩展性。"
/>
<div className="space-y-6">
{[
{ title: "底层平台", desc: "OneOS 主平台提供统一身份认证、消息总线、数据中台支持。" },
{ title: "应用层", desc: "模块化设计,包括资产管理、流程审批、报表分析等核心模块。" },
{ title: "接入层", desc: "全面支持Web端、移动端及第三方系统API接入。" }
].map((item, i) => (
<div key={i} className="flex gap-4">
<div className="mt-1 flex-shrink-0 w-6 h-6 rounded-full bg-brand-light text-brand flex items-center justify-center">
<CheckCircle2 size={14} />
</div>
<div>
<h4 className="font-bold text-slate-900">{item.title}</h4>
<p className="text-slate-500 text-sm">{item.desc}</p>
</div>
</div>
))}
</div>
</div>
<div className="bg-white p-10 rounded-[40px] shadow-2xl shadow-slate-200 border border-slate-100 relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-brand-light rounded-bl-full -z-0" />
<div className="relative z-10 flex flex-col items-center gap-8">
<div className="w-48 h-48 rounded-full border-4 border-dashed border-brand-muted flex items-center justify-center p-4">
<div className="w-full h-full bg-brand rounded-full flex flex-col items-center justify-center text-white text-center">
<Database size={32} className="mb-2" />
<span className="text-xs font-bold uppercase tracking-widest"></span>
</div>
</div>
<div className="grid grid-cols-2 gap-4 w-full">
{['基础数据管理', '业务流程管理', '统计分析', '系统管理'].map((mod) => (
<div key={mod} className="bg-slate-50 p-4 rounded-2xl text-center text-sm font-bold text-slate-700 border border-slate-100">
{mod}
</div>
))}
</div>
</div>
</div>
</div>
</div>
</section>
{/* Business Cycle */}
<section className="max-w-7xl mx-auto px-6">
<SectionHeading title="业务闭环:覆盖租赁全生命周期" />
<div className="relative">
<div className="absolute top-1/2 left-0 right-0 h-1 bg-slate-100 -translate-y-1/2 hidden lg:block" />
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 relative z-10">
{[
{ icon: FileText, title: "源头管控", desc: "合同与风控。从客户签约到押金/租金收取,自动关联合同条款,确保资金安全。" },
{ icon: Truck, title: "过程执行", desc: "车务管理。涵盖验车、交车、用车、还车等核心环节,数字化固化标准作业程序。" },
{ icon: Settings, title: "资金结算", desc: "账务闭环。自动化生成应收应付账单,实现费用、违章、保险等精准核算。" }
].map((step, i) => (
<div key={i} className="bg-white p-8 rounded-3xl border border-slate-100 shadow-sm text-center">
<div className="w-16 h-16 bg-brand text-white rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-lg shadow-brand-muted">
<step.icon size={32} />
</div>
<h3 className="text-xl font-bold text-slate-900 mb-3">{step.title}</h3>
<p className="text-slate-500 text-sm leading-relaxed">{step.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* Core Modules */}
<section className="bg-slate-50 py-24">
<div className="max-w-7xl mx-auto px-6">
<SectionHeading title="核心模块详解" subtitle="专业化模块设计,支撑复杂租赁场景" />
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{[
{
title: "合同与资金管理",
tag: "业务基石",
items: ["合同全生命周期管理(创建、审批、变更、续签)", "资金流水透明化(租金、押金、违约金自动计算)", "多种支付方式支持,财务系统实时同步"]
},
{
title: "车务与现场管理",
tag: "执行核心",
items: ["标准化验车流程(拍照上传电子证据,规避纠纷)", "智能调度与交车(车辆状态实时更新,强制证件核对)", "用车监控集成GPS实时位置与轨迹监控"]
},
{
title: "异常与售后处理",
tag: "风险兜底",
items: ["违章与事故处理(记录录入、查询与费用追偿)", "维保与年检自动提醒,形成车辆健康档案", "保险理赔流程清晰指引"]
},
{
title: "结算与报表",
tag: "数据决策",
items: ["自动化结算(根据里程、时长、违章自动生成结算单)", "多维数据分析看板(利用率、营收、信用分析)", "辅助管理层精准决策"]
}
].map((mod, i) => (
<div key={i} className="bg-white p-10 rounded-[32px] border border-slate-200 shadow-sm">
<div className="flex items-center justify-between mb-6">
<h3 className="text-2xl font-bold text-slate-900">{mod.title}</h3>
<span className="px-3 py-1 bg-brand-light text-brand text-[10px] font-black uppercase rounded-full tracking-wider">{mod.tag}</span>
</div>
<ul className="space-y-4">
{mod.items.map((item, j) => (
<li key={j} className="flex gap-3 text-slate-600 text-sm">
<div className="mt-1 flex-shrink-0 w-4 h-4 rounded-full bg-brand-light text-brand flex items-center justify-center">
<ChevronRight size={10} />
</div>
{item}
</li>
))}
</ul>
</div>
))}
</div>
</div>
</section>
{/* Modules Showcase */}
<section className="max-w-7xl mx-auto px-6">
<SectionHeading title="关键功能模块展示" subtitle="直观、易用、专业的管理界面" />
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
{/* 01 工作台 */}
<div className="space-y-6">
<ScreenshotPlaceholder
label="工作台"
description="任务流程一屏统管,业务处理高效协同。"
src={`${BASE}assets/screenshots/01.jpg`}
onClick={() => setSelectedImage(`${BASE}assets/screenshots/01.jpg`)}
/>
<div className="px-2">
<h4 className="text-xl font-bold text-slate-900 mb-2">01. </h4>
<p className="text-brand font-medium mb-3"></p>
<p className="text-slate-500 text-sm leading-relaxed">
</p>
</div>
</div>
{/* 02 车辆档案 */}
<div className="space-y-6">
<ScreenshotPlaceholder
label="车辆全生命周期档案"
description="一屏掌控所有细节"
src={`${BASE}assets/screenshots/02.jpg`}
onClick={() => setSelectedImage(`${BASE}assets/screenshots/02.jpg`)}
/>
<div className="px-2">
<h4 className="text-xl font-bold text-slate-900 mb-2">02. </h4>
<p className="text-brand font-medium mb-3"></p>
<p className="text-slate-500 text-sm leading-relaxed">
</p>
</div>
</div>
{/* 03 业务闭环 */}
<div className="space-y-6">
<ScreenshotPlaceholder
label="全链路业务闭环"
description="覆盖车辆运营全生命周期"
src={`${BASE}assets/screenshots/03.jpg`}
onClick={() => setSelectedImage(`${BASE}assets/screenshots/03.jpg`)}
/>
<div className="px-2">
<h4 className="text-xl font-bold text-slate-900 mb-2">03. </h4>
<p className="text-brand font-medium mb-3"></p>
<p className="text-slate-500 text-sm leading-relaxed">
</p>
</div>
</div>
{/* 04 结算体系 */}
<div className="space-y-6">
<ScreenshotPlaceholder
label="费用明细透明可视"
description="构建信任结算体系"
src={`${BASE}assets/screenshots/04.png`}
onClick={() => setSelectedImage(`${BASE}assets/screenshots/04.png`)}
/>
<div className="px-2">
<h4 className="text-xl font-bold text-slate-900 mb-2">04. </h4>
<p className="text-brand font-medium mb-3"></p>
<p className="text-slate-500 text-sm leading-relaxed">
ETC补缴
</p>
</div>
</div>
{/* 05 移动小程序 */}
<div className="md:col-span-2 bg-slate-50 rounded-[40px] p-8 md:p-12 overflow-hidden">
<div className="max-w-4xl mb-12">
<h4 className="text-2xl font-bold text-slate-900 mb-4">05. </h4>
<p className="text-brand text-lg font-medium mb-4"></p>
<p className="text-slate-500 leading-relaxed mb-4">
线
</p>
</div>
<div className="relative group/scroll">
<div
id="mobile-scroll-container"
className="flex gap-6 overflow-x-auto pb-8 snap-x no-scrollbar scroll-smooth"
>
{[
{ src: `${BASE}assets/screenshots/05.png`, label: "首页概览" },
{ src: `${BASE}assets/screenshots/06.png`, label: "验车流程" },
{ src: `${BASE}assets/screenshots/07.png`, label: "费用录入" },
{ src: `${BASE}assets/screenshots/08.png`, label: "任务列表" },
{ src: `${BASE}assets/screenshots/09.png`, label: "个人中心" },
{ src: `${BASE}assets/screenshots/05.png`, label: "消息通知" },
{ src: `${BASE}assets/screenshots/06.png`, label: "系统设置" },
{ src: `${BASE}assets/screenshots/07.png`, label: "数据分析" },
{ src: `${BASE}assets/screenshots/08.png`, label: "审批中心" },
{ src: `${BASE}assets/screenshots/09.png`, label: "资产看板" },
].map((img, i) => (
<div key={i} className="flex-shrink-0 snap-center w-[240px] md:w-[280px]">
<ScreenshotPlaceholder
label={img.label}
description="移动端实时操作"
type="mobile"
src={img.src}
onClick={() => setSelectedImage(img.src)}
/>
</div>
))}
</div>
{/* Navigation Buttons */}
<button
onClick={() => {
const el = document.getElementById('mobile-scroll-container');
if (el) el.scrollBy({ left: -300, behavior: 'smooth' });
}}
className="absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/90 backdrop-blur rounded-full shadow-xl flex items-center justify-center text-slate-900 hover:bg-brand hover:text-white transition-all opacity-0 group-hover/scroll:opacity-100 z-20"
>
<ChevronRight size={24} className="rotate-180" />
</button>
<button
onClick={() => {
const el = document.getElementById('mobile-scroll-container');
if (el) el.scrollBy({ left: 300, behavior: 'smooth' });
}}
className="absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/90 backdrop-blur rounded-full shadow-xl flex items-center justify-center text-slate-900 hover:bg-brand hover:text-white transition-all opacity-0 group-hover/scroll:opacity-100 z-20"
>
<ChevronRight size={24} />
</button>
<div className="absolute left-0 top-0 bottom-8 w-20 bg-gradient-to-r from-slate-50 to-transparent pointer-events-none z-10" />
<div className="absolute right-0 top-0 bottom-8 w-20 bg-gradient-to-l from-slate-50 to-transparent pointer-events-none z-10" />
</div>
</div>
</div>
</section>
{/* Security */}
<section className="max-w-5xl mx-auto px-6">
<div className="bg-slate-900 rounded-[40px] p-12 text-white flex flex-col md:flex-row items-center gap-12">
<div className="w-24 h-24 bg-brand rounded-3xl flex items-center justify-center flex-shrink-0">
<ShieldCheck size={48} />
</div>
<div>
<h3 className="text-3xl font-bold mb-4"></h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-8">
<div>
<h4 className="text-brand-accent font-bold mb-2"></h4>
<p className="text-slate-400 text-sm leading-relaxed">OneOS平台的安全机制</p>
</div>
<div>
<h4 className="text-brand-accent font-bold mb-2"></h4>
<p className="text-slate-400 text-sm leading-relaxed">RBAC权限管理</p>
</div>
</div>
</div>
</div>
</section>
</div>
);
};
const Footer = () => (
<footer className="bg-slate-50 border-t border-slate-200 py-20">
<div className="max-w-7xl mx-auto px-6">
<div className="flex flex-col md:flex-row justify-between items-center gap-8">
<div className="flex items-center gap-3">
<div className="h-8 flex items-center justify-center overflow-hidden">
<img src={`${BASE}assets/screenshots/logo.png`} alt="Logo" className="h-full w-auto object-contain" referrerPolicy="no-referrer" />
</div>
<span className="text-lg font-bold tracking-tight text-slate-900">OneOS <span className="text-brand">Solutions</span></span>
</div>
<p className="text-slate-500 text-sm">© 2026 OneOS . .</p>
</div>
</div>
</footer>
);
// --- Main App ---
export default function App() {
return (
<div className="min-h-screen bg-white font-sans selection:bg-brand-light selection:text-brand">
<Navbar />
<main>
<AssetManagementSystem />
</main>
<Footer />
</div>
);
}