Compare commits

...

11 Commits

Author SHA1 Message Date
小潘 f02e6555af 取消跟踪IDE与AI缓存目录 2026-06-29 09:35:00 +08:00
小潘 c56f65898b 产品的主题色 2026-06-29 09:27:05 +08:00
小潘 d9a3fc16a4 忽略文件 docs 2026-06-29 09:09:06 +08:00
小潘 9309e3e76a 统一字体 响应式 调整 2026-06-18 14:41:04 +08:00
小潘 68e6e98234 添加国产支持 、 其他样式修改 2026-06-17 14:12:56 +08:00
小潘 f1bc500678 第二次修改风格 2026-06-16 15:41:29 +08:00
小潘 a33d132289 GEO 优化 2026-06-11 16:53:18 +08:00
小潘 40fb2ab22e 添加不同页面有不同的产品展示的主题 2026-06-11 11:36:53 +08:00
小潘 77af4e0dc7 添加 GEO 生成式引擎优化 2026-06-10 14:03:44 +08:00
小潘 0403710e8c 视频压缩 2026-06-10 08:47:25 +08:00
小潘 92c84e9dab 监听滑动动画 2026-06-08 17:36:30 +08:00
78 changed files with 2079 additions and 900 deletions
-10
View File
@@ -1,10 +0,0 @@
{
"permissions": {
"allow": [
"Bash(npm install *)",
"Bash(npx vite *)",
"Bash(npx tailwindcss *)",
"Bash(timeout 10)"
]
}
}
-2
View File
@@ -1,2 +0,0 @@
{
}
@@ -1,163 +0,0 @@
# 产品营销网站设计文档
## 概述
为广西职业院校提供教育信息化产品的营销推广网站。纯前端静态站点,部署到 EdgeOne,域名 `https://products.2009xc.com/`
## 技术栈
- **构建工具**Vite
- **框架**React 18 + React Router v6
- **样式**Tailwind CSS v3 + CSS 变量(主题色切换)
- **部署**:静态文件,EdgeOne CDN
## 页面结构
### 路由设计
| 路由 | 页面 | 说明 |
|------|------|------|
| `/` | 首页 | Hero区 + 产品概览 + 信任背书 + 新闻动态 |
| `/product/:slug` | 产品详情页 | 通用模板,根据slug加载产品数据 |
| `/contact` | 联系我们/成为代理商 | 代理优势 + 申请表单 + 联系方式 |
### 目录结构
```
website
/
├── public/ # 静态资源
├── src/
│ ├── components/
│ │ ├── Navbar.jsx # 顶部导航栏(固定、响应式汉堡菜单)
│ │ ├── Footer.jsx # 页脚
│ │ ├── HeroSection.jsx # 首页Hero大图区
│ │ ├── ProductCard.jsx # 产品概览卡片
│ │ ├── TrustSection.jsx # 信任背书区(Logo墙+评价+数据)
│ │ ├── NewsSection.jsx # 新闻动态区
│ │ ├── DemoSection.jsx # 演示体验模块(网址+二维码+账号密码)
│ │ ├── ContactForm.jsx # 代理商申请表单
│ │ └── ThemeProvider.jsx# 主题色上下文(CSS变量切换)
│ ├── pages/
│ │ ├── Home.jsx
│ │ ├── ProductPage.jsx # 通用产品详情页模板
│ │ └── Contact.jsx
│ ├── data/
│ │ └── products.js # 产品数据配置
│ ├── App.jsx
│ └── main.jsx
├── index.html
├── tailwind.config.js
├── vite.config.js
└── package.json
```
## 首页设计
### 1. 导航栏
- 公司Logo(左)+ 产品菜单(下拉展示所有产品)+ "联系我们"按钮(右)
- 滚动后固定顶部,添加阴影
- 移动端折叠为汉堡菜单
### 2. Hero 大图区
- 全屏渐变背景(深蓝→蓝紫渐变)
- 公司标语:"智慧教育,赋能未来"(示例)
- 副标题:一句话描述公司使命
- CTA按钮:"了解我们的产品"
### 3. 产品概览区
- 2×3 网格卡片布局
- 每张卡片:SVG图标 + 产品名称 + 一句话简介 + "了解更多"按钮
- hover效果:上浮+阴影增强
### 4. 信任背书区
- 合作院校Logo墙(示例占位)
- 关键数据统计:"服务100+所院校"、"覆盖50万+师生"(示例)
- 客户评价轮播
### 5. 新闻动态区
- 3-4条最新动态卡片(标题+摘要+日期)
- 示例占位内容
### 6. 页脚
- 公司信息 + 联系方式 + 版权 + 备案号
## 产品详情页设计
通过URL参数 `/product/:slug``products.js` 加载数据,所有产品共享同一模板。
### 页面结构(从上到下)
1. **产品头部** — 全宽渐变背景(产品主题色),产品名称 + Slogan + 产品截图/插画
2. **核心价值** — 3-4个价值点(图标+标题+描述),营销化文案
3. **功能亮点** — 左右交替布局,图文并茂,每个功能一个区块
4. **应用场景** — 针对职业院校的实际使用场景
5. **演示体验模块** — 演示网址(可点击)+ 二维码(SVG占位)+ 体验账号密码(卡片)
6. **其他产品推荐** — "您可能还感兴趣"卡片
7. **CTA** — "立即联系我们"按钮
## 主题色系统
通过 CSS 变量 + ThemeProvider 上下文实现每个产品页面的主题色切换。
| 产品 | slug | 主题色 | 色值 |
|------|------|--------|------|
| 智慧校园管理平台 | smart-campus | 蓝色 | #2563EB |
| 实习实训管理平台 | internship | 绿色 | #059669 |
| 教学资源管理平台 | teaching-resource | 紫色 | #7C3AED |
| 考试测评系统 | exam-system | 橙色 | #EA580C |
| 就业服务平台 | career-service | 青色 | #0891B2 |
导航栏和页脚使用公司统一配色(深色),不受产品主题色影响。
## 联系我们/成为代理商页面
1. **代理优势区** — 4-6个加盟优势卡片(政策支持、培训赋能、丰厚回报、品牌背书等)
2. **代理流程** — 横向步骤条:咨询 → 签约 → 培训 → 开业
3. **在线申请表单** — 姓名、公司名称、联系电话、邮箱、所在地区、代理意向产品(下拉)、备注
4. **联系方式区** — 电话、邮箱、地址、微信公众号二维码
表单仅做前端验证,展示提交成功提示(无后端处理)。
## 响应式断点
- **桌面端** ≥1024px:完整布局,双栏图文交替
- **平板端** 768-1023px:间距调整,卡片2列
- **移动端** <768px:单栏,汉堡菜单
## 设计风格
- 科技感/现代风,参考阿里云/腾讯云产品页风格
- 大色块渐变、圆角卡片、适当的阴影和动效
- 中文排版,适合中国用户习惯
- SVG图标和装饰元素增加美观
- 文案以营销宣传为主,不深入技术细节
## 产品数据结构
```js
// src/data/products.js
{
slug: 'smart-campus',
name: '校企益邦',
slogan: '数字化管理,让校园更智慧',
themeColor: '#2563EB',
icon: '...', // SVG图标组件名
brief: '...', // 一句话简介(首页卡片用)
values: [...], // 核心价值点
features: [...], // 功能亮点
scenarios: [...], // 应用场景
demo: {
url: '...',
qrCode: '...', // 二维码图片路径
account: '...',
password: '...'
}
}
```
## 部署
- `vite build` 输出到 `website/dist/`
- 上传到 EdgeOne,绑定域名 `products.2009xc.com`
- Vite 配置 `base: '/'`
+2
View File
@@ -14,6 +14,8 @@ dist-ssr
# Editor directories and files
.vscode/*
.claude/*
docs/*
!.vscode/extensions.json
.idea
.DS_Store
+130 -4
View File
@@ -4,11 +4,137 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/css2?family=ZCOOL+QingKe+HuangYou&family=ZCOOL+XiaoWei&family=Long+Cang&family=Ma+Shan+Zheng&family=Zhi+Mang+Xing&family=Noto+Serif+SC:wght@600;700;900&display=swap" rel="stylesheet">
<title>广西讯驰 - 智慧教育产品</title>
</head>
<!-- ===== GEO: 语义化 meta 标签 ===== -->
<meta name="description" content="广西讯驰是专注智慧教育领域的科技企业,为职业院校和高校提供实习就业管理、科研管理、智慧党建、实训管理平台等数字化解决方案,已服务150+所院校、覆盖5万+学生。" />
<meta name="keywords" content="智慧教育,实习管理平台,科研管理系统,智慧党建,实训管理平台,广西讯驰,职业院校信息化" />
<meta name="baidu-site-verification" content="codeva-N8Wnw7Njp2" />
<title>广西讯驰 - 智慧教育数字化解决方案 | 校企益邦 · 科研管理 · 智慧党建 · 实训管理平台</title>
<!-- ===== GEO: Schema.org JSON-LD 结构化数据——AI 直接解析企业信息 ===== -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "广西讯驰",
"description": "专注智慧教育领域的科技企业,为职业院校和高校提供数字化解决方案",
"url": "https://products.2009xc.com/",
"logo": "https://products.2009xc.com/favicon.ico",
"foundingDate": "2010",
"areaServed": "中国",
"knowsAbout": ["校企益邦", "科研管理系统", "智慧党建", "实训管理平台"],
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+86-771-3864294",
"contactType": "customer service"
}
}
</script>
<!-- FAQPage 结构化数据——AI 偏好的问答格式 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "广西讯驰提供哪些智慧教育解决方案?",
"acceptedAnswer": {
"@type": "Answer",
"text": "核心产品包括校企益邦(实习就业管理平台)、科研管理系统、智慧党建平台、实训管理平台实训系统,覆盖实习管理、科研管理、党建数字化、商科实训四大领域。"
}
},
{
"@type": "Question",
"name": "广西讯驰服务了多少院校?",
"acceptedAnswer": {
"@type": "Answer",
"text": "已服务超过150所职业院校和高校,覆盖5万+学生,客户主要分布在广西及周边省份。"
}
},
{
"@type": "Question",
"name": "校企益邦实习管理平台有哪些功能?",
"acceptedAnswer": {
"@type": "Answer",
"text": "校企益邦涵盖实习岗位发布与匹配、学生实习过程跟踪、实习报告与评价、校企双导师协同管理、数据统计分析等全流程功能。"
}
}
]
}
</script>
</head>
<body>
<!-- React 挂载点 -->
<div id="root"></div>
<!-- ===== GEO: noscript 内容——AI 爬虫不执行 JS,从这里读取 ===== -->
<noscript>
<header>
<h1>广西讯驰</h1>
<p><strong>专注智慧教育领域的科技企业</strong></p>
<p>成立于2010年,总部位于广西南宁,为职业院校和高校提供数字化解决方案。</p>
<ul>
<li>已服务 <strong>150+</strong> 所院校</li>
<li>覆盖 <strong>5万+</strong> 学生</li>
</ul>
</header>
<section>
<h2>核心产品和服务</h2>
<ul>
<li>
<h3>校企益邦(实习就业管理平台)</h3>
<p>面向职业院校实习就业管理的专业化平台,覆盖实习计划制定、过程跟踪、数据采集、鉴定评价全流程,已服务广西多所职业院校,帮助学校轻松应对教育厅数据上报要求。</p>
</li>
<li>
<h3>科研管理系统</h3>
<p>面向高校及科研院所的全生命周期科研业务管理平台。覆盖项目申报、评审立项、经费管控、成果登记到数据统计的全流程数字化,已帮助多所高校实现科研管理从纸质流转到"一网通办"的跨越。</p>
</li>
<li>
<h3>智慧党建平台</h3>
<p>面向高校及职业院校各级党组织的智慧党建管理系统。以"党业融合、指标驱动、全程留痕"为核心理念,将三会一课、党员活动、工作记录等日常工作自动关联考核指标,实现年终考核"零重复填报"。</p>
</li>
<li>
<h3>实训管理平台</h3>
<p>专为职业院校商科专业群打造的实训项目管理系统。以"项目—模块—任务"三级结构为骨架,以"咖豆积分+技能画像"为引擎,将抽象的五级进阶培养模式变成可量化、可追踪、可激励的线上实战平台。</p>
</li>
</ul>
</section>
<section>
<h2>常见问题</h2>
<h3>校企益邦实习管理平台和其他平台有什么不同?</h3>
<p>校企益邦专为职业院校场景设计,支持集中实习和自主实习两种模式,提供企业资质审核、实习保险对接、工学交替打卡、实习成绩多维评价等功能,已适配广西、湖南等多省教育厅实习管理数据上报标准。</p>
<h3>系统支持私有化部署吗?</h3>
<p>支持。所有产品均提供SaaS云服务和私有化部署两种方案,满足院校对数据安全和合规性的不同要求。</p>
<h3>科研管理系统能否对接省级课题管理平台?</h3>
<p>可以。科研管理系统已与广西教育厅、广西科技厅等省级课题管理平台实现数据对接,支持项目申报、中检、结题等环节的数据同步。</p>
</section>
<section>
<h2>资质与荣誉</h2>
<ul>
<li>国家高新技术企业</li>
<li>通过ISO 9001质量管理体系认证</li>
<li>多项软件著作权(实习管理、科研管理、智慧党建、商科实训等)</li>
</ul>
</section>
<footer>
<p><strong>广西讯驰信息科技有限公司</strong></p>
<p>地址:广西南宁市 | 电话:0771-3864294</p>
<p>官网:https://products.2009xc.com/</p>
<p>页面更新时间:2026年6月10日</p>
</footer>
</noscript>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

+76
View File
@@ -0,0 +1,76 @@
# 广西讯驰 - 智慧教育数字化解决方案
> 广西讯驰信息科技有限公司(讯驰科技 / XUNCHI TECH),成立于 2010 年,总部位于广西南宁,专注为职业院校和高校提供智慧教育数字化解决方案。国家高新技术企业,已服务 150+ 所院校、覆盖 5 万+ 学生。
## 核心页面
- [首页](https://products.2009xc.com/): 企业概览、核心数据、四大产品展示、资质荣誉
- [产品详情](https://products.2009xc.com/product/smart-campus): 各产品详情页(痛点、功能、优势、FAQ),通过 /product/{slug} 动态路由访问
- [合作咨询](https://products.2009xc.com/contact): 联系方式、代理合作权益、在线咨询表单
## 核心产品
### 校企益邦(实习就业管理平台)
- 详情页: https://products.2009xc.com/product/smart-campus
- 面向职业院校实习就业管理的专业化平台,覆盖实习计划制定、过程跟踪、数据采集、鉴定评价全流程。已服务 20+ 所院校、50,000+ 学生、100 万+ 累计签到。
- 核心功能:实习计划管理、GPS 打卡 + 电子围栏、问卷式日志、多维度评价、一键同步省级教育平台、微信小程序
- 解决痛点:实习管理缺乏统一执行方案、教师数据采集负担重、学生参与度低
### 科研管理系统
- 详情页: https://products.2009xc.com/product/exam-system
- 面向高校及科研院所的全生命周期科研业务管理平台,覆盖项目申报、评审立项、经费管控、成果登记到数据统计。已服务 10+ 高校、管理 3,000+ 项目、登记 8,000+ 成果。
- 核心功能:在线申报审批、智能查重、经费管控、成果自动计分、关键节点提醒、一键年报导出
- 解决痛点:纸质流程繁琐、人工审核工作量大、经费管理复杂、统计汇总困难
### 智慧党建平台
- 详情页: https://products.2009xc.com/product/teaching-resource
- 面向高校各级党组织的智慧党建管理系统。以"党业融合、指标驱动、全程留痕"为核心理念,将三会一课、党员活动、工作记录自动关联考核指标。已覆盖 150+ 党支部、管理 5,000+ 党员。
- 核心功能:三级考核指标体系、三会一课数字化、万能工作记录、实时进度看板、党员发展管理、党建数据中心
- 解决痛点:党建与业务两张皮、年终突击补材料、会议组织与留痕困难、分级考核存在盲区
### 实训管理平台
- 详情页: https://products.2009xc.com/product/internship
- 专为职业院校商科专业群打造的实训项目管理系统。以"项目—模块—任务"三级结构为骨架,以"咖豆积分 + 技能画像"为引擎,将五级进阶培养模式变成可量化、可追踪的线上实战平台。已积累 500+ 实训项目、覆盖 15+ 专业。
- 核心功能:三级精细化管理、咖豆积分引擎、五级自动晋升、动态技能画像、人才培养驾驶舱、积分商城
- 解决痛点:渐进式人才培养难以落地、实训项目数据分散、技能成长过程不可见
## 常见问题
### 系统支持本地化/私有化部署吗?
支持。所有产品均提供 SaaS 云服务和私有化部署两种方案,满足院校对数据安全和合规性的不同要求。
### 可以对接学校现有的教务/人事系统吗?
可以。所有产品均提供标准 API 接口,支持与学校现有系统对接。
### 售后服务和技术支持是怎样的?
提供 7x24 小时技术支持,包括远程运维、定期巡检、系统升级、用户培训等。
### 数据安全如何保障?
通过国家信息安全等级保护三级认证,采用银行级数据加密、多活灾备、细粒度权限控制。
## 企业资质
- 国家高新技术企业
- AAA 级信用企业
- ISO 9001 质量管理体系认证
- ISO 14001 环境管理体系认证
- ISO 45001 职业健康安全管理体系认证
- ISO 27001 信息安全管理体系认证
- ISO 20000 信息技术服务管理体系认证
- 40+ 项软件著作权
- 16 年行业经验
## 联系方式
- 官网: https://products.2009xc.com/
- 电话: 0771-3864294
- 地址: 广西南宁市
- ICP 备案: 桂ICP备10003405号-1
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- 首页 -->
<url>
<loc>https://products.2009xc.com/</loc>
<lastmod>2026-06-10</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<!-- 产品页 -->
<url>
<loc>https://products.2009xc.com/product/smart-campus</loc>
<lastmod>2026-06-10</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://products.2009xc.com/product/exam-system</loc>
<lastmod>2026-06-10</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://products.2009xc.com/product/teaching-resource</loc>
<lastmod>2026-06-10</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://products.2009xc.com/product/internship</loc>
<lastmod>2026-06-10</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<!-- 联系页 -->
<url>
<loc>https://products.2009xc.com/contact</loc>
<lastmod>2026-06-10</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
</urlset>
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 KiB

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 255 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 46 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

+140
View File
@@ -0,0 +1,140 @@
import { useState, useEffect, useCallback, useRef } from 'react'
import { X } from 'lucide-react'
export default function Carousel({ images, interval = 4000, themeColor }) {
const [current, setCurrent] = useState(0)
const [prevSrc, setPrevSrc] = useState(null)
const [isTransitioning, setIsTransitioning] = useState(false)
const [preview, setPreview] = useState(null)
const timerRef = useRef(null)
const busyRef = useRef(false)
const goTo = useCallback((index) => {
if (index === current || busyRef.current) return
busyRef.current = true
setPrevSrc(images[current]?.url)
setCurrent(index)
// rAF x2 to ensure the DOM has rendered the new image before starting transition
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setIsTransitioning(true)
})
})
// After transition completes
setTimeout(() => {
setIsTransitioning(false)
setPrevSrc(null)
busyRef.current = false
}, 500)
}, [current, images])
const next = useCallback(() => {
goTo((current + 1) % images.length)
}, [current, images.length, goTo])
useEffect(() => {
if (preview !== null) return
timerRef.current = setInterval(next, interval)
return () => clearInterval(timerRef.current)
}, [next, interval, preview])
const handleImageClick = (index) => {
setPreview(index)
}
const handleClosePreview = () => {
setPreview(null)
}
return (
<>
<div >
{/* Center - monitor frame (static, no animation) */}
<div
className="relative z-30 cursor-pointer"
style={{ '--monitor-theme': themeColor }}
onClick={() => handleImageClick(current)}
>
{/* 显示器外框 */}
<div className="monitor-frame">
<div className="monitor-screen relative">
{/* 旧图片 — 交叉淡出 */}
{prevSrc && (
<img
src={prevSrc}
alt=""
className="absolute inset-0 w-full h-full object-contain transition-opacity duration-500"
style={{ opacity: isTransitioning ? 0 : 1 }}
/>
)}
{/* 新图片 — 交叉淡入 */}
<img
src={images[current]?.url}
alt=""
className="absolute inset-0 w-full h-full object-contain transition-opacity duration-500"
style={{ opacity: prevSrc ? (isTransitioning ? 1 : 0) : 1 }}
/>
</div>
{/* 底部下巴 */}
<div className="monitor-chin">
<span className="monitor-chin-label">{images[current]?.name || ''}</span>
</div>
</div>
{/* 支架 */}
<div className="monitor-stand-neck" />
<div className="monitor-stand-base" />
</div>
</div>
{/* Dot indicators */}
<div className="flex justify-center gap-2 mt-2">
{images.map((_, i) => (
<button
key={i}
onClick={() => i !== current && goTo(i)}
className="rounded-full transition-all duration-500"
style={{
width: i === current ? '24px' : '8px',
height: '8px',
backgroundColor: i === current ? themeColor : '#d1d5db',
}}
/>
))}
</div>
{/* Image name — lines on both sides */}
<div className="text-center mt-5">
<div className="flex items-center justify-center gap-5 md:gap-7">
<span className="flex-1 h-[3px] max-w-[80px] md:max-w-[140px] rounded-full" style={{ background: `linear-gradient(90deg, transparent, ${themeColor}30)` }} />
<span className="text-lg md:text-2xl font-bold tracking-[0.08em]"
style={{ color: `${themeColor}aa` }}
>
{images[current]?.name || ''}
</span>
<span className="flex-1 h-[3px] max-w-[80px] md:max-w-[140px] rounded-full" style={{ background: `linear-gradient(90deg, ${themeColor}30, transparent)` }} />
</div>
</div>
{/* Preview Modal */}
{preview !== null && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4"
onClick={handleClosePreview}
>
<button
className="absolute top-6 right-6 z-10 w-10 h-10 rounded-full bg-white/20 hover:bg-white/30 flex items-center justify-center text-white transition-colors"
onClick={handleClosePreview}
>
<X className="w-6 h-6" />
</button>
<img
src={images[preview].url}
alt=""
className="max-w-full max-h-[85vh] object-contain rounded-2xl"
onClick={(e) => e.stopPropagation()}
/>
</div>
)}
</>
)
}
+188
View File
@@ -0,0 +1,188 @@
import React from 'react'
import ScrollReveal from './ScrollReveal'
import { Award, Leaf, Shield, Lock, Wrench } from 'lucide-react'
/* ─── 证书数据 — 统一蓝色 ─── */
const certificates = [
{
id: 'iso9001',
icon: Award,
code: 'ISO 9001',
name: '质量管理体系认证',
date: '2024年3月',
color: '#004ec4',
sealColor: '#004ec4',
},
{
id: 'iso14001',
icon: Leaf,
code: 'ISO 14001',
name: '环境管理体系认证',
date: '2024年3月',
color: '#004ec4',
sealColor: '#004ec4',
},
{
id: 'iso45001',
icon: Shield,
code: 'ISO 45001',
name: '职业健康安全管理体系认证',
date: '2024年3月',
color: '#004ec4',
sealColor: '#004ec4',
},
{
id: 'iso27001',
icon: Lock,
code: 'ISO 27001',
name: '信息安全管理体系认证',
date: '2024年3月',
color: '#004ec4',
sealColor: '#004ec4',
},
{
id: 'iso20000',
icon: Wrench,
code: 'ISO 20000',
name: '信息技术服务管理体系认证',
date: '2024年3月',
color: '#004ec4',
sealColor: '#004ec4',
},
]
export default function CertificateCard({ certificate, index, scale = 1 }) {
const { icon: Icon, code, name, desc, color } = certificate
return (
<ScrollReveal delay={index * 0.08}>
<div className="group relative transition-all duration-500 w-[260px] h-[380px] sm:w-[300px] sm:h-[440px] md:w-[360px] md:h-[500px]"
style={{ transform: `scale(${scale})` }}
>
{/* ── 外阴影 ── */}
<div className="absolute inset-0 rounded-sm translate-y-1.5 translate-x-1 opacity-25 group-hover:opacity-35 transition-opacity duration-500"
style={{ background: `linear-gradient(135deg, ${color}40, ${color}15)` }}
/>
{/* ── 证书主体 ── */}
<div className="relative rounded-sm h-full flex flex-col"
style={{ backgroundColor: '#faf7ed' }}
>
{/* 外层粗边框 */}
<div className="absolute inset-0 border-[3px] rounded-sm"
style={{ borderColor: `${color}50` }}
/>
{/* 中层装饰线 */}
<div className="absolute inset-[3px] border rounded-sm"
style={{ borderColor: `${color}25` }}
/>
{/* 内层双线框 */}
<div className="absolute inset-[7px] border rounded-sm"
style={{ borderColor: `${color}18` }}
/>
<div className="absolute inset-[9px] border rounded-sm"
style={{ borderColor: `${color}0f` }}
/>
{/* 四边中点装饰 */}
<BorderCenterOrnament position="top" color={color} />
<BorderCenterOrnament position="bottom" color={color} />
<BorderCenterOrnament position="left" color={color} />
<BorderCenterOrnament position="right" color={color} />
{/* 内容区 */}
<div className="relative px-6 py-6 sm:px-8 sm:py-8 md:px-10 md:py-10 flex-1 flex flex-col items-center text-center justify-center">
{/* 认证图标 */}
<div className="relative mb-3 sm:mb-4">
<div className="w-12 h-12 sm:w-14 sm:h-14 md:w-16 md:h-16 rounded-full border-2 flex items-center justify-center group-hover:scale-110 transition-transform duration-500"
style={{ borderColor: `${color}40`, backgroundColor: `${color}08` }}
>
<Icon className="w-6 h-6 sm:w-7 sm:h-7 md:w-8 md:h-8" style={{ color }} strokeWidth={1.5} />
</div>
</div>
{/* 认证编号 */}
<div className="text-base sm:text-lg md:text-xl font-black tracking-[0.15em] mb-1 sm:mb-2" style={{ color }}>
{code}
</div>
{/* 认证名称 */}
<div className="text-sm sm:text-base font-bold text-[#1e293b] mb-3 sm:mb-4 leading-tight">
{name}
</div>
{/* 分隔线 — 带花纹 */}
<div className="flex items-center gap-2 mb-3 sm:mb-4 w-full max-w-[120px] sm:max-w-[140px] md:max-w-[160px]">
<div className="flex-1 h-px" style={{ background: `linear-gradient(90deg, transparent, ${color}50)` }} />
<div className="flex gap-0.5">
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: `${color}60` }} />
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: `${color}40` }} />
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: `${color}60` }} />
</div>
<div className="flex-1 h-px" style={{ background: `linear-gradient(90deg, ${color}50, transparent)` }} />
</div>
{/* 证明文字 */}
<p className="text-xs sm:text-sm text-[#64748b] leading-relaxed mb-4 sm:mb-5 max-w-[240px] sm:max-w-[280px] md:max-w-[320px]">
{desc}
</p>
</div>
{/* 底部蓝色线条 */}
<div className="relative h-1.5 bg-gradient-to-r from-blue-600 to-blue-800">
{/* 折角 */}
<div className="absolute left-0 top-0 w-1.5 h-1.5"
style={{ background: 'linear-gradient(135deg, #faf7ed 50%, transparent 50%)' }}
/>
<div className="absolute right-0 top-0 w-1.5 h-1.5"
style={{ background: 'linear-gradient(225deg, #faf7ed 50%, transparent 50%)' }}
/>
</div>
</div>
{/* hover 顶部高光线 */}
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-t-sm"
style={{ background: `linear-gradient(90deg, transparent, ${color}, transparent)` }}
/>
</div>
</ScrollReveal>
)
}
/* ─── 边框中点装饰 ─── */
function BorderCenterOrnament({ position, color }) {
const posMap = {
'top': 'top-[3px] left-1/2 -translate-x-1/2',
'bottom': 'bottom-[3px] left-1/2 -translate-x-1/2',
'left': 'left-[3px] top-1/2 -translate-y-1/2',
'right': 'right-[3px] top-1/2 -translate-y-1/2',
}
const isHorizontal = position === 'top' || position === 'bottom'
return (
<div className={`absolute ${posMap[position]} flex ${isHorizontal ? 'flex-row' : 'flex-col'} items-center gap-0.5`}
style={{ opacity: 0.5 }}
>
<div className="w-2 h-2 rounded-full border" style={{ borderColor: `${color}40`, backgroundColor: `${color}10` }} />
<div className="w-1 h-1 rounded-full" style={{ backgroundColor: `${color}50` }} />
<div className="w-2 h-2 rounded-full border" style={{ borderColor: `${color}40`, backgroundColor: `${color}10` }} />
</div>
)
}
export { certificates }
@@ -0,0 +1,94 @@
import React, { useState, useEffect, useCallback } from 'react'
import CertificateCard, { certificates } from './CertificateCard'
export default function CertificateCarousel() {
const [active, setActive] = useState(2) // 从中间开始 (ISO 45001)
const [isMobile, setIsMobile] = useState(false)
// 检测是否为移动端
useEffect(() => {
const check = () => setIsMobile(window.innerWidth < 768)
check()
window.addEventListener('resize', check)
return () => window.removeEventListener('resize', check)
}, [])
const next = useCallback(() => {
setActive(prev => (prev + 1) % certificates.length)
}, [])
// 自动轮播
useEffect(() => {
const timer = setInterval(next, 3000)
return () => clearInterval(timer)
}, [next])
const goTo = (index) => setActive(index)
// 响应式参数
const containerHeight = isMobile ? 'h-[360px]' : 'h-[600px]'
const translateXBase = isMobile ? 140 : 260
const scaleCenter = 1.0
const scaleNear = isMobile ? 0.8 : 0.7
const scaleFar = isMobile ? 0.5 : 0.4
return (
<div className="relative max-w-4xl mx-auto">
{/* 证书展示区 */}
<div className={`relative ${containerHeight} flex items-center justify-center`}>
{certificates.map((cert, i) => {
let offset = i - active
// 循环环绕:取最短路径
if (offset > 2) offset = offset - certificates.length
if (offset < -2) offset = offset + certificates.length
const absOffset = Math.abs(offset)
let scale = scaleCenter
if (absOffset === 1) scale = scaleNear
if (absOffset >= 2) scale = scaleFar
let opacity = 1
if (absOffset === 1) opacity = 0.6
if (absOffset >= 2) opacity = 0
let translateX = offset * translateXBase
let zIndex = 10 - absOffset
return (
<div
key={cert.id}
className="absolute transition-all duration-500 ease-in-out cursor-pointer"
style={{
transform: `translateX(${translateX}px) scale(${scale})`,
opacity,
zIndex,
filter: absOffset >= 2 ? 'blur(2px)' : 'none',
}}
onClick={() => goTo(i)}
>
<CertificateCard certificate={cert} index={i} scale={isMobile ? 0.7 : 1} />
</div>
)
})}
</div>
{/* 底部指示点 */}
<div className="flex justify-center gap-2 mt-4">
{certificates.map((cert, i) => (
<button
key={cert.id}
onClick={() => goTo(i)}
className={`w-2 h-2 rounded-full transition-all duration-300 ${
i === active
? 'bg-[#004ec4] w-6'
: 'bg-[#004ec4]/30 hover:bg-[#004ec4]/50'
}`}
/>
))}
</div>
</div>
)
}
+19 -19
View File
@@ -3,7 +3,7 @@ import { CheckCircle, X } from 'lucide-react';
const productOptions = [
'校企益邦',
'商咖五级进阶',
'实训管理平台',
'智慧党建平台',
'科研管理系统',
];
@@ -37,7 +37,7 @@ export default function ContactForm() {
setShowModal(false);
}
const inputClass = 'w-full px-4 py-3 border border-gray-200 rounded-xl text-base focus:outline-none focus:ring-2 focus:ring-[#043b8f]/20 focus:border-[#043b8f]/40 transition-shadow placeholder-[#94a3b8] bg-white text-[#334155]';
const inputClass = 'w-full px-4 py-3 border border-gray-200 rounded-xl text-base focus:outline-none focus:ring-2 focus:ring-[#004ec4]/30 focus:border-[#004ec4]/60 transition-shadow placeholder-[#94a3b8] bg-white text-[#334155]';
return (
<>
@@ -46,21 +46,21 @@ export default function ContactForm() {
<div className="text-center mb-16">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">Inquiry</span>
<span className="w-5 h-[2px] rounded-full bg-[#004ec4]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#004ec4]">Inquiry</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
在线咨询
</h2>
<p className="text-[#475569] text-lg md:text-xl mt-5 max-w-xl leading-relaxed">填写表单我们将在 1 个工作日内与您联系</p>
<p className="text-[#475569] text-sm sm:text-base md:text-lg lg:text-xl mt-4 md:mt-5 max-w-xl leading-relaxed">填写表单我们将在 1 个工作日内与您联系</p>
</div>
</div>
<div className="relative bg-white rounded-2xl p-8 md:p-10 border border-gray-100 overflow-hidden max-w-3xl mx-auto">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-100"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[#043b8f]/[0.02] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[#043b8f]/[0.06] rounded-tr-3xl" />
style={{ background: 'linear-gradient(90deg, transparent, #004ec4, transparent)' }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[#004ec4]/[0.06] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[#004ec4]/[0.15] rounded-tr-3xl" />
<div className="relative z-10">
<form onSubmit={handleSubmit} className="space-y-8">
@@ -91,7 +91,7 @@ export default function ContactForm() {
关注产品 <span className="text-[#94a3b8] text-sm">(可选)</span>
</label>
<select id="product" name="product" value={form.product} onChange={handleChange}
className="w-full px-4 py-3 border border-gray-200 rounded-xl text-base focus:outline-none focus:ring-2 focus:ring-[#043b8f]/20 focus:border-[#043b8f]/40 transition-shadow bg-white text-[#64748b]">
className="w-full px-4 py-3 border border-gray-200 rounded-xl text-base focus:outline-none focus:ring-2 focus:ring-[#004ec4]/30 focus:border-[#004ec4]/60 transition-shadow bg-white text-[#64748b]">
<option value="">请选择关注的产品</option>
{productOptions.map((product) => (
<option key={product} value={product}>{product}</option>
@@ -107,8 +107,8 @@ export default function ContactForm() {
<button
type="submit"
className="w-full py-4 text-white font-bold rounded-xl text-base transition-all focus:outline-none focus:ring-2 focus:ring-[#043b8f]/20 focus:ring-offset-2 hover:shadow-lg hover:shadow-[#043b8f]/20"
style={{ background: 'linear-gradient(135deg, #043b8f, #0659c7)' }}
className="w-full py-4 text-white font-bold rounded-xl text-base transition-all focus:outline-none focus:ring-2 focus:ring-[#004ec4]/30 focus:ring-offset-2 hover:shadow-lg hover:shadow-[#004ec4]/30"
style={{ background: 'linear-gradient(135deg, #004ec4, #0659c7)' }}
>
提交咨询
</button>
@@ -123,8 +123,8 @@ export default function ContactForm() {
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={closeModal} />
<div className="relative bg-white rounded-2xl p-8 md:p-10 max-w-md w-full shadow-2xl overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px]"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
<div className="absolute top-0 right-0 w-20 h-20 bg-[#043b8f]/[0.02] rounded-bl-full" />
style={{ background: 'linear-gradient(90deg, transparent, #004ec4, transparent)' }} />
<div className="absolute top-0 right-0 w-20 h-20 bg-[#004ec4]/[0.06] rounded-bl-full" />
<button onClick={closeModal}
className="absolute top-4 right-4 w-8 h-8 rounded-full bg-gray-50 flex items-center justify-center hover:bg-gray-100 transition-colors">
@@ -132,16 +132,16 @@ export default function ContactForm() {
</button>
<div className="relative z-10 text-center">
<div className="w-16 h-16 rounded-full bg-[#043b8f]/[0.06] border border-[#043b8f]/10 flex items-center justify-center mx-auto mb-6">
<CheckCircle className="w-8 h-8 text-[#043b8f]" />
<div className="w-16 h-16 rounded-full bg-[#004ec4]/[0.12] border border-[#004ec4]/20 flex items-center justify-center mx-auto mb-6">
<CheckCircle className="w-8 h-8 text-[#004ec4]" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">提交成功</h3>
<p className="text-[#64748b] text-base leading-relaxed mb-8">
感谢您的咨询我们将在 <span className="font-bold text-[#043b8f]">1 个工作日</span> 内与您联系
感谢您的咨询我们将在 <span className="font-bold text-[#004ec4]">1 个工作日</span> 内与您联系
</p>
<button onClick={closeModal}
className="w-full py-3.5 text-white font-bold rounded-xl text-base transition-all hover:shadow-lg hover:shadow-[#043b8f]/20"
style={{ background: 'linear-gradient(135deg, #043b8f, #0659c7)' }}>
className="w-full py-3.5 text-white font-bold rounded-xl text-base transition-all hover:shadow-lg hover:shadow-[#004ec4]/30"
style={{ background: 'linear-gradient(135deg, #004ec4, #0659c7)' }}>
我知道了
</button>
</div>
+5 -5
View File
@@ -20,16 +20,16 @@ export default function Footer() {
<div className="md:px-8">
<div className="flex items-center justify-center gap-2.5 mb-3 whitespace-nowrap">
<img src="/favicon.ico" alt="讯驰" className="w-8 h-8" />
<span className="text-lg font-bold text-white tracking-tight">讯驰科技</span>
<span className="text-base sm:text-lg font-bold text-white tracking-tight">讯驰科技</span>
</div>
<p className="text-white/60 text-xs leading-loose mb-4">智慧校园解决方案提供商</p>
<p className="text-white/60 text-xs sm:text-sm leading-loose mb-4">智慧校园解决方案提供商</p>
<div className="flex items-start justify-center gap-2 mb-3">
<MapPin className="w-3.5 h-3.5 text-white/50 mt-0.5 shrink-0" />
<span className="text-white/70 text-xs leading-loose">南宁市总部路3号中国东盟科技企业孵化基地二期9号厂房二楼212房</span>
<span className="text-white/70 text-xs sm:text-sm leading-loose">南宁市总部路3号中国东盟科技企业孵化基地二期9号厂房二楼212房</span>
</div>
<div className="flex items-center justify-center gap-2">
<Phone className="w-3.5 h-3.5 text-white/50 shrink-0" />
<span className="text-white/70 text-xs">0771-3864294</span>
<span className="text-white/70 text-xs sm:text-sm">0771-3864294</span>
</div>
</div>
@@ -77,7 +77,7 @@ export default function Footer() {
{/* Row 2: Copyright */}
<div className="pt-6 border-t border-white/15">
<p className="text-white/50 text-xs text-center">&copy; 2024 讯驰科技 版权所有 &middot; 桂ICP备XXXXXXXX号</p>
<p className="text-white/50 text-xs text-center">&copy; 2026 讯驰科技 版权所有 &middot;备案号桂ICP备10003405号-1</p>
</div>
</div>
</footer>
+82 -94
View File
@@ -1,85 +1,86 @@
import { ArrowRight } from 'lucide-react';
import { useEffect, useRef } from 'react';
import bag from '../../public/bag.mp4'
// import { useEffect, useRef } from 'react';
function ParticleNetwork() {
const canvasRef = useRef(null);
// function ParticleNetwork() {
// const canvasRef = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
let animId;
let particles = [];
const COUNT = 35;
const CONNECT_DIST = 150;
// useEffect(() => {
// const canvas = canvasRef.current;
// if (!canvas) return;
// const ctx = canvas.getContext('2d');
// let animId;
// let particles = [];
// const COUNT = 35;
// const CONNECT_DIST = 150;
function resize() {
canvas.width = canvas.offsetWidth * window.devicePixelRatio;
canvas.height = canvas.offsetHeight * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
// function resize() {
// canvas.width = canvas.offsetWidth * window.devicePixelRatio;
// canvas.height = canvas.offsetHeight * window.devicePixelRatio;
// ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
// }
function init() {
resize();
particles = [];
for (let i = 0; i < COUNT; i++) {
particles.push({
x: Math.random() * canvas.offsetWidth,
y: Math.random() * canvas.offsetHeight,
vx: (Math.random() - 0.5) * 0.15,
vy: (Math.random() - 0.5) * 0.15,
r: Math.random() * 1 + 0.3,
color: Math.random() > 0.8 ? '#f08519' : '#004ec4',
});
}
}
// function init() {
// resize();
// particles = [];
// for (let i = 0; i < COUNT; i++) {
// particles.push({
// x: Math.random() * canvas.offsetWidth,
// y: Math.random() * canvas.offsetHeight,
// vx: (Math.random() - 0.5) * 0.15,
// vy: (Math.random() - 0.5) * 0.15,
// r: Math.random() * 1 + 0.3,
// color: Math.random() > 0.8 ? '#f08519' : '#004ec4',
// });
// }
// }
function draw() {
const w = canvas.offsetWidth;
const h = canvas.offsetHeight;
ctx.clearRect(0, 0, w, h);
// function draw() {
// const w = canvas.offsetWidth;
// const h = canvas.offsetHeight;
// ctx.clearRect(0, 0, w, h);
for (let i = 0; i < particles.length; i++) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
if (p.x < 0 || p.x > w) p.vx *= -1;
if (p.y < 0 || p.y > h) p.vy *= -1;
// for (let i = 0; i < particles.length; i++) {
// const p = particles[i];
// p.x += p.vx;
// p.y += p.vy;
// if (p.x < 0 || p.x > w) p.vx *= -1;
// if (p.y < 0 || p.y > h) p.vy *= -1;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.globalAlpha = 0.15;
ctx.fill();
// ctx.beginPath();
// ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
// ctx.fillStyle = p.color;
// ctx.globalAlpha = 0.15;
// ctx.fill();
for (let j = i + 1; j < particles.length; j++) {
const p2 = particles[j];
const dx = p.x - p2.x;
const dy = p.y - p2.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < CONNECT_DIST) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = '#004ec4';
ctx.globalAlpha = 0.03 * (1 - dist / CONNECT_DIST);
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
}
ctx.globalAlpha = 1;
animId = requestAnimationFrame(draw);
}
// for (let j = i + 1; j < particles.length; j++) {
// const p2 = particles[j];
// const dx = p.x - p2.x;
// const dy = p.y - p2.y;
// const dist = Math.sqrt(dx * dx + dy * dy);
// if (dist < CONNECT_DIST) {
// ctx.beginPath();
// ctx.moveTo(p.x, p.y);
// ctx.lineTo(p2.x, p2.y);
// ctx.strokeStyle = '#004ec4';
// ctx.globalAlpha = 0.03 * (1 - dist / CONNECT_DIST);
// ctx.lineWidth = 0.5;
// ctx.stroke();
// }
// }
// }
// ctx.globalAlpha = 1;
// animId = requestAnimationFrame(draw);
// }
init();
draw();
window.addEventListener('resize', () => { resize(); });
return () => cancelAnimationFrame(animId);
}, []);
// init();
// draw();
// window.addEventListener('resize', () => { resize(); });
// return () => cancelAnimationFrame(animId);
// }, []);
return <canvas ref={canvasRef} className="absolute inset-0 w-full h-full z-[1]" />;
}
// return <canvas ref={canvasRef} className="absolute inset-0 w-full h-full z-[1] border-amber-500 " />;
// }
export default function HeroSection() {
return (
@@ -93,7 +94,7 @@ export default function HeroSection() {
playsInline
className="absolute inset-0 w-full h-full object-cover z-0 scale-105"
>
<source src="/src/assets/images/bag.mp4" type="video/mp4" />
<source src={bag} type="video/mp4" />
</video>
{/* Cinematic overlay */}
@@ -108,7 +109,7 @@ export default function HeroSection() {
style={{ animation: 'pulse-slow 12s ease-in-out 3s infinite' }} />
</div>
<ParticleNetwork />
{/* Top & bottom edge lines */}
<div className="absolute top-0 left-0 right-0 z-10 h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent" />
@@ -151,7 +152,7 @@ export default function HeroSection() {
}
.hero-main-title {
font-family: "ZCOOL XiaoWei", "STSong", "SimSun", serif;
font-family: "STSong", "SimSun", "NSimSun", serif;
letter-spacing: 0.6em;
color: white;
text-shadow: 0 4px 60px rgba(0, 162, 234, 0.3),
@@ -159,7 +160,7 @@ export default function HeroSection() {
}
.hero-sec-title {
font-family: "Ma Shan Zheng", "STXingkai", cursive;
font-family: "STKaiti", "KaiTi", "DFKai-SB", cursive;
letter-spacing: 0.5em;
color: white;
text-shadow: 0 2px 40px rgba(0, 162, 234, 0.25),
@@ -167,7 +168,7 @@ export default function HeroSection() {
}
.hero-desc {
font-family: "Ma Shan Zheng", "STXingkai", cursive;
font-family: "KaiTi", "STKaiti", "DFKai-SB", cursive;
letter-spacing: 0.3em;
color: rgba(255, 255, 255, 1);
text-shadow: 0 2px 40px rgba(0, 162, 234, 0.25),
@@ -198,23 +199,23 @@ export default function HeroSection() {
{/* Heading */}
<h1 className="text-center"
style={{ animation: 'fadeInUp 1.4s cubic-bezier(0.16, 1, 0.3, 1) 0.4s both' }}>
<span className="block text-5xl md:text-8xl lg:text-[110px] font-black hero-main-title">
<span className="block text-5xl md:text-8xl lg:text-[120px] font-black hero-main-title">
智慧教育
</span>
<span className="block mt-3 text-3xl md:text-[42px] lg:text-[48px] font-extralight hero-sec-title">
<span className="block mt-6 md:mt-15 text-3xl md:text-[42px] lg:text-[108px] font-extralight hero-sec-title">
赋能未来
</span>
</h1>
{/* Divider */}
<div className="flex items-center justify-center mt-2 mb-2"
<div className="flex items-center justify-center mt-6 mb-6"
style={{ animation: 'fadeIn 1s ease-out 1s both' }}>
</div>
{/* Subtitle */}
<div style={{ animation: 'fadeInUp 1s cubic-bezier(0.16, 1, 0.3, 1) 1.1s both' }}>
<p className="text-lg md:text-xl max-w-xs mx-auto leading-[2] hero-desc">
<p className="text-lg lg:text-[38px] md:text-xl mx-auto leading-[2] hero-desc whitespace-nowrap">
让校园管理更简单更智能
</p>
</div>
@@ -230,20 +231,7 @@ export default function HeroSection() {
</div>
</div>
{/* Scroll indicator */}
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 z-10"
style={{ animation: 'fadeIn 1s ease-out 1.8s both' }}>
<a href="#data" className="flex flex-col items-center gap-3 group">
<span className="text-[9px] font-extralight text-white/15 group-hover:text-white/30 transition-colors duration-700"
style={{ letterSpacing: '0.6em' }}>
SCROLL
</span>
<div className="w-px h-10 bg-white/[0.06] relative overflow-hidden rounded-full">
<div className="absolute w-full h-4 bg-gradient-to-b from-[#004ec4]/60 to-transparent rounded-full"
style={{ animation: 'scrollFlow 2.5s ease-in-out infinite' }} />
</div>
</a>
</div>
</section>
);
}
+22 -14
View File
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useMemo } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Menu, X, Phone } from 'lucide-react';
import { products } from '../data/products';
@@ -8,7 +8,15 @@ export default function Navbar() {
const [mobileOpen, setMobileOpen] = useState(false);
const location = useLocation();
// 根据当前路由获取产品主题色
const themeColor = useMemo(() => {
const match = location.pathname.match(/^\/product\/(.+)$/);
if (match) {
const product = products.find(p => p.slug === match[1]);
return product ? product.themeColor : null;
}
return null;
}, [location.pathname]);
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > window.innerHeight * 0.7);
@@ -44,9 +52,9 @@ export default function Navbar() {
left: 10%;
right: 10%;
height: 3px;
background: linear-gradient(90deg, transparent, #3b82f6, transparent);
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.8), transparent);
border-radius: 2px;
box-shadow: 0 0 12px rgba(59, 130, 246, 0.5), 0 0 24px rgba(59, 130, 246, 0.2);
box-shadow: 0 0 12px rgba(255,255,255,0.5), 0 0 24px rgba(255,255,255,0.2);
}
.nav-item {
position: relative;
@@ -67,8 +75,8 @@ export default function Navbar() {
}
`}</style>
<nav
className={`sticky top-0 z-50 transition-all duration-500 backdrop-blur-xl ${scrolled ? 'bg-[#0f172a]/95 shadow-[0_4px_30px_rgba(0,0,0,0.2)] border-b border-white/[0.06]' : 'bg-gradient-to-b from-black/40 to-transparent'}`}
style={!scrolled ? undefined : undefined}
className={`sticky top-0 z-50 transition-all duration-500 backdrop-blur-xl ${scrolled ? 'shadow-[0_4px_30px_rgba(0,0,0,0.2)] border-b border-white/[0.06]' : 'bg-gradient-to-b from-black/40 to-transparent'}`}
style={scrolled ? { backgroundColor: themeColor ? `${themeColor}f0` : '#0f172af2' } : undefined}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center h-[80px] overflow-visible">
@@ -78,7 +86,7 @@ export default function Navbar() {
<Link to="/" className="flex items-center gap-3 group">
<img src="/favicon.ico" alt="讯驰" className="w-[100px] h-auto" />
<div className="flex flex-col leading-tight">
<span className={`text-xl font-bold tracking-wide text-white transition-colors duration-500`}>讯驰科技</span>
<span className={`text-3xl font-bold tracking-wide text-white transition-colors duration-500`}>讯驰科技</span>
<span className={`text-[10px] tracking-[0.2em] uppercase font-medium text-white/80 transition-colors duration-500`}>XUNCHI TECH</span>
</div>
</Link>
@@ -89,10 +97,10 @@ export default function Navbar() {
<Link
key={item.to}
to={item.to}
className={`relative px-4 font-medium rounded-sm transition-all duration-300 nav-item ${
className={`relative px-5 font-medium rounded-sm transition-all duration-300 nav-item ${
location.pathname === item.to
? 'nav-active-tab text-white font-semibold text-base'
: 'text-white hover:text-white text-sm py-3'
? 'nav-active-tab text-white font-semibold text-xl'
: 'text-white hover:text-white text-lg py-3'
}`}
>
{item.label}
@@ -107,7 +115,7 @@ export default function Navbar() {
<div className={`hidden lg:flex items-center shrink-0 transition-colors duration-500`}>
<a
href="tel:07713864294"
className="flex items-center gap-1.5 text-sm font-medium text-white hover:text-white transition-all duration-300"
className="flex items-center gap-1.5 text-lg font-medium text-white hover:text-white transition-all duration-300"
>
<Phone className="w-3.5 h-3.5" />
0771-3864294
@@ -131,12 +139,12 @@ export default function Navbar() {
mobileOpen ? 'max-h-[800px]' : 'max-h-0'
}`}
>
<div className="bg-[#0a1628]/95 backdrop-blur-2xl border-t border-white/[0.06] px-4 pb-6 pt-3 space-y-1">
<div className="bg-[#0a1628]/95 backdrop-blur-2xl border-t border-white/[0.06] px-4 pb-6 pt-3 space-y-1" style={scrolled && themeColor ? { backgroundColor: `${themeColor}f0` } : undefined}>
{navItems.map((item) => (
<Link
key={item.to}
to={item.to}
className={`block px-4 py-3 rounded-xl text-sm font-medium transition-colors ${
className={`block px-4 py-3 rounded-xl text-lg font-medium transition-colors ${
location.pathname === item.to
? 'text-white bg-white/[0.06]'
: 'text-white/50 hover:bg-white/[0.04]'
@@ -146,7 +154,7 @@ export default function Navbar() {
</Link>
))}
<div className="pt-3 mt-2 border-t border-white/[0.06]">
<a href="tel:07713864294" className="flex items-center gap-2 px-5 py-3.5 text-[#043b8f] text-base font-medium">
<a href="tel:07713864294" className="flex items-center gap-2 px-5 py-3.5 text-[#f3f3f4] text-base font-medium">
<Phone className="w-4 h-4" />
0771-3864294
</a>
+18 -9
View File
@@ -5,11 +5,9 @@ const chartData = [
{ value: 25, name: '校企益邦' },
{ value: 25, name: '科研管理系统' },
{ value: 25, name: '智慧党建平台' },
{ value: 25, name: '商咖五级进阶' },
{ value: 25, name: '实训管理平台' },
];
const color = '#043b8f';
const rotationNeeded = [45, -45, -135, -225];
function normalizeAngle(a) {
@@ -25,12 +23,23 @@ function shortestRotation(from, to) {
return from + diff;
}
export default function RosePieChart({ onSelect, activeIndex }) {
/** 将 hex 颜色转为带透明度的 rgba */
function hexToRgba(hex, alpha) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r},${g},${b},${alpha})`;
}
export default function RosePieChart({ onSelect, activeIndex, colors }) {
const containerRef = useRef(null);
const chartRef = useRef(null);
const rotationRef = useRef(0);
const [rotation, setRotation] = useState(0);
// 默认回退颜色
const safeColors = colors?.length === 4 ? colors : ['#60ce93', '#0c86cf', '#aa3130', '#8b50ee'];
useEffect(() => {
if (!containerRef.current) return;
@@ -47,7 +56,7 @@ export default function RosePieChart({ onSelect, activeIndex }) {
},
series: [{
type: 'pie',
radius: ['20%', '68%'],
radius: ['25%', '75%'],
center: ['50%', '50%'],
startAngle: 90,
padAngle: 3,
@@ -73,7 +82,7 @@ export default function RosePieChart({ onSelect, activeIndex }) {
},
data: chartData.map((d, i) => ({
...d,
itemStyle: { color, opacity: 1, borderRadius: 6, borderWidth: 0 },
itemStyle: { color: safeColors[i], opacity: 1, borderRadius: 6, borderWidth: 0 },
})),
animationType: 'scale',
animationEasing: 'elasticOut',
@@ -106,9 +115,9 @@ export default function RosePieChart({ onSelect, activeIndex }) {
series: [{
data: chartData.map((d, i) => ({
...d,
label: { color: isActive && activeIndex === i ? '#fff' : '#043b8f' },
label: { color: isActive && activeIndex === i ? '#fff' : safeColors[i] },
itemStyle: {
color: isActive && activeIndex !== i ? color + '30' : color,
color: isActive && activeIndex !== i ? hexToRgba(safeColors[i], 0.2) : safeColors[i],
opacity: 1,
borderRadius: 6,
borderColor: 'rgba(15,23,42,0.8)',
@@ -140,7 +149,7 @@ export default function RosePieChart({ onSelect, activeIndex }) {
transition: 'transform 1s cubic-bezier(0.4, 0, 0.2, 1)',
}}
>
<div ref={containerRef} className="w-full h-[420px] md:h-[480px]" />
<div ref={containerRef} className="w-full h-[600px] md:h-[720px]" />
</div>
);
}
+40 -15
View File
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom'
export default function ScrollReveal({
children,
className = '',
@@ -14,26 +14,51 @@ export default function ScrollReveal({
}) {
const ref = useRef(null);
const [visible, setVisible] = useState(false);
const { pathname } = useLocation()
useEffect(() => {
const timer = setTimeout(() => {
setVisible(false);
}, 0);
return () => {
clearTimeout(timer);
};
}, [pathname]);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setVisible(true);
if (once) observer.unobserve(el);
} else if (!once) {
setVisible(false);
}
},
{ threshold, rootMargin: '0px 0px -60px 0px' }
);
let observer;
let cancelled = false;
observer.observe(el);
return () => observer.disconnect();
}, [once, threshold]);
const timer = setTimeout(() => {
if (cancelled) return;
observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setVisible(true);
if (once) observer.unobserve(el);
} else if (!once) {
setVisible(false);
}
},
{ threshold, rootMargin: '0px 0px -60px 0px' }
);
observer.observe(el);
}, 0);
return () => {
cancelled = true;
clearTimeout(timer);
if (observer) observer.disconnect();
};
}, [once, threshold, pathname]);
const transforms = {
up: `translateY(${distance}px)`,
+43
View File
@@ -0,0 +1,43 @@
import { useState, useEffect, useRef } from 'react'
import { useLocation } from 'react-router-dom'
/* ─── Animated counter ─── */
export default function StatNumber({ target, suffix }) {
const [count, setCount] = useState(0)
const ref = useRef(null)
const started = useRef(false)
const { pathname } = useLocation()
useEffect(() => {
started.current = false
setTimeout(() => setCount(0), 0)
}, [pathname])
useEffect(() => {
const el = ref.current
if (!el) return
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting && !started.current) {
started.current = true
const dur = 2000
const start = performance.now()
const num = parseInt(target)
function tick(now) {
const p = Math.min((now - start) / dur, 1)
setCount(Math.round((1 - Math.pow(1 - p, 3)) * num))
if (p < 1) requestAnimationFrame(tick)
}
requestAnimationFrame(tick)
obs.unobserve(el)
}
}, { threshold: 0.3 })
obs.observe(el)
return () => obs.disconnect()
}, [target, pathname])
return <span ref={ref}>{count}{suffix}</span>
}
+40
View File
@@ -0,0 +1,40 @@
import { useState, useEffect, useRef } from 'react';
/**
* 加载 SVG 文件并将指定颜色替换为主题色后内联渲染
* @param {string} url - SVG 文件地址
* @param {string} color - 替换的目标主题色 (hex)
* @param {string} className - 外层容器样式
*/
export default function ThemedSvg({ url, color, className }) {
const [content, setContent] = useState('');
const ref = useRef(null);
useEffect(() => {
let cancelled = false;
fetch(url)
.then(res => res.text())
.then(text => {
if (cancelled) return;
// 将 undraw 默认紫色 #6c63ff 替换为页面主题色
const themed = text.replace(/#6c63ff/gi, color);
setContent(themed);
})
.catch(() => {
if (!cancelled) setContent('');
});
return () => { cancelled = true; };
}, [url, color]);
if (!content) {
return <div className={className} />;
}
return (
<div
ref={ref}
className={className}
dangerouslySetInnerHTML={{ __html: content }}
/>
);
}
+115 -78
View File
@@ -1,5 +1,5 @@
import {
Building2, GraduationCap, BookOpen, ClipboardCheck
Building2, GraduationCap, BookOpen, ClipboardCheck,
} from 'lucide-react';
export const products = [
@@ -7,31 +7,43 @@ export const products = [
slug: 'smart-campus',
name: '校企益邦',
slogan: '实习过程全掌控,就业数据一目了然',
themeColor: '#2563EB',
themeColor: '#60ce93',
heroImage: new URL('../assets/images/Carousel/xq.png', import.meta.url).href,
themeFrom: '#1e40af',
themeTo: '#3b82f6',
screenshots: [
{ url: new URL('../assets/images/xq/xq1.png', import.meta.url).href, name: '专业管理' },
{ url: new URL('../assets/images/xq/xq2.png', import.meta.url).href, name: '数据大屏' },
{ url: new URL('../assets/images/xq/xq3.png', import.meta.url).href, name: '辅导员工作台' },
],
themeFrom: '#047857',
themeTo: '#34d399',
icon: Building2,
brief: '面向职业院校实习就业管理的专业化平台,覆盖实习计划制定、过程跟踪、数据采集、鉴定评价全流程,已服务广西多所职业院校,帮助学校轻松应对教育厅数据上报要求。',
tags: ['实习计划管理', 'GPS签到+电子围栏', '问卷式日志', '多维鉴定评价'],
stats: [
{ num: '20+所', label: '覆盖学校' },
{ num: '50,000+', label: '服务学生' },
{ num: '100万+', label: '累计签到' },
{ num: '20', suffix: '+所', label: '覆盖学校' },
{ num: '50,000', suffix: '+', label: '服务学生' },
{ num: '100', suffix: '万+', label: '累计签到' },
],
features: [
{ emoji: '📋', title: '实习计划管理', desc: '按学期、专业、班级灵活制定实习计划,绑定不同实习规则,实现多批次差异化管理' },
{ emoji: '📍', title: 'GPS签到+电子围栏', desc: '基于地理位置的真实考勤,自动判断签到有效性,未签到学生一键导出名单' },
{ emoji: '📝', title: '问卷式日志', desc: '预设定向提问模板,引导学生深度反思,"流水账"变"成长档案"' },
{ emoji: '📊', title: '多维鉴定评价', desc: '综合考勤、日志、教师评价、企业反馈,自动生成实习鉴定分数,客观公正' },
{ emoji: '🔄', title: '一键同步教育厅平台', desc: '数据审核后直接同步至"广西职业院校学生实习管理平台",无缝对接省级管理要求' },
{ emoji: '📱', title: '微信小程序加持', desc: '学生通过小程序签到、写日志、上传附件,无需下载额外App,使用门槛极低' },
{ icon: 'CalendarCheck', title: '实习计划管理', desc: '按学期、专业、班级灵活制定实习计划,绑定不同实习规则,实现多批次差异化管理', image: new URL('../assets/Features/xq/xq-1.jpg', import.meta.url).href },
{ icon: 'SearchCheck', title: 'GPS签到+电子围栏', desc: '基于地理位置的真实考勤,自动判断签到有效性,未签到学生一键导出名单', image: new URL('../assets/Features/xq/xq-2.png', import.meta.url).href },
{ icon: 'ScrollText', title: '问卷式日志', desc: '预设定向提问模板,引导学生深度反思,"流水账"变"成长档案"', image: new URL('../assets/Features/xq/xq-3.png', import.meta.url).href, small: true },
{ icon: 'FileDigit', title: '多维鉴定评价', desc: '综合考勤、日志、教师评价、企业反馈,自动生成实习鉴定分数,客观公正', image: new URL('../assets/Features/xq/xq-4.png', import.meta.url).href },
{ icon: 'GitBranch', title: '一键同步教育厅平台', desc: '数据审核后直接同步至"广西职业院校学生实习管理平台",无缝对接省级管理要求', image: new URL('../assets/Features/xq/xq-5.png', import.meta.url).href },
{ icon: 'Layers', title: '微信小程序加持', desc: '学生通过小程序签到、写日志、上传附件,无需下载额外App,使用门槛极低',
image: new URL('../assets/Features/xq/xq-6.png', import.meta.url).href, small: true },
],
painPointsTitle: '校企益邦解决的核心问题',
painPointsTitle: '校企益邦\n解决的核心问题',
painPoints: [
{ title: '没有统一的方案执行,学校难以管控学生的实习情况', desc: '平台提供丰富的实习规则定制,涵盖签到、汇报、评价、考核等要求。定制后的实习规则可作为实习要求模板应用于多个实习计划,管理不同批次的学生,省去重复配置众多实习要求的麻烦,体现模板化。' },
{ title: '学生信息采集工作量大,授课教师苦不堪言', desc: '将学生实习信息采集工作分配给学生完成,指导老师只需对这些信息进行审核,减少老师在实习信息采集和整理环节的工作,保障信息准确性、规范性的同时,提高了信息收集效率。' },
{ title: '学生配合的积极性较低,提交的数据不达标', desc: '实习日志采用"问卷式",有指向性地采集学生实习日志,让学生即使面对需要频繁提交的日志,也有汇报方向,减少"流水账式"汇报,提升汇报效果。' },
{
url: new URL('../assets/images/Carousel/zh-points-1.svg', import.meta.url).href,
title: '没有统一的方案执行,学校难以管控学生的实习情况', desc: '平台提供丰富的实习规则定制,涵盖签到、汇报、评价、考核等要求。定制后的实习规则可作为实习要求模板应用于多个实习计划,管理不同批次的学生,省去重复配置众多实习要求的麻烦,体现模板化。', },
{
url: new URL('../assets/images/Carousel/Points-2.svg', import.meta.url).href,
title: '学生信息采集工作量大,授课教师苦不堪言', desc: '将学生实习信息采集工作分配给学生完成,指导老师只需对这些信息进行审核,减少老师在实习信息采集和整理环节的工作,保障信息准确性、规范性的同时,提高了信息收集效率。', },
{
url: new URL('../assets/images/Carousel/Points-3.svg', import.meta.url).href,
title: '学生配合的积极性较低,提交的数据不达标', desc: '实习日志采用"问卷式",有指向性地采集学生实习日志,让学生即使面对需要频繁提交的日志,也有汇报方向,减少"流水账式"汇报,提升汇报效果。', },
],
target: '职业院校教务处、实习管理科、各院系指导教师',
faq: [
@@ -45,33 +57,38 @@ export const products = [
slug: 'exam-system',
name: '科研管理系统',
slogan: '让科研管理告别纸质时代',
themeColor: '#EA580C',
themeColor: '#0c86cf',
heroImage: new URL('../assets/images/Carousel/ky.png', import.meta.url).href,
themeFrom: '#c2410c',
themeTo: '#f97316',
screenshots: [
{ url: new URL('../assets/images/ky/ky1.png', import.meta.url).href, name: '个人科研画像' },
{ url: new URL('../assets/images/ky/ky2.jpeg', import.meta.url).href, name: '经办跟踪' },
{ url: new URL('../assets/images/ky/ky3.jpeg', import.meta.url).href, name: '消息推送' },
],
themeFrom: '#075985',
themeTo: '#38bdf8',
icon: ClipboardCheck,
brief: '面向高校及科研院所的全生命周期科研业务管理平台。覆盖项目申报、评审立项、经费管控、成果登记到数据统计的全流程数字化,已帮助多所高校实现科研管理从纸质流转到"一网通办"的跨越。',
tags: ['在线申报审批', '智能查重防重复', '经费精细管控', '成果自动赋分'],
stats: [
{ num: '10+所', label: '服务高校' },
{ num: '3,000+', label: '管理项目' },
{ num: '8,000+', label: '登记成果' },
{ num: '10', suffix: '+所', label: '服务高校' },
{ num: '3000', suffix: '+', label: '管理项目' },
{ num: '8000', suffix: '+', label: '登记成果' },
],
features: [
{ emoji: '📝', title: '在线申报审批', desc: '项目申报、审核、立项全流程在线办理,智能校验引擎自动检查相似度和限项规则' },
{ emoji: '🔍', title: '智能查重防重复', desc: '项目名称相似度超80%自动拦截,同一主持人月度限申报1次,杜绝重复申报' },
{ emoji: '💰', title: '经费精细管控', desc: '纵向/横向分类管理,预算—报销实时关联,超预算自动预警,经费使用透明合规' },
{ emoji: '🏆', title: '成果自动赋分', desc: '论文、专利、著作按级别自动量化评分,一次登记多处复用,告别重复填报' },
{ emoji: '', title: '关键节点预警', desc: '经费超期、结题到期、审批退回等场景自动推送提醒,确保每个环节不遗漏' },
{ emoji: '📄', title: '年度报表一键导出', desc: '科研成果统计、项目经费汇总等年度报表自动生成,效率提升数十倍' },
{ icon: 'FileStack', title: '在线申报审批', desc: '项目申报、审核、立项全流程在线办理,智能校验引擎自动检查相似度和限项规则', image: new URL('../assets/Features/ky/ky-1.png', import.meta.url).href },
{ icon: 'SearchCheck', title: '智能查重防重复', desc: '项目名称相似度超80%自动拦截,同一主持人月度限申报1次,杜绝重复申报', image: new URL('../assets/Features/ky/ky-2.png', import.meta.url).href },
{ icon: 'Banknote', title: '经费精细管控', desc: '纵向/横向分类管理,预算—报销实时关联,超预算自动预警,经费使用透明合规', image: new URL('../assets/Features/ky/ky-3.png', import.meta.url).href },
{ icon: 'TrendingUp', title: '成果自动赋分', desc: '论文、专利、著作按级别自动量化评分,一次登记多处复用,告别重复填报', image: new URL('../assets/Features/ky/ky-4.png', import.meta.url).href },
{ icon: 'Bell', title: '关键节点预警', desc: '经费超期、结题到期、审批退回等场景自动推送提醒,确保每个环节不遗漏', image: new URL('../assets/Features/ky/ky-5.png', import.meta.url).href },
{ icon: 'FileDigit', title: '年度报表一键导出', desc: '科研成果统计、项目经费汇总等年度报表自动生成,效率提升数十倍', image: new URL('../assets/Features/ky/ky-6.png', import.meta.url).href },
],
painPointsTitle: '科研管理系统解决的核心问题',
painPointsTitle: '科研管理系统\n解决的核心问题',
painPoints: [
{ title: '传统科研管理流程繁琐,纸质办公效率低下', desc: '全流程数字化,实现科研项目申报、审批、结题的全程在线办理,消除纸质办公,提升工作效率。' },
{ title: '项目申报审核工作量大,人工审核成本高', desc: '智能校验引擎,内置项目查重(相似度检测)、限项检查、预算自动配套计算规则,大幅降低人工审核成本。' },
{ title: '科研经费管理复杂,预算与报销脱节', desc: '经费精细化管控,支持纵向/横向经费分类管理,实时关联预算与报销进度,确保经费使用规范透明。' },
{ title: '科研成果统计困难,年度报表制作耗时', desc: '成果分值量化,自动根据奖励办法对论文、专利、著作等成果进行赋分,支持一键导出年度统计报表。' },
{ title: '项目进度监控不及时,关键节点容易遗漏', desc: '多维度预警,集成短信通知与站内提醒,涵盖预算超期、结题预警、流程回退等关键节点。' },
{ url: new URL('../assets/images/Carousel/Points-1.svg', import.meta.url).href, title: '传统科研管理流程繁琐,纸质办公效率低下', desc: '全流程数字化,实现科研项目申报、审批、结题的全程在线办理,消除纸质办公,提升工作效率。'},
{ url: new URL('../assets/images/Carousel/ky-Points-1.svg', import.meta.url).href, title: '项目申报审核工作量大,人工审核成本高', desc: '智能校验引擎,内置项目查重(相似度检测)、限项检查、预算自动配套计算规则,大幅降低人工审核成本。' },
{ url: new URL('../assets/images/Carousel/ky-Points-3.svg', import.meta.url).href, title: '科研经费管理复杂,预算与报销脱节', desc: '经费精细化管控,支持纵向/横向经费分类管理,实时关联预算与报销进度,确保经费使用规范透明。' },
{ url: new URL('../assets/images/Carousel/Points-4.svg', import.meta.url).href, title: '科研成果统计困难,年度报表制作耗时', desc: '成果分值量化,自动根据奖励办法对论文、专利、著作等成果进行赋分,支持一键导出年度统计报表。' },
{ url: new URL('../assets/images/Carousel/Points-5.svg', import.meta.url).href, title: '项目进度监控不及时,关键节点容易遗漏', desc: '多维度预警,集成短信通知与站内提醒,涵盖预算超期、结题预警、流程回退等关键节点。' },
],
target: '高校科研处、财务处、各院系教师及科研项目负责人',
faq: [
@@ -85,40 +102,53 @@ export const products = [
slug: 'teaching-resource',
name: '智慧党建平台',
slogan: '党建工作有痕,年终考核无忧',
themeColor: '#7C3AED',
themeColor: '#aa3130',
heroImage: new URL('../assets/images/Carousel/zh.png', import.meta.url).href,
themeFrom: '#6d28d9',
themeTo: '#8b5cf6',
heroSize: 'small',
screenshots: [
{ url: new URL('../assets/images/zh/zh1.png', import.meta.url).href, name: '消息推送' },
{ url: new URL('../assets/images/zh/zh2.png', import.meta.url).href, name: '数据大屏' },
{ url: new URL('../assets/images/zh/zh3.jpeg', import.meta.url).href, name: '发展党员管理' },
],
themeFrom: '#7f1d1d',
themeTo: '#dc2626',
icon: BookOpen,
brief: '面向高校及职业院校各级党组织的智慧党建管理系统。以"党业融合、指标驱动、全程留痕"为核心理念,将三会一课、党员活动、工作记录等日常工作自动关联考核指标,实现年终考核"零重复填报"。',
tags: ['三级考核指标', '三会一课数字化', '万能工作记录', '实时进度看板'],
stats: [
{ num: '150+', label: '服务支部' },
{ num: '5,000+', label: '管理党员' },
{ num: '200+', label: '考核指标' },
{ num: '150', suffix: '+所', label: '服务支部' },
{ num: '5000', suffix: '+', label: '管理党员' },
{ num: '200', suffix: '+', label: '考核指标' },
],
features: [
{ emoji: '📊', title: '三级考核指标', desc: '灵活构建校—院—支部三级指标树,分值自动汇总,考核部门与执行部门逐一对应' },
{ emoji: '📋', title: '三会一课数字化', desc: '通知—签到—记录全流程闭环,二维码签到5分钟刷新,杜绝"代签""补签"' },
{ emoji: '✍️', title: '万能工作记录', desc: '不受模块限制,任何业务工作均可灵活录入并关联考核指标,实现考核全覆盖' },
{ emoji: '🔔', title: '实时进度看板', desc: '领导层穿透查看各项指标完成进度,哪项落后一目了然,变"年底算账"为"过程管控"' },
{ emoji: '👥', title: '发展党员管理', desc: '内置入党全流程节点模型,支持自定义配置,关键节点到期自动提醒支部管理员' },
{ emoji: '📡', title: '党建数据中心', desc: '全校党建数据可视化大屏——组织健康度、党员活跃度、重点工作督办一览无余' },
{ icon: 'Pyramid', title: '三级考核指标', desc: '灵活构建校—院—支部三级指标树,分值自动汇总,考核部门与执行部门逐一对应', image: new URL('../assets/Features/zh/zh-1.png', import.meta.url).href },
{ icon: 'CalendarCheck', title: '三会一课数字化', desc: '通知—签到—记录全流程闭环,二维码签到5分钟刷新,杜绝"代签""补签"', image: new URL('../assets/Features/zh/zh-2.png', import.meta.url).href },
{ icon: 'ScrollText', title: '万能工作记录', desc: '不受模块限制,任何业务工作均可灵活录入并关联考核指标,实现考核全覆盖', image: new URL('../assets/Features/zh/zh-3.png', import.meta.url).href },
{ icon: 'ChartBar', title: '实时进度看板', desc: '领导层穿透查看各项指标完成进度,哪项落后一目了然,变"年底算账"为"过程管控"', image: new URL('../assets/Features/zh/zh-4.png', import.meta.url).href },
{ icon: 'Database', title: '发展党员管理', desc: '内置入党全流程节点模型,支持自定义配置,关键节点到期自动提醒支部管理员', image: new URL('../assets/Features/zh/zh-5.png', import.meta.url).href },
{ icon: 'Activity', title: '党建数据中心', desc: '全校党建数据可视化大屏——组织健康度、党员活跃度、重点工作督办一览无余', image: new URL('../assets/Features/zh/zh-6.png', import.meta.url).href },
],
painPointsTitle: '智慧党建平台解决的核心问题',
painPointsTitle: '智慧党建平台\n解决的核心问题',
painPoints: [
{ title: '党建工作与教学业务“两张皮”,考核指标繁杂多样,难以实现全口径统一管理', desc: '构建“专用模块+通用记录”的双重支撑体系。对于标准化党务,通过“三会一课”、“党员活动”等模块自动关联;对于教学科研等系统中无特定管理模块的考核任务,利用“工作记录”模块作为万能适配器进行灵活录入并关联指标。确保每一项考核指标(无论党务或业务)都有据可查,实现党建考核的100%全覆盖。' },
{
title: ' 年终考核“突击补材料”,重复填表耗时耗力,数据难追溯',
desc: '推行“过程资产化”管理,日常工作(如会议、活动)录入即并关联考核指标,年终无需盘点,数据自动生成,拒绝重复劳动。'
url: new URL('../assets/images/Carousel/zh-Points-1.svg', import.meta.url).href,
title: '党建工作与教学业务”两张皮”,考核指标繁杂多样,难以实现全口径统一管理',
desc: '构建”专用模块+通用记录”的双重支撑体系。对于标准化党务,通过”三会一课”、”党员活动”等模块自动关联;对于教学科研等系统中无特定管理模块的考核任务,利用”工作记录”模块作为万能适配器进行灵活录入并关联指标。确保每一项考核指标(无论党务或业务)都有据可查,实现党建考核的100%全覆盖。'},
{
url: new URL('../assets/images/Carousel/zh-Points-2.svg', import.meta.url).href,
title: ' 年终考核”突击补材料”,重复填表耗时耗力,数据难追溯',
desc: '推行”过程资产化”管理,日常工作(如会议、活动)录入即并关联考核指标,年终无需盘点,数据自动生成,拒绝重复劳动。',
},
{ url: new URL('../assets/images/Carousel/Points-3.svg', import.meta.url).href, title: '”三会一课”组织难、签到难、记录难,合规性监管滞后',
desc: '提供移动端全流程闭环,支持一键发布通知、现场扫码签到、会议纪要即时编辑上传,确保组织生活规范、留痕、可查。',
},
{
title: '“三会一课”组织难、签到难、记录难,合规性监管滞后',
desc: '提供移动端全流程闭环,支持一键发布通知、现场扫码签到、会议纪要即时编辑上传,确保组织生活规范、留痕、可查。'
},
{
title: '党建考核存在“层级衰减”与“管理盲区”,平时进度看不见,年底检查来不及',
desc: '构建分层级可视化考核体系。系统打破科层壁垒,领导层通过“完成情况看板”直观监测各指标的实时进度一键穿透查看明细;执行层通过“指标明细”精准对应“自评”与“佐证材料”,明确“谁来做、谁来考、通过什么材料佐证”。让考核从“年底算总账”变为“全过程透明化管控”,确保压力无损传导至基层末梢。'
url: new URL('../assets/images/Carousel/Points-4.svg', import.meta.url).href,
title: '党建考核存在”层级衰减”与”管理盲区”,平时进度看不见,年底检查来不及',
desc: '构建分层级可视化考核体系。系统打破科层壁垒,领导层通过”完成情况看板”直观监测各指标的实时进度一键穿透查看明细;执行层通过”指标明细”精准对应”自评”与”佐证材料”,明确”谁来做、谁来考、通过什么材料佐证”。让考核从”年底算总账”变为”全过程透明化管控”,确保压力无损传导至基层末梢。',
},
],
target: '高校党委组织部、各二级党委/党总支、基层党支部',
@@ -131,38 +161,45 @@ export const products = [
},
{
slug: 'internship',
name: '商咖五级进阶',
name: '实训管理平台',
slogan: '五级渐进,让每一次成长都看得见',
themeColor: '#059669',
themeColor: '#8b50ee',
heroImage: new URL('../assets/images/Carousel/sk.png', import.meta.url).href,
themeFrom: '#047857',
themeTo: '#10b981',
screenshots: [
{ url: new URL('../assets/images/sk/sk1.png', import.meta.url).href, name: '数据大屏' },
{ url: new URL('../assets/images/sk/sk2.jpeg', import.meta.url).href, name: '角色管理' },
{ url: new URL('../assets/images/sk/sk3.jpeg', import.meta.url).href, name: '消息推送' },
],
themeFrom: '#5b21b6',
themeTo: '#a78bfa',
icon: GraduationCap,
brief: '专为职业院校商科专业群打造的实训项目管理系统。以"项目—模块—任务"三级结构为骨架,以"咖豆积分+技能画像"为引擎,将抽象的五级进阶培养模式变成可量化、可追踪、可激励的线上实战平台。',
tags: ['三级精细管理', '咖豆积分引擎', '五级自动晋级', '动态技能画像'],
stats: [
{ num: '500+', label: '实训项目库' },
{ num: '15+', label: '覆盖专业' },
{ num: '200万+', label: '累计咖豆' },
{ num: '500', suffix: '+', label: '实训项目库' },
{ num: '15', suffix: '+', label: '覆盖专业' },
{ num: '200', suffix: '万+', label: '累计咖豆' },
],
features: [
{ emoji: '📦', title: '三级精细管理', desc: '项目→模块→任务三级结构,权重灵活分配,实训教学内容标准化、可复用' },
{ emoji: '🫘', title: '咖豆积分引擎', desc: '成绩自动转换为虚拟积分,支持积分排行、兑换、晋级,让学习像游戏一样上瘾' },
{ emoji: '', title: '五级自动晋级', desc: '自定义等级名称、门槛积分和专属权益,达标自动晋升,成长路径一目了然' },
{ emoji: '🎯', title: '动态技能画像', desc: '基于任务完成情况自动生成个人雷达图,技能点逐一点亮,优势短板清晰可见' },
{ emoji: '📈', title: '人才培养驾驶舱', desc: '全校实训数据一屏呈现——项目热度、参与度、完成率、经费使用,决策有据可依' },
{ emoji: '🎁', title: '积分商城', desc: '咖豆可兑换实物或虚拟礼品,构建"学、练、评、换"完整激励闭环' },
{ icon: 'Layers', title: '三级精细管理', desc: '项目→模块→任务三级结构,权重灵活分配,实训教学内容标准化、可复用', image: new URL('../assets/Features/sk/sk-1.png', import.meta.url).href },
{ icon: 'Database', title: '咖豆积分引擎', desc: '成绩自动转换为虚拟积分,支持积分排行、兑换、晋级,让学习像游戏一样上瘾', image: new URL('../assets/Features/sk/sk-2.png', import.meta.url).href },
{ icon: 'TrendingUp', title: '五级自动晋级', desc: '自定义等级名称、门槛积分和专属权益,达标自动晋升,成长路径一目了然', small: true, image: new URL('../assets/Features/sk/sk-3.png', import.meta.url).href },
{ icon: 'Activity', title: '动态技能画像', desc: '基于任务完成情况自动生成个人雷达图,技能点逐一点亮,优势短板清晰可见', small: true, image: new URL('../assets/Features/sk/sk-4.png', import.meta.url).href },
{ icon: 'ChartBar', title: '人才培养驾驶舱', desc: '全校实训数据一屏呈现——项目热度、参与度、完成率、经费使用,决策有据可依', image: new URL('../assets/Features/sk/sk-5.png', import.meta.url).href },
{ icon: 'Banknote', title: '积分商城', desc: '咖豆可兑换实物或虚拟礼品,构建"学、练、评、换"完整激励闭环', small: true, image: new URL('../assets/Features/sk/sk-6.png', import.meta.url).href },
],
painPoints: [
{ title: '人才培养模式落地难,教学管理与“五级进阶”理念脱节。',
desc: '系统内置“项目-模块-任务”三级结构与积分晋级规则,将抽象的“五级进阶”理念转化为可量化、可执行、可追踪的线上流程。学生通过完成任务获得积分,自动点亮技能点,实现逐级晋升,确保培养模式的精准落地。' },
{
title: ' 实训项目数据散落,缺乏统一管理与深度分析',
desc: '建立统一的校级实训项目库,实现项目创建、审批、排课、执行到归档的全生命周期管理。同时,建设可视化数据大屏,为管理层提供各学院项目数量、学生参与度、完成情况等全景决策数据。' },
{
title: '技能成长过程不可视,学生缺乏明确的学习目标和动力。',
desc: '系统根据学生参与的实训任务、积分获取情况,自动生成动态的个人技能画像。学生可以直观看到自己已掌握的“技能点”以及距离下一等级的差距,明确优势与不足,并获得持续进阶的动力。' },
url: new URL('../assets/images/Carousel/sx-Points-2.svg', import.meta.url).href, title: '人才培养模式落地难,教学管理与”五级进阶”理念脱节。',
desc: '系统内置”项目-模块-任务”三级结构与积分晋级规则,将抽象的”五级进阶”理念转化为可量化、可执行、可追踪的线上流程。学生通过完成任务获得积分,自动点亮技能点,实现逐级晋升,确保培养模式的精准落地。', },
{ url: new URL('../assets/images/Carousel/Points-2.svg', import.meta.url).href,
title: ' 实训项目数据散落,缺乏统一管理与深度分析',
desc: '建立统一的校级实训项目库,实现项目创建、审批、排课、执行到归档的全生命周期管理。同时,建设可视化数据大屏,为管理层提供各学院项目数量、学生参与度、完成情况等全景决策数据。', },
{ url: new URL('../assets/images/Carousel/Points-3.svg', import.meta.url).href,
title: '技能成长过程不可视,学生缺乏明确的学习目标和动力。',
desc: '系统根据学生参与的实训任务、积分获取情况,自动生成动态的个人技能画像。学生可以直观看到自己已掌握的”技能点”以及距离下一等级的差距,明确优势与不足,并获得持续进阶的动力。', },
],
target: '商科专业群、实训中心、教务处',
faq: [
+108 -2
View File
@@ -55,7 +55,7 @@ body {
/* Product page hero art titles */
.hero-product-title {
font-family: "ZCOOL XiaoWei", "STSong", "SimSun", serif;
font-family: "STSong", "SimSun", "NSimSun", serif;
letter-spacing: 0.3em;
color: white;
text-shadow: 0 4px 60px rgba(0, 162, 234, 0.3),
@@ -63,9 +63,115 @@ body {
}
.hero-product-slogan {
font-family: "Ma Shan Zheng", "STXingkai", cursive;
font-family: "STKaiti", "KaiTi", "DFKai-SB", cursive;
letter-spacing: 0.15em;
color: white;
text-shadow: 0 2px 40px rgba(0, 162, 234, 0.25),
0 0 80px rgba(0, 162, 234, 0.15);
}
/* ── 台式显示器外框 ── */
.monitor-frame {
position: relative;
background: #1a1a2e;
border-radius: 8px;
padding: 6px 6px 3px 6px;
box-shadow:
0 0 0 1px #2a2a3e,
0 2px 12px rgba(0, 0, 0, 0.3);
max-width: 750px;
margin: 0 auto;
}
.monitor-screen {
border-radius: 3px;
overflow: hidden;
background: #000;
height: 180px;
}
.monitor-chin {
display: flex;
align-items: center;
justify-content: center;
padding: 5px 0 2px 0;
}
.monitor-chin-label {
font-size: 9px;
font-weight: 500;
color: #666;
letter-spacing: 0.06em;
white-space: nowrap;
}
.monitor-stand-neck {
width: 24px;
height: 18px;
background: linear-gradient(180deg, #2a2a3e 0%, #222236 100%);
margin: 0 auto;
border-radius: 0 0 4px 4px;
}
.monitor-stand-base {
width: 100px;
height: 5px;
margin: 0 auto;
border-radius: 3px;
background: var(--monitor-theme, #60ce93);
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}
/* sm */
@media (min-width: 640px) {
.monitor-frame {
max-width: 700px;
border-radius: 10px;
padding: 8px 8px 4px 8px;
box-shadow:
0 0 0 1.5px #2a2a3e,
0 3px 16px rgba(0, 0, 0, 0.3);
}
.monitor-screen {
border-radius: 3px;
height: 260px;
}
.monitor-chin { padding: 6px 0 3px 0; }
.monitor-chin-label { font-size: 10px; }
.monitor-stand-neck { width: 30px; height: 24px; border-radius: 0 0 5px 5px; }
.monitor-stand-base { width: 130px; height: 7px; border-radius: 4px; }
}
/* md */
@media (min-width: 768px) {
.monitor-frame {
max-width: 780px;
border-radius: 12px;
padding: 10px 10px 6px 10px;
box-shadow:
0 0 0 2px #2a2a3e,
0 4px 24px rgba(0, 0, 0, 0.3);
}
.monitor-screen {
border-radius: 4px;
height: 360px;
}
.monitor-chin { padding: 8px 0 4px 0; }
.monitor-chin-label { font-size: 11px; }
.monitor-stand-neck { width: 36px; height: 30px; border-radius: 0 0 6px 6px; }
.monitor-stand-base { width: 150px; height: 8px; border-radius: 4px; }
}
/* lg */
@media (min-width: 1024px) {
.monitor-frame { max-width: 820px; }
.monitor-screen { height: 440px; }
}
/* xl */
@media (min-width: 1280px) {
.monitor-frame { max-width: 860px; }
.monitor-screen { height: 500px; }
.monitor-stand-neck { width: 40px; height: 36px; }
.monitor-stand-base { width: 180px; height: 10px; border-radius: 5px; }
}
+47 -49
View File
@@ -25,14 +25,14 @@ export default function Contact() {
<section className="relative py-32 md:py-40 text-white overflow-hidden">
{/* Background image */}
<div className="absolute inset-0">
<img src={new URL('../assets/images/Carousel/huaban.jpg', import.meta.url).href} alt="" className="w-full h-full object-cover" />
<img src={new URL('../../public/huaban.jpg', import.meta.url).href} alt="" className="w-full h-full object-cover" />
</div>
{/* Black overlay */}
<div className="absolute inset-0 bg-black/50" />
<div className="relative max-w-4xl mx-auto px-6 text-center pt-32">
<h1 className="text-4xl md:text-6xl font-bold mb-10 hero-product-title">携手合作</h1>
<h1 className="text-4xl md:text-6xl font-bold mb-10 hero-product-title">共建智慧校园</h1>
<p className="text-xl md:text-2xl max-w-2xl mx-auto leading-loose hero-product-slogan">
<h1 className="text-3xl xs:text-4xl sm:text-5xl md:text-6xl font-bold mb-6 md:mb-10 hero-product-title">携手合作</h1>
<h1 className="text-3xl xs:text-4xl sm:text-5xl md:text-6xl font-bold mb-6 md:mb-10 hero-product-title">共建智慧校园</h1>
<p className="text-base xs:text-lg sm:text-xl md:text-2xl max-w-2xl mx-auto leading-loose hero-product-slogan">
诚邀广西各界教育伙伴共同为职业院校提供优质的数字化解决方案
</p>
</div>
@@ -45,15 +45,15 @@ export default function Contact() {
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">Contact</span>
<span className="w-5 h-[2px] rounded-full bg-[#004ec4]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#004ec4]">Contact</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
联系方式
</h2>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-6">
{[
{ icon: MapPin, title: '公司地址', text: '南宁市总部路3号中国东盟科技企业孵化基地二期9号厂房二楼212房' },
{ icon: Phone, title: '联系电话', text: '0771-3864294' },
@@ -62,21 +62,20 @@ export default function Contact() {
const Icon = item.icon
return (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-[#fafbfc] rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[#043b8f]/5 transition-all duration-500 group overflow-hidden">
<div className="relative bg-[#fafbfc] rounded-xl md:rounded-2xl p-6 sm:p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[#004ec4]/5 transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[#043b8f]/[0.02] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[#043b8f]/[0.06] rounded-tr-3xl" />
style={{ background: 'linear-gradient(90deg, transparent, #004ec4, transparent)' }} />
<div className="absolute top-0 right-0 w-20 sm:w-28 h-20 sm:h-28 bg-[#004ec4]/[0.06] rounded-bl-full" />
<div className="relative z-10">
<div className="flex items-center gap-3 mb-5">
<div className="w-11 h-11 rounded-xl flex items-center justify-center bg-[#043b8f]/[0.06] border border-[#043b8f]/10">
<Icon className="w-5 h-5 text-[#043b8f]" />
<div className="flex items-center gap-3 mb-4 md:mb-5">
<div className="w-9 h-9 sm:w-11 sm:h-11 rounded-xl flex items-center justify-center bg-[#004ec4]/[0.12] border border-[#004ec4]/20">
<Icon className="w-4 h-4 sm:w-5 sm:h-5 text-[#004ec4]" />
</div>
<div className="h-px flex-1 bg-gradient-to-r from-[#043b8f]/10 to-transparent" />
<div className="h-px flex-1 bg-gradient-to-r from-[#004ec4]/25 to-transparent" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{item.title}</h3>
<p className="text-[#64748b] text-sm leading-relaxed">{item.text}</p>
<h3 className="text-lg sm:text-xl font-bold text-[#0f172a] mb-2 md:mb-3">{item.title}</h3>
<p className="text-[#64748b] text-sm sm:text-base leading-relaxed">{item.text}</p>
</div>
</div>
</ScrollReveal>
@@ -92,34 +91,33 @@ export default function Contact() {
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">Partnership</span>
<span className="w-5 h-[2px] rounded-full bg-[#004ec4]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#004ec4]">Partnership</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
成为代理商
</h2>
<p className="text-[#475569] text-lg md:text-xl mt-5 max-w-xl leading-[2]">
<p className="text-[#475569] text-sm sm:text-base md:text-lg lg:text-xl mt-4 md:mt-5 max-w-xl leading-[1.8] md:leading-[2]">
诚邀广西各地区具有教育行业资源的合作伙伴共享职业院校信息化发展机遇
</p>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-14 max-w-3xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6 mb-10 md:mb-14 max-w-3xl mx-auto">
{agentBenefits.map((item, i) => {
return (
<ScrollReveal key={i} delay={i * 0.05}>
<div className="relative bg-white rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[#043b8f]/5 transition-all duration-500 group overflow-hidden">
<div className="relative bg-white rounded-xl md:rounded-2xl p-6 sm:p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[#004ec4]/5 transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
<div className="absolute top-0 right-0 w-32 h-32 bg-[#043b8f]/[0.02] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-20 h-20 border border-[#043b8f]/[0.06] rounded-tr-3xl" />
style={{ background: 'linear-gradient(90deg, transparent, #004ec4, transparent)' }} />
<div className="absolute top-0 right-0 w-24 sm:w-32 h-24 sm:h-32 bg-[#004ec4]/[0.06] rounded-bl-full" />
<div className="relative z-10">
<div className="flex items-center gap-3 mb-4">
<span className="flex items-center justify-center w-8 h-8 rounded-md bg-[#043b8f]/[0.06] text-xs font-black text-[#043b8f]">0{i + 1}</span>
<div className="h-px flex-1 bg-gradient-to-r from-[#043b8f]/10 to-transparent" />
<div className="flex items-center gap-3 mb-3 md:mb-4">
<span className="flex items-center justify-center w-7 h-7 sm:w-8 sm:h-8 rounded-md bg-[#004ec4]/[0.12] text-xs font-black text-[#004ec4]">0{i + 1}</span>
<div className="h-px flex-1 bg-gradient-to-r from-[#004ec4]/25 to-transparent" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{item.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{item.desc}</p>
<h3 className="text-lg sm:text-xl font-bold text-[#0f172a] mb-2 md:mb-3">{item.title}</h3>
<p className="text-[#64748b] text-sm sm:text-base md:text-lg leading-relaxed">{item.desc}</p>
</div>
</div>
</ScrollReveal>
@@ -129,18 +127,18 @@ export default function Contact() {
<ScrollReveal>
<div className="max-w-3xl mx-auto">
<div className="relative bg-white rounded-2xl p-8 md:p-10 border border-gray-100 overflow-hidden">
<div className="relative bg-white rounded-xl md:rounded-2xl p-6 sm:p-8 md:p-10 border border-gray-100 overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-100"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[#043b8f]/[0.02] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[#043b8f]/[0.06] rounded-tr-3xl" />
style={{ background: 'linear-gradient(90deg, transparent, #004ec4, transparent)' }} />
<div className="absolute top-0 right-0 w-20 sm:w-28 h-20 sm:h-28 bg-[#004ec4]/[0.06] rounded-bl-full" />
<div className="relative z-10">
<h3 className="text-2xl md:text-3xl font-black text-[#0f172a] mb-10 tracking-tight leading-[1.1]">合作基本要求</h3>
<div className="space-y-6">
<h3 className="text-xl sm:text-2xl md:text-3xl font-black text-[#0f172a] mb-6 md:mb-10 tracking-tight leading-[1.1]">合作基本要求</h3>
<div className="space-y-4 md:space-y-6">
{requirements.map((req, i) => (
<div key={i} className="flex items-center gap-5">
<span className="flex items-center justify-center w-9 h-9 rounded-lg bg-[#043b8f]/[0.06] text-sm font-black text-[#043b8f] shrink-0">{i + 1}</span>
<span className="text-[#334155] text-lg leading-relaxed">{req}</span>
<div key={i} className="flex items-center gap-4 md:gap-5">
<span className="flex items-center justify-center w-7 h-7 sm:w-9 sm:h-9 rounded-lg bg-[#004ec4]/[0.12] text-xs sm:text-sm font-black text-[#004ec4] shrink-0">{i + 1}</span>
<span className="text-[#334155] text-sm sm:text-base md:text-lg leading-relaxed">{req}</span>
</div>
))}
</div>
@@ -164,22 +162,22 @@ export default function Contact() {
/>
<div className="relative max-w-3xl mx-auto text-center">
<ScrollReveal>
<h2 className="text-4xl md:text-6xl font-bold text-white mb-8">携手讯驰 共建智慧校园</h2>
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-6xl font-bold text-white mb-6 md:mb-8">携手讯驰 共建智慧校园</h2>
<p className="text-white text-md mb-14 max-w-xl mx-auto leading-relaxed">
<p className="text-white text-sm xs:text-base sm:text-lg mb-10 md:mb-14 max-w-xl mx-auto leading-relaxed">
获取产品演示与个性化方案迈出数字化转型第一步
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<div className="flex flex-col sm:flex-row gap-3 xs:gap-4 justify-center">
<Link
to="/"
className="inline-flex items-center gap-2 px-8 py-3 rounded-xl text-sm font-semibold text-white border border-white hover:bg-white/10 transition-all"
className="inline-flex items-center justify-center gap-2 px-6 xs:px-8 py-2.5 xs:py-3 rounded-xl text-xs xs:text-sm font-semibold text-white border border-white hover:bg-white/10 transition-all"
>
返回首页
<ArrowRight className="w-4 h-4" />
<ArrowRight className="w-3.5 h-3.5 xs:w-4 xs:h-4" />
</Link>
<a
href="tel:07713864294"
className="inline-flex items-center gap-2 px-8 py-3 rounded-xl text-sm font-semibold text-white border border-white hover:bg-white/10 transition-all"
className="inline-flex items-center justify-center gap-2 px-6 xs:px-8 py-2.5 xs:py-3 rounded-xl text-xs xs:text-sm font-semibold text-white border border-white hover:bg-white/10 transition-all"
>
致电 0771-3864294
</a>
+245 -284
View File
@@ -1,17 +1,17 @@
import React, { useState, useEffect, useRef } from 'react'
import { Link } from 'react-router-dom';
import HeroSection from '../components/HeroSection'
import ScrollReveal from '../components/ScrollReveal'
import CertificateCarousel from '../components/CertificateCarousel'
import { products } from '../data/products'
import RosePieChart from '../components/RosePieChart'
import { ArrowRight, MapPin, Award, Headphones, Shield } from 'lucide-react'
import StatNumber from '../components/StatNumber'
import hunBg from '../assets/images/hun.jpg'
import {ArrowRight, Play, Users, Building2} from 'lucide-react'
/* ─── Section 3: 数据亮点 ─── */
const stats = [
{ num: '40', suffix: '+', label: '软件著作权' },
{ num: '5', suffix: '项', label: 'ISO体系认证' },
{ num: '15', suffix: '年', label: '行业深耕经验' },
{ num: '16', suffix: '年', label: '行业深耕经验' },
{ num: '94', suffix: '+', label: '合作院校覆盖' },
]
@@ -41,92 +41,29 @@ const capabilities = [
/* ─── Section 7: 客户认可 ─── */
const customerCards = [
{
type: 'school',
icon: Building2,
title: 'ISO 9001 质量管理体系',
},
{
type: 'video',
icon: Play,
title: 'ISO 14001 环境管理体系',
},
{
type: 'satisfaction',
icon: Users,
title: 'ISO 45001 职业健康安全管理体系',
},
{
type: 'satisfaction',
icon: Users,
title: 'ISO 27001 信息安全管理体系',
},
{
type: 'satisfaction',
icon: Users,
title: 'ISO 20000 信息技术服务管理体系',
},
]
/* ─── Animated counter ─── */
function StatNumber({ target, suffix }) {
const [count, setCount] = useState(0)
const ref = useRef(null)
const started = useRef(false)
useEffect(() => {
const el = ref.current
if (!el) return
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting && !started.current) {
started.current = true
const dur = 2000
const start = performance.now()
const num = parseInt(target)
function tick(now) {
const p = Math.min((now - start) / dur, 1)
setCount(Math.round((1 - Math.pow(1 - p, 3)) * num))
if (p < 1) requestAnimationFrame(tick)
}
requestAnimationFrame(tick)
obs.unobserve(el)
}
}, { threshold: 0.3 })
obs.observe(el)
return () => obs.disconnect()
}, [target])
return <span ref={ref}>{count}{suffix}</span>
}
import { useState, useEffect, useRef } from 'react'
export default function Home() {
const [activeCapability, setActiveCapability] = useState(0)
const [tagRadius, setTagRadius] = useState(68)
const [activeStep, setActiveStep] = useState(0)
const [lightPos, setLightPos] = useState(0) // 光点位置(立即移动)
const timerRef = useRef(null)
const activeColor = products[activeCapability]?.themeColor || '#043b8f'
const handleStepClick = (i) => {
if (timerRef.current) clearTimeout(timerRef.current)
setLightPos(i) // 光点立即开始滑动
timerRef.current = setTimeout(() => {
setActiveStep(i) // 700ms 后节点变色
}, 700)
}
useEffect(() => {
const updateRadius = () => {
const w = window.innerWidth
if (w < 480) setTagRadius(40)
else if (w < 768) setTagRadius(48)
else if (w < 1024) setTagRadius(54)
else setTagRadius(60)
}
updateRadius()
window.addEventListener('resize', updateRadius)
return () => window.removeEventListener('resize', updateRadius)
return () => { if (timerRef.current) clearTimeout(timerRef.current) }
}, [])
return (
@@ -159,98 +96,108 @@ export default function Home() {
{/* ─── 4. 开放能力区(白底) ─── */}
<section id="products" className="py-20 px-4 bg-white">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-14">
<div className="max-w-7xl mx-auto">
<ScrollReveal className="text-center mb-8 md:mb-14">
<p className="text-[10px] xs:text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Core Capabilities</p>
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-7xl font-bold text-[#0f172a] mb-6">四大产品体系</h2>
<p className="text-[#64748b] text-sm xs:text-base md:text-lg lg:text-xl mx-auto md:whitespace-nowrap mb-4 leading-relaxed">全面覆盖职业院校教学管理党建科研四大核心场景为数字化校园建设提供一站式解决方案</p>
<h2 className="text-2xl xs:text-3xl sm:text-4xl md:text-5xl lg:text-7xl font-bold text-[#0f172a] mb-6">四大产品体系</h2>
<p className="text-[#64748b] text-sm xs:text-base md:text-lg lg:text-xl xl:text-2xl mx-auto max-w-3xl md:whitespace-nowrap mb-4 leading-relaxed px-2">全面覆盖职业院校教学管理党建科研四大核心场景为数字化校园建设提供一站式解决方案</p>
</ScrollReveal>
{/* Tab bar */}
<ScrollReveal>
<div className="flex justify-center gap-1 mb-12 flex-wrap">
{products.map((p, i) => (
<div className="flex flex-wrap justify-center gap-1 sm:gap-1.5 mb-8 md:mb-26">
{products.map((p, i) => {
const isActive = activeCapability === i
return (
<button
key={p.slug}
onClick={() => setActiveCapability(i)}
className={`flex items-center gap-1.5 xs:gap-2 px-3 xs:px-4 sm:px-6 py-2 xs:py-3 rounded-lg text-xs xs:text-sm font-medium transition-all border ${activeCapability === i
? 'text-[#043b8f] border-[#043b8f]'
: 'text-[#475569] border-gray-200 hover:text-[#043b8f] hover:border-[#043b8f]'
}`}
className="flex items-center justify-center gap-1 xs:gap-2 sm:gap-3 px-2 xs:px-3 sm:px-5 md:px-7 py-2 xs:py-2.5 sm:py-3.5 rounded-lg text-xs xs:text-sm sm:text-base md:text-lg lg:text-xl font-bold transition-all border w-[calc(50%-0.25rem)] xs:w-[calc(50%-0.375rem)] sm:w-auto"
style={{
color: isActive ? p.themeColor : '#475569',
borderColor: isActive ? p.themeColor : '#e5e7eb',
}}
onMouseEnter={(e) => { if (!isActive) { e.target.style.color = p.themeColor; e.target.style.borderColor = p.themeColor; } }}
onMouseLeave={(e) => { if (!isActive) { e.target.style.color = '#475569'; e.target.style.borderColor = '#e5e7eb'; } }}
>
<p.icon className="w-4 h-4 xs:w-5 xs:h-5" style={{ color: activeCapability === i ? '#043b8f' : p.themeColor }} />
<p.icon className="w-3 h-3 xs:w-4 xs:h-4 sm:w-5 sm:h-5" style={{ color: isActive ? p.themeColor : p.themeColor }} />
{p.name}
</button>
))}
)
})}
</div>
</ScrollReveal>
{/* Left-right layout */}
<ScrollReveal>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center mb-12">
{/* Left: visual */}
<div className="flex justify-center">
<RosePieChart onSelect={setActiveCapability} activeIndex={activeCapability} />
<div className="flex flex-col mb-16 md:mb-32 mt-4 md:mt-8 bg-white rounded-2xl md:rounded-3xl border border-gray-200 shadow-lg overflow-hidden">
{/* ── 窗口标题栏(macOS 风格) ── */}
<div className="flex items-center gap-2 px-4 py-2.5 md:px-5 md:py-3 bg-gray-100 border-b border-gray-200">
<div className="w-3 h-3 rounded-full bg-red-400 flex-shrink-0" />
<div className="w-3 h-3 rounded-full bg-yellow-400 flex-shrink-0" />
<div className="w-3 h-3 rounded-full bg-green-400 flex-shrink-0" />
<span className="text-xs text-gray-400 ml-2 font-medium truncate">
{capabilities[activeCapability].title} 产品详情
</span>
</div>
{/* ── 内容区 ── */}
<div className="flex flex-col lg:flex-row gap-6 md:gap-8 lg:gap-12 xl:gap-24 lg:items-stretch p-6 sm:p-8 md:p-10 lg:p-12 bg-gradient-to-br from-gray-50 to-white lg:h-[520px]">
{/* Left: visual — 产品图片 */}
<div className="w-full lg:w-1/2 lg:max-w-xl flex items-center justify-center">
{products[activeCapability]?.heroImage && (
<img src={products[activeCapability].heroImage} alt={products[activeCapability].name} className="w-full h-auto max-h-full object-contain" />
)}
</div>
{/* Right: text */}
<div className="space-y-4">
{(activeCapability !== null ? [capabilities[activeCapability]] : capabilities).map((c, i) => {
const idx = activeCapability !== null ? activeCapability : i
return (
<div key={idx} className={`relative p-8 rounded-2xl transition-all duration-500 cursor-pointer group overflow-hidden ${activeCapability === idx
? 'bg-white border border-[#043b8f]/20 shadow-[0_4px_24px_rgba(4,59,199,0.12)]'
: 'hover:bg-gray-50/80 border border-gray-100'
}`} onClick={() => setActiveCapability(idx)}>
{activeCapability === idx && (
<div className="absolute top-0 left-0 right-0 h-[2px]"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
)}
<div className="absolute top-0 right-0 w-24 h-24 bg-[#043b8f]/[0.03] rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[#043b8f]/[0.06] rounded-tr-3xl opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="flex items-center gap-3 mb-3">
<span className={`flex items-center justify-center w-7 h-7 rounded-md text-[11px] font-black transition-colors duration-300 ${activeCapability === idx
? 'bg-[#043b8f]/[0.08] text-[#043b8f]'
: 'bg-gray-100 text-[#94a3b8]'
}`}>0{idx + 1}</span>
<div className={`h-px flex-1 transition-colors duration-300 ${activeCapability === idx
? 'bg-gradient-to-r from-[#043b8f]/15 to-transparent'
: 'bg-gray-100'
}`} />
</div>
<h3 className={`text-lg xs:text-xl sm:text-2xl font-bold transition-colors duration-300 ${activeCapability === idx ? 'text-[#043b8f]' : 'text-[#0f172a]'
}`}>{c.title}</h3>
<p className={`text-sm xs:text-base mt-1.5 font-medium transition-colors duration-300 ${activeCapability === idx ? 'text-[#043b8f]/60' : 'text-[#94a3b8]'
}`}>{c.sub}</p>
<p className="text-sm xs:text-base text-[#334155] leading-[2] mt-4">{c.desc}</p>
{c.tip && (
<div className="mt-5 flex flex-wrap gap-2">
{c.tip.split('、').map((t, ti) => (
<span key={ti} className={`px-3 py-1 xs:px-4 xs:py-1.5 text-xs xs:text-sm font-medium rounded-md transition-all duration-200 ${
activeCapability === idx
? 'text-[#043b8f] bg-[#043b8f]/[0.08]'
: 'text-[#94a3b8] bg-gray-50'
}`}>
{t}
</span>
))}
</div>
)}
{activeCapability === idx && (
<div className="mt-5 flex justify-end">
<Link to={`/product/${c.slug}`}
onClick={(e) => e.stopPropagation()}
className="text-sm text-[#043b8f]/60 hover:text-[#043b8f] hover:underline flex items-center gap-1 transition-colors duration-200">
查看全部
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" /></svg>
</Link>
</div>
)}
</div>
)
})}
{/* Right: text — 纯文字布局 */}
<div className="w-full lg:w-1/2 lg:max-w-xl flex flex-col justify-center">
{/* 顶部装饰线 + 标签 */}
<div className="flex items-center gap-3 mb-3 md:mb-4">
<span className="w-6 md:w-8 h-[2px] rounded-full" style={{ backgroundColor: activeColor + '66' }} />
<span className="text-xs sm:text-sm md:text-lg font-bold tracking-[0.22em] uppercase" style={{ color: activeColor + '99' }}>
Core Capability
</span>
</div>
{/* 标题 */}
<h3 className="text-xl sm:text-2xl md:text-3xl lg:text-4xl xl:text-5xl font-black text-[#0f172a] tracking-tight leading-[1.1] mb-2">
{capabilities[activeCapability].title}
</h3>
{/* 副标题 */}
<p className="text-sm sm:text-base md:text-lg lg:text-xl font-semibold mb-4 md:mb-5" style={{ color: activeColor + 'aa' }}>
{capabilities[activeCapability].sub}
</p>
{/* 正文 */}
<p className="text-[#334155] text-sm sm:text-base md:text-lg lg:text-xl leading-[1.7] md:leading-[1.8] font-medium mb-4 md:mb-6">
{capabilities[activeCapability].desc}
</p>
{/* Tags — 只展示前 3 个 */}
{capabilities[activeCapability].tip && (
<div className="flex flex-wrap items-center gap-1.5 md:gap-2 pt-4 md:pt-5 border-t border-gray-100">
{capabilities[activeCapability].tip.split('、').slice(0, 3).map((t, ti) => (
<span key={ti} className="px-2 md:px-3 py-1 text-xs sm:text-sm md:text-base font-semibold rounded-lg border transition-all duration-300 hover:-translate-y-1 cursor-default"
style={{ color: activeColor, borderColor: activeColor + '40', backgroundColor: activeColor + '0a' }}>
{t}
</span>
))}
</div>
)}
{/* 查看全部 */}
<div className="mt-auto pt-4 md:pt-5 flex justify-end">
<Link to={`/product/${capabilities[activeCapability].slug}`}
className="inline-flex items-center gap-1.5 px-3 md:px-4 py-1.5 md:py-2 rounded-lg text-sm md:text-base font-semibold transition-all duration-300 hover:brightness-110"
style={{ color: '#fff', backgroundColor: activeColor }}>
查看全部
<svg className="w-3 h-3 md:w-3.5 md:h-3.5 transition-transform duration-300 group-hover:translate-x-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" /></svg>
</Link>
</div>
</div>
</div>
</div>
</ScrollReveal>
{/* Bottom button */}
@@ -266,150 +213,164 @@ export default function Home() {
</div>
</section>
{/* ─── 5. 企业级解决方案区(深色) ─── */}
<section className="py-20 px-4 relative overflow-hidden"
style={{ background: `linear-gradient(180deg, rgba(0,10,30,0.7) 0%, rgba(0,20,50,0.5) 50%, rgba(0,10,30,0.7) 100%), url(${hunBg}) center/cover no-repeat` }}>
{/* ─── 5. 为什么选择讯驰 ─── */}
<section className="py-20 md:py-32 px-4 sm:px-6 md:px-10 relative overflow-hidden"
style={{
background: `linear-gradient(135deg, rgba(10,22,40,0.75), rgba(15,31,58,0.75)), url(${hunBg}) center/cover no-repeat`,
}}>
{/* 大型光晕 */}
<div className="absolute top-0 left-1/4 w-[800px] h-[800px] rounded-full opacity-[0.04]"
style={{ background: 'radial-gradient(circle, #004ec4, transparent 60%)' }}
/>
<div className="absolute bottom-0 right-1/4 w-[600px] h-[600px] rounded-full opacity-[0.04]"
style={{ background: 'radial-gradient(circle, #2563eb, transparent 60%)' }}
/>
<div className="relative max-w-5xl mx-auto text-center">
<div className="relative max-w-7xl mx-auto">
{/* 标题 */}
<ScrollReveal>
<p className="text-[10px] xs:text-xs text-white/80 tracking-[0.3em] uppercase mb-4 font-semibold">Why Choose Us</p>
<h2 className="text-3xl xs:text-4xl md:text-6xl font-black mb-6 leading-tight text-white"
style={{
textShadow: '0 4px 30px rgba(59,130,246,0.3), 0 2px 4px rgba(0,0,0,0.4)',
}}>为什么选择讯驰</h2>
<div className="flex items-center justify-center gap-3 mb-16">
<span className="w-12 h-px bg-gradient-to-r from-transparent to-white/60" />
<p className="text-white text-base xs:text-lg sm:text-xl md:text-2xl font-medium tracking-wider">AAA级信用企业 · 高新技术企业</p>
<span className="w-12 h-px bg-gradient-to-l from-transparent to-white/60" />
<div className="text-center mb-12 md:mb-20">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-2 md:gap-3 mb-4 md:mb-5">
<span className="w-6 md:w-8 h-[2px] rounded-full bg-white/30" />
<span className="text-[10px] md:text-[11px] font-bold tracking-[0.22em] uppercase text-white/60">Why Choose Us</span>
<span className="w-6 md:w-8 h-[2px] rounded-full bg-white/30" />
</div>
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-black text-white tracking-tight leading-[1.1] mb-3 md:mb-4"
style={{ textShadow: '0 4px 60px rgba(59,130,246,0.25), 0 2px 8px rgba(0,0,0,0.5)' }}>
为什么选择讯驰
</h2>
<p className="text-white/50 text-sm xs:text-base md:text-lg font-medium tracking-wider">
AAA级信用企业 · 高新技术企业 · 16年行业深耕
</p>
</div>
</div>
</ScrollReveal>
{/* Surrounding tags */}
<ScrollReveal>
<div className="relative w-[280px] h-[280px] xs:w-[320px] xs:h-[320px] md:w-[420px] md:h-[420px] lg:w-[500px] lg:h-[500px] mx-auto mb-16">
{/* Outer rings */}
<div className="absolute inset-0 rounded-full border border-white/[0.08]" />
<div className="absolute inset-6 xs:inset-8 md:inset-10 lg:inset-12 rounded-full border border-white/[0.06]" />
<div className="absolute inset-12 xs:inset-16 md:inset-20 lg:inset-24 rounded-full border border-white/[0.04]" />
{/* Orbiting dots */}
<div className="absolute w-2 h-2 rounded-full bg-[#004ec4]"
style={{ top: '8%', left: '50%', transform: 'translateX(-50%)', boxShadow: '0 0 10px rgba(0,78,196,0.6)' }} />
<div className="absolute w-1.5 h-1.5 rounded-full bg-[#f08519]"
style={{ bottom: '12%', right: '15%', boxShadow: '0 0 8px rgba(240,133,25,0.6)' }} />
{/* Tags around circle */}
{[
{ text: '深耕广西,扎根本土', angle: 250 },
{ text: '产品成熟,实战验证', angle: 300 },
{ text: '贴心服务,全程陪伴', angle: 55 },
{ text: '数据安全,合规可靠', angle: 120 },
].map((tag, i) => {
const rad = (tag.angle * Math.PI) / 180
const r = tagRadius
const x = 50 + r * Math.sin(rad)
const y = 50 - r * Math.cos(rad)
const colors = [
{ bg: 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.08) 100%)', dark: 'rgba(255,255,255,0.2)', glow: 'rgba(255,255,255,0.1)', border: 'rgba(255,255,255,0.25)' },
{ bg: 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.08) 100%)', dark: 'rgba(255,255,255,0.2)', glow: 'rgba(255,255,255,0.1)', border: 'rgba(255,255,255,0.25)' },
{ bg: 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.08) 100%)', dark: 'rgba(255,255,255,0.2)', glow: 'rgba(255,255,255,0.1)', border: 'rgba(255,255,255,0.25)' },
{ bg: 'linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.08) 100%)', dark: 'rgba(255,255,255,0.2)', glow: 'rgba(255,255,255,0.1)', border: 'rgba(255,255,255,0.25)' },
]
return (
<div
key={i}
className="absolute whitespace-nowrap"
style={{
left: `${x}%`,
top: `${y}%`,
transform: 'translate(-50%, -50%)',
}}
>
{/* 书签主体 */}
<div
className="relative px-3 py-2 xs:px-4 xs:py-2.5 md:px-6 md:py-3.5 lg:px-7 lg:py-4 text-xs xs:text-sm md:text-base lg:text-lg font-bold text-white/95 overflow-hidden"
style={{
background: colors[i].bg,
borderRadius: '12px 12px 4px 4px',
border: `1px solid ${colors[i].border}`,
backdropFilter: 'blur(12px)',
boxShadow: `0 0 25px ${colors[i].glow}, 0 6px 16px rgba(0,0,0,0.25)`,
}}
>
{/* 顶部高光条 */}
<div className="absolute top-0 left-0 right-0 h-[2px]"
style={{ background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent)' }}
/>
{/* 左侧折角 */}
<div className="absolute top-0 left-0 w-4 h-4"
style={{
background: 'linear-gradient(135deg, rgba(255,255,255,0.2) 50%, transparent 50%)',
borderRadius: '12px 0 0 0',
}}
/>
{i < 2
? <>{tag.text}<span className="inline-block w-1.5 h-1.5 xs:w-2 xs:h-2 rounded-full bg-white/80 ml-1.5 xs:ml-2 align-middle" /></>
: <><span className="inline-block w-1.5 h-1.5 xs:w-2 xs:h-2 rounded-full bg-white/80 mr-1.5 xs:mr-2 align-middle" />{tag.text}</>
}
</div>
</div>
)
})}
</div>
</ScrollReveal>
{/* Bottom button */}
<ScrollReveal>
<Link
to="/contact"
className="inline-flex items-center gap-2 px-6 xs:px-8 py-2.5 xs:py-3 rounded-full text-sm xs:text-base font-medium text-white border border-white/20 hover:bg-white/10 hover:border-white/40 transition-all duration-300"
style={{ boxShadow: '0 4px 20px rgba(0,0,0,0.2)' }}
>
获取解决方案
<ArrowRight className="w-4 h-4" />
</Link>
</ScrollReveal>
</div>
</section>
{/* ─── 7. 客户认可区(白底) ─── */}
<section className="py-20 px-4 bg-[#f8fafc]">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-14">
<p className="text-[10px] xs:text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Recognition</p>
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-6xl font-bold text-[#0f172a] mb-4">资质与认证</h2>
<p className="text-[#64748b] text-sm xs:text-base max-w-xl mx-auto">AAA级信用企业 · 高新技术企业</p>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{customerCards.map((card, i) => {
const Icon = card.icon
{/* 4 大优势 — 响应式不换行 */}
<div className="flex flex-wrap md:flex-nowrap justify-center gap-2 sm:gap-4 md:gap-6 lg:gap-10 mb-12 md:mb-16">
{[
{ icon: MapPin, title: '深耕广西' },
{ icon: Award, title: '产品成熟'},
{ icon: Headphones, title: '贴心服务'},
{ icon: Shield, title: '数据安全' },
].map((item, i) => {
const Icon = item.icon
return (
<ScrollReveal key={i} delay={i * 0.1}>
<div className="group relative bg-white rounded-2xl p-8 border border-gray-100 text-center h-full flex flex-col items-center transition-all duration-500 hover:shadow-xl hover:shadow-[#043b8f]/5 hover:-translate-y-1 overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: 'linear-gradient(90deg, transparent, #043b8f, transparent)' }} />
<div className="absolute top-0 right-0 w-20 h-20 bg-[#043b8f]/[0.03] rounded-bl-full opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="absolute bottom-0 left-0 w-14 h-14 border border-[#043b8f]/[0.06] rounded-tr-3xl opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
{/* 图标 */}
<div className="w-12 h-12 xs:w-14 xs:h-14 sm:w-16 sm:h-16 rounded-xl bg-[#043b8f]/[0.06] border border-[#043b8f]/10 flex items-center justify-center mb-5 group-hover:bg-[#043b8f]/10 transition-all duration-300">
<Icon className="w-6 h-6 xs:w-7 xs:h-7 sm:w-8 sm:h-8 text-[#043b8f]" />
<div className="group relative block w-[42vw] sm:w-[42vw] md:w-48 lg:w-56 xl:w-64 2xl:w-72 cursor-pointer flex-shrink-0" onClick={() => handleStepClick(i)}>
<div className="relative bg-white/[0.04] border border-white/[0.06] rounded-2xl md:rounded-3xl p-3 sm:p-4 md:p-6 lg:p-8 text-center transition-all duration-500 hover:bg-white/[0.08] hover:border-[#004ec4]/40 hover:-translate-y-1 h-36 sm:h-44 md:h-52 lg:h-56 xl:h-60 2xl:h-64 flex flex-col items-center justify-center"
style={{ boxShadow: '0 8px 40px rgba(0,0,0,0.2)' }}
>
{/* hover 高光线 */}
<div className="absolute top-0 left-6 right-6 md:left-8 md:right-8 h-[2px] opacity-0 group-hover:opacity-100 transition-all duration-700 rounded-full"
style={{ background: 'linear-gradient(90deg, transparent, #004ec4 20%, #004ec4 80%, transparent)' }}
/>
{/* 图标 */}
<div className="w-10 h-10 sm:w-12 sm:h-12 md:w-14 md:h-14 lg:w-16 lg:h-16 rounded-xl md:rounded-2xl bg-gradient-to-br from-[#004ec4]/25 to-[#004ec4]/5 flex items-center justify-center mx-auto mb-3 sm:mb-4 md:mb-5 group-hover:from-[#004ec4]/35 group-hover:to-[#004ec4]/15 group-hover:scale-105 group-hover:shadow-xl group-hover:shadow-[#004ec4]/15 transition-all duration-500">
<Icon className="w-5 h-5 sm:w-6 sm:h-6 md:w-7 md:h-7 lg:w-8 lg:h-8" style={{ color: '#74b0ff' }} strokeWidth={1.5} />
</div>
{/* 标题 */}
<h3 className="text-sm sm:text-base md:text-lg lg:text-xl xl:text-2xl font-bold text-white mb-1 md:mb-2">{item.title}</h3>
{/* 描述 */}
<p className="text-[10px] sm:text-xs md:text-sm text-white/45 leading-relaxed">{item.desc}</p>
</div>
{/* 数字 */}
<div className="text-3xl xs:text-4xl sm:text-5xl font-black text-transparent bg-clip-text bg-gradient-to-b from-[#0f172a] to-[#334155] mb-1">{card.count}</div>
{/* 标题 */}
<h3 className="text-sm xs:text-base sm:text-lg font-bold text-[#0f172a] mb-3">{card.title}</h3>
{/* 分隔线 */}
<div className="w-8 h-px bg-gradient-to-r from-transparent via-[#043b8f]/20 to-transparent mb-3" />
{/* 描述 */}
<p className="text-sm text-[#64748b] leading-relaxed">{card.desc}</p>
</div>
</ScrollReveal>
)
})}
</div>
{/* ── 流动光速时间线 ── */}
<div className="relative max-w-2xl mx-auto mt-12 md:mt-20 mb-12 md:mb-20 px-4">
{/* 光束外层光晕 */}
<div className="absolute top-1/2 -translate-y-1/2 left-[8%] right-[8%] h-4 rounded-full"
style={{
background: 'linear-gradient(90deg, transparent, rgba(0,78,196,0.08) 15%, rgba(0,78,196,0.15) 50%, rgba(0,78,196,0.08) 85%, transparent)',
filter: 'blur(6px)',
}}
/>
{/* 光束中层 */}
<div className="absolute top-1/2 -translate-y-1/2 left-[8%] right-[8%] h-1.5 rounded-full"
style={{
background: 'linear-gradient(90deg, transparent, rgba(59,130,246,0.2) 10%, rgba(96,165,250,0.5) 30%, rgba(147,197,253,0.7) 50%, rgba(96,165,250,0.5) 70%, rgba(59,130,246,0.2) 90%, transparent)',
filter: 'blur(3px)',
}}
/>
{/* 光束核心 */}
<div className="relative h-1 rounded-full"
style={{
background: 'linear-gradient(90deg, transparent, rgba(59,130,246,0.4) 10%, rgba(147,197,253,0.8) 30%, #93c5fd 50%, rgba(147,197,253,0.8) 70%, rgba(59,130,246,0.4) 90%, transparent)',
boxShadow: '0 0 12px rgba(59,130,246,0.3), 0 0 24px rgba(59,130,246,0.1)',
}}>
{/* 流动光点 */}
<div className="absolute top-1/2 -translate-y-1/2 w-3 h-3 rounded-full transition-all duration-700 ease-out"
style={{
left: `${12 + lightPos * 25}%`,
background: '#e0f2fe',
boxShadow: '0 0 12px #93c5fd, 0 0 28px #60a5fa, 0 0 56px #3b82f6, 0 0 84px #1d4ed8',
}}
/>
{/* 4 个节点标记 */}
{[0, 1, 2, 3].map(n => (
<div key={n} className="absolute top-1/2 -translate-y-1/2 w-3 h-3 rounded-full border-2 transition-all duration-500"
style={{
left: `${12 + n * 25}%`,
borderColor: n <= activeStep ? '#93c5fd' : 'rgba(147,197,253,0.25)',
background: n <= activeStep ? '#3b82f6' : 'rgba(10,22,40,0.9)',
boxShadow: n <= activeStep ? '0 0 10px rgba(147,197,253,0.7), 0 0 20px rgba(59,130,246,0.4)' : '0 0 4px rgba(0,78,196,0.2)',
}}
/>
))}
</div>
</div>
{/* 底部按钮 */}
<ScrollReveal className="text-center">
<Link
to="/contact"
className="inline-flex items-center gap-2 md:gap-3 px-6 sm:px-8 md:px-10 py-3 md:py-4 rounded-24 text-sm md:text-base font-bold text-white bg-[#1266e5cb] hover:bg-[#005ce6] transition-all duration-300 hover:shadow-2xl hover:shadow-[#004ec4]/30 hover:scale-105"
>
获取专属解决方案
<ArrowRight className="w-5 h-5" />
</Link>
</ScrollReveal>
</div>
</section>
{/* ─── 7. 资质与认证 ─── */}
<section className="py-24 px-4 bg-gradient-to-b from-white via-[#f8fafc] to-white">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="text-center mb-16">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-8 h-[2px] rounded-full bg-[#004ec4]/40" />
<span className="text-[11px] font-bold tracking-[0.22em] uppercase text-[#004ec4]/60">Recognition</span>
<span className="w-8 h-[2px] rounded-full bg-[#004ec4]/40" />
</div>
<h2 className="text-4xl xs:text-5xl sm:text-6xl md:text-7xl font-black text-[#0f172a] tracking-tight leading-[1.1] mb-4">资质与认证</h2>
<p className="text-[#64748b] text-base xs:text-lg font-medium">AAA级信用企业 · 高新技术企业</p>
</div>
</ScrollReveal>
{/* 5 个 ISO 认证证书 — 轮播图 */}
<ScrollReveal>
<CertificateCarousel />
</ScrollReveal>
</div>
</section>
+592 -150
View File
@@ -1,13 +1,37 @@
import { useParams, Link } from 'react-router-dom'
import { ChevronDown, ArrowRight, Shield, Clock, Headphones, AlertCircle } from 'lucide-react'
import { getProduct } from '../data/products'
import ScrollReveal from '../components/ScrollReveal'
import { useParams, Link } from "react-router-dom"
import { useState, useEffect, useRef } from "react"
import { ChevronDown, ArrowRight, Shield, Clock, Headphones, Cpu, Monitor, HardDrive, AlertCircle, GitBranch, FileStack, MessageCircleX, FileDigit, SearchCheck, Banknote, ChartBar, Bell, Layers, ScrollText, CalendarCheck, TrendingUp, Pyramid, Database, Activity, Star } from "lucide-react"
import { getProduct } from "../data/products"
import ScrollReveal from "../components/ScrollReveal"
import StatNumber from "../components/StatNumber"
import Carousel from "../components/Carousel"
import ThemedSvg from "../components/ThemedSvg"
// 图标映射表 — 所有图标均来自 Lucide React (MIT License,免费商用)
const iconMap = {
GitBranch, FileStack, MessageCircleX, FileDigit, SearchCheck,
Banknote, ChartBar, Bell, Layers, ScrollText, CalendarCheck,
TrendingUp, Pyramid, Database, Activity,
}
export default function ProductPage() {
const { slug } = useParams()
const product = getProduct(slug)
const [activeStep, setActiveStep] = useState(0)
const [lightPos, setLightPos] = useState(0)
const timerRef = useRef(null)
const handleStepClick = (i) => {
if (timerRef.current) clearTimeout(timerRef.current)
setLightPos(i)
timerRef.current = setTimeout(() => {
setActiveStep(i)
}, 700)
}
useEffect(() => {
return () => { if (timerRef.current) clearTimeout(timerRef.current) }
}, [])
if (!product) {
return (
@@ -21,118 +45,268 @@ export default function ProductPage() {
)
}
// 将 hex 主题色转为 RGB 分量,用于 rgba() 可变透明度
const hex = product.themeColor.replace('#', '')
const r = parseInt(hex.slice(0, 2), 16)
const g = parseInt(hex.slice(2, 4), 16)
const b = parseInt(hex.slice(4, 6), 16)
// const Icon = product.icon
// const otherProducts = products.filter(p => p.slug !== slug)
return (
<main className="bg-white">
<main
className="bg-white"
style={{
'--theme': product.themeColor,
'--theme-r': r,
'--theme-g': g,
'--theme-b': b,
}}
>
{/* ── Hero ── */}
<div className="relative -mt-[70px]">
<section className="relative py-32 md:py-40 text-white overflow-hidden">
{/* Background image */}
{product.heroImage && (
<div className="absolute inset-0">
<img src={product.heroImage} alt="" className="w-full h-full object-cover" />
<section className="relative overflow-hidden" style={{ backgroundColor: product.themeColor }}>
{/* Desktop: grid 布局 — 统一宽度/高度/间距 */}
<div className="hidden md:grid grid-cols-2 gap-12 lg:gap-20 xl:gap-28 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 min-h-[920px] items-center">
{/* Image - left */}
<div className="flex items-center justify-center">
{product.heroImage && (
<img src={product.heroImage} alt="" className="w-full h-auto object-contain" />
)}
</div>
)}
{/* Black overlay */}
<div className="absolute inset-0 bg-black/50" />
{/* Content - right */}
<div className="flex flex-col items-center justify-center gap-5 md:gap-6">
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-extrabold text-center tracking-tight leading-[1.1] text-white hero-product-title">
{product.name}
</h1>
<p className="text-lg sm:text-xl md:text-2xl lg:text-3xl text-center leading-relaxed text-white/80 hero-product-slogan">
{product.slogan}
</p>
{product.stats && (
<div className="grid grid-flow-col auto-cols-fr gap-5 md:gap-6 w-full mt-6 md:mt-8">
{product.stats.map((s, i) => (
<div key={i} className="rounded-2xl p-4 text-center border border-white/20 bg-white/10">
<div className="text-2xl md:text-3xl font-extrabold tracking-tight text-white">
<StatNumber target={s.num} suffix={s.suffix} />
</div>
<div className="text-xs text-white/60 mt-1.5 font-medium">{s.label}</div>
</div>
))}
</div>
)}
</div>
</div>
<div className="relative max-w-4xl mx-auto px-6 text-center pt-32">
<h1 className="text-4xl md:text-7xl font-extrabold mb-5 tracking-tight leading-[1.1] text-white hero-product-title">
{product.name}
</h1>
<p className="text-2xl md:text-3xl mb-6 max-w-xl mx-auto leading-relaxed hero-product-slogan">
{product.slogan}
</p>
{/* Stats */}
{product.stats && (
<div className="flex justify-center mt-14 rounded-2xl border border-white/10 overflow-hidden max-w-xl mx-auto"
style={{ backgroundColor: 'rgba(255,255,255,0.08)', backdropFilter: 'blur(8px)' }}>
{product.stats.map((s, i) => (
<div key={i} className={`flex flex-col items-center justify-center px-6 md:px-10 py-5 ${i < product.stats.length - 1 ? 'border-r border-white/10' : ''}`}>
<div className="text-2xl md:text-3xl font-extrabold tracking-tight text-white">{s.num}</div>
<div className="text-xs text-white/50 mt-2 font-medium">{s.label}</div>
</div>
))}
</div>
{/* Mobile: stacked layout */}
<div className="md:hidden flex flex-col items-center px-6 pt-32 pb-16">
{product.heroImage && (
<img src={product.heroImage} alt="" className={`w-4/5 max-w-xs h-auto object-contain mb-8 sm:mb-10 ${product.heroSize === 'small' ? 'max-h-[150px]' : 'max-h-[200px]'}`} />
)}
<div className="text-center w-full">
<span className="inline-block px-3 sm:px-4 py-1 sm:py-1.5 rounded-full text-[10px] sm:text-xs font-bold tracking-wider uppercase mb-4 sm:mb-5"
style={{ backgroundColor: product.themeColor + '18', color: product.themeColor }}>
智慧教育解决方案
</span>
<h1 className="text-2xl xs:text-3xl sm:text-4xl font-extrabold mb-3 sm:mb-4 tracking-tight leading-[1.1] text-white hero-product-title">
{product.name}
</h1>
<p className="text-base xs:text-lg sm:text-xl mb-5 sm:mb-6 leading-relaxed text-white/80 hero-product-slogan">
{product.slogan}
</p>
{product.stats && (
<div className="grid grid-cols-3 gap-2 sm:gap-3 mt-3 sm:mt-4">
{product.stats.map((s, i) => (
<div key={i} className="rounded-xl sm:rounded-2xl p-2 sm:p-3 text-center border border-white/20 bg-white/10">
<div className="text-lg xs:text-xl sm:text-2xl font-extrabold tracking-tight text-white">
<StatNumber target={s.num} suffix={s.suffix} />
</div>
<div className="text-[10px] xs:text-[11px] sm:text-xs text-white/60 mt-1 font-medium">{s.label}</div>
</div>
))}
</div>
)}
</div>
</div>
</section>
</div>
{/* ── Brief ── */}
<section className="py-20 px-6 bg-white">
<div className="max-w-3xl mx-auto">
{/* ── Brief / 产品简介 ── */}
<section className="py-16 md:py-20 px-6 bg-[#f8fafc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal>
<div className="relative bg-[#f8fafc] rounded-3xl p-10 md:p-14 overflow-hidden">
{/* Floating decorative shapes */}
<div className="absolute -top-6 -right-6 w-24 h-24 rounded-full bg-[#043b8f]" />
<div className="absolute -bottom-8 -left-8 w-20 h-20 rounded-2xl rotate-12 bg-[#043b8f]" />
<div className="absolute top-1/2 -right-4 w-8 h-8 rounded-full border-2 border-[#043b8f]" />
{/* ═══ 折角卡片 ═══ */}
<div className="relative bg-white rounded-2xl shadow-[0_4px_32px_-8px_rgba(0,0,0,0.08),0_0_0_1px_rgba(0,0,0,0.04)]">
<p className="text-[#334155] text-lg md:text-xl leading-loose relative z-10">
{product.brief}
</p>
</div>
{product.tags && (
<div className="flex flex-wrap items-center justify-center gap-3 mt-10">
{product.tags.map((tag, i) => (
<span key={i}
className="relative px-6 py-2 text-sm font-bold text-white transition-all duration-200 hover:scale-105 hover:-translate-y-0.5"
style={{ backgroundColor: '#043b8f' }}>
<span className="relative z-10">{tag}</span>
<svg className="absolute -bottom-2 left-0 w-full h-2 text-white/20" viewBox="0 0 100 8" preserveAspectRatio="none">
<path d="M0 0 L35 8 L65 8 L100 0" fill="currentColor" />
</svg>
<span className="absolute top-1/2 -translate-y-1/2 -left-2 w-1 h-4 rounded-full" style={{ backgroundColor: '#043b8f' }} />
</span>
))}
{/* ── 右上折角 ── */}
<div className="absolute top-0 right-0 w-[100px] h-[100px] md:w-[130px] md:h-[130px] overflow-hidden rounded-tr-2xl z-20 pointer-events-none">
{/* 折角在卡片上投下的阴影 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
clipPath: 'polygon(100% 0, 100% 100%, 0 0)',
background: 'rgba(0,0,0,0.1)',
filter: 'blur(4px)',
transform: 'translate(2px, 2px)',
}}
/>
{/* 折角主体 — 品牌色渐变,模拟纸张背面 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
clipPath: 'polygon(100% 0, 100% 100%, 0 0)',
background: `linear-gradient(225deg, ${product.themeColor} 0%, rgba(${r},${g},${b},0.78) 100%)`,
}}
/>
{/* 折角内面高光 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
clipPath: 'polygon(100% 0, 100% 100%, 0 0)',
background: `linear-gradient(225deg, rgba(255,255,255,0.28) 0%, transparent 50%)`,
}}
/>
{/* 折痕线 */}
<div className="absolute top-0 right-0 w-full h-full"
style={{
background: `linear-gradient(225deg, transparent 37%, rgba(0,0,0,0.07) 39%, rgba(0,0,0,0.03) 41%, transparent 43%)`,
}}
/>
{/* 图标 */}
<Star className="absolute w-[16px] h-[16px] md:w-[20px] md:h-[20px] text-yellow-400 fill-yellow-400 drop-shadow-sm"
style={{ top: '18px', right: '14px', transform: 'rotate(45deg)' }}
strokeWidth={1.5}
/>
</div>
)}
{/* ── 内容区 ── */}
<div className="relative p-6 sm:p-8 md:p-10 lg:p-14">
{/* 顶部装饰线 + 标签 */}
<div className="flex items-center gap-3 mb-4 md:mb-5">
<span className="w-8 md:w-10 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-xs sm:text-sm md:text-base font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">
Brief
</span>
</div>
{/* 标题 */}
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1] mb-4 md:mb-6">
产品简介
</h2>
{/* 正文 */}
<p className="text-[#334155] text-sm sm:text-base md:text-lg lg:text-xl leading-[1.7] md:leading-[1.9] font-medium">
{product.brief}
</p>
{/* ═══ 底部标签 ═══ */}
{product.tags && (
<div className="flex flex-wrap items-center justify-center gap-3 mt-7 pt-7 border-t border-gray-100">
{product.tags.map((tag, i) => (
<span
key={i}
className="px-3 sm:px-5 py-1.5 sm:py-2 text-sm sm:text-base md:text-lg font-bold rounded-lg border transition-all duration-300 hover:-translate-y-1 cursor-default"
style={{
color: product.themeColor,
borderColor: `rgba(${r},${g},${b},0.25)`,
backgroundColor: `rgba(${r},${g},${b},0.04)`,
}}
>
{tag}
</span>
))}
</div>
)}
</div>
</div>
</ScrollReveal>
</div>
</section>
{/* ── Pain Points ── */}
{product.painPoints && product.painPoints.length > 0 && (
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-14 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">Pain Points</span>
</div>
<h2 className="text-2xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
{product.painPointsTitle || `${product.name}解决的核心问题`}
</h2>
<p className="text-[#475569] text-lg md:text-xl mt-6 max-w-xl leading-relaxed">深入理解管理难题提供经过实战验证的解决方案</p>
</div>
</ScrollReveal>
<div className="space-y-5">
<section className="py-24 md:py-32 px-6 bg-gradient-to-b from-white via-[#fafbfc] to-white overflow-hidden">
{/* ── 标题区 ── */}
<div className="text-center mb-16 md:mb-20">
<span className="inline-flex items-center gap-2 text-sm font-bold tracking-[0.2em] uppercase text-[var(--theme)] mb-4">
<span className="w-8 h-px bg-[var(--theme)]/40" />
Pain Points & Solutions
<span className="w-8 h-px bg-[var(--theme)]/40" />
</span>
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.3] md:leading-[1.8] mb-3 md:mb-4 whitespace-pre-line">
{product.painPointsTitle || `${product.name}\n解决的核心问题`}
</h2>
<p className="mt-3 md:mt-4 text-[#64748b] text-sm sm:text-base md:text-lg max-w-2xl mx-auto">
直击业务痛点提供经过实战验证的完整解决方案
</p>
</div>
<div className="max-w-7xl mx-auto">
{/* ── 响应式布局:移动端堆叠,md+ 左右对半 ── */}
<div className="flex flex-col gap-16 md:gap-24 lg:gap-32">
{product.painPoints.map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-white rounded-2xl p-10 border border-gray-100 hover:shadow-lg hover:shadow-gray-200/50 transition-all duration-300 group h-full">
<div className="absolute top-0 left-0 right-0 h-1 rounded-t-2 bg-[#043b8f] opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Pain point */}
<div className="flex items-start gap-3 mb-5 pb-5 border-b border-dashed border-gray-200">
<div className="shrink-0 w-9 h-9 rounded-full bg-red-50 flex items-center justify-center">
<AlertCircle className="w-5 h-5 text-red-500" />
</div>
<h3 className="text-lg font-bold text-[#0f172a] leading-relaxed">{item.title}</h3>
</div>
{/* Solution */}
<div className="ml-11 pl-5 border-l-2 border-[#043b8f] bg-[#043b8f]/[0.03] rounded-r-lg py-4 pr-4">
<span className="inline-block text-sm font-semibold text-white bg-[#043b8f] rounded px-3 py-1.5 mb-3">解决方案</span>
<p className="text-[#334155] text-xl leading-[1.9]">{item.desc}</p>
<div key={i} className={`flex flex-col md:flex-row items-center gap-8 md:gap-12 lg:gap-20 ${i % 2 === 1 ? 'md:flex-row-reverse' : ''}`}>
{/* ═══ 配图 ═══ */}
<div className="w-full md:w-1/2 flex justify-center">
<div className="relative w-full max-w-[320px] sm:max-w-[360px] md:w-4/5 lg:w-3/5">
{item.url ? (
item.url.endsWith('.svg') ? (
<ThemedSvg
url={item.url}
color={product.themeColor}
className="relative z-10 w-full mx-auto [&>svg]:w-full [&>svg]:h-auto"
/>
) : (
<img
src={item.url}
alt={item.title}
className="relative z-10 w-full mx-auto"
/>
)
) : (
<div className="relative z-10 w-full mx-auto aspect-[4/3] rounded-2xl bg-gray-100 flex items-center justify-center">
<span className="text-gray-400 text-sm">暂无配图</span>
</div>
)}
</div>
</div>
</ScrollReveal>
{/* ═══ 文字 ═══ */}
<div className="w-full md:w-1/2">
<div className="space-y-6 md:space-y-8 lg:space-y-10">
{/* ── 痛点书签卡 ── */}
<div className="relative">
<div className="relative bg-white rounded-2xl shadow-[0_6px_28px_-8px_rgba(239,68,68,0.15)] border border-red-200 overflow-visible">
<div className="absolute -top-4 sm:-top-5 md:-top-6 left-4 sm:left-6 md:left-8 px-3 sm:px-4 md:px-5 py-1 sm:py-1.5 md:py-2 rounded-t-lg md:rounded-t-xl bg-red-400 shadow-[0_-2px_8px_rgba(239,68,68,0.2)]">
<span className="text-xs sm:text-sm md:text-base font-black tracking-[0.1em] uppercase text-white">痛点</span>
</div>
<div className="absolute left-0 top-0 bottom-0 w-1.5 bg-red-400 rounded-l-2xl" />
<div className="pl-6 sm:pl-8 md:pl-10 pr-4 sm:pr-6 md:pr-8 py-4 sm:py-5 md:py-8 pt-8 sm:pt-10 md:pt-12">
<p className="text-[#1e293b] text-sm sm:text-base md:text-lg leading-[1.7] md:leading-[1.9] font-bold">
{item.title}
</p>
</div>
</div>
</div>
{/* ── 解决方案书签卡 ── */}
<div className="relative">
<div className="relative bg-white rounded-2xl shadow-[0_6px_28px_-8px_rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.15)] border overflow-visible"
style={{ borderColor: `${product.themeColor}30` }}>
<div className="absolute -top-4 sm:-top-5 md:-top-6 left-4 sm:left-6 md:left-8 px-3 sm:px-4 md:px-5 py-1 sm:py-1.5 md:py-2 rounded-t-lg md:rounded-t-xl shadow-[0_-2px_8px_rgba(0,0,0,0.1)]"
style={{ backgroundColor: product.themeColor }}>
<span className="text-xs sm:text-sm md:text-base font-black tracking-[0.1em] uppercase text-white">解决方案</span>
</div>
<div className="absolute left-0 top-0 bottom-0 w-1.5 rounded-l-2xl"
style={{ backgroundColor: product.themeColor }} />
<div className="pl-6 sm:pl-8 md:pl-10 pr-4 sm:pr-6 md:pr-8 py-4 sm:py-5 md:py-8 pt-8 sm:pt-10 md:pt-12">
<p className="text-[#1e293b] text-sm sm:text-base md:text-lg leading-[1.7] md:leading-[1.9] font-bold">
{item.desc}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
))}
</div>
</div>
@@ -140,23 +314,24 @@ export default function ProductPage() {
)}
{/* ── Core Features ── */}
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<section className="py-24 px-6 bg-white">
<div className="max-w-7xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">Core Features</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-[11px] font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">Core Features</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
</div>
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
核心功能
</h2>
{product.target && (
<div className="mt-5 flex flex-col items-center gap-3">
<span className="text-lg text-[#0f172a] font-bold">适用对象</span>
<div className="flex flex-wrap justify-center gap-3">
<div className="mt-6 flex flex-col items-center gap-4">
<span className="text-sm sm:text-base md:text-lg font-extrabold text-[#0f172a]">适用对象</span>
<div className="flex flex-wrap justify-center gap-2 md:gap-3">
{product.target.split('、').map((t, i) => (
<span key={i} className="px-4 py-1.5 rounded-lg text-sm font-bold text-[#043b8f] border border-[#043b8f]/30">{t.trim()}</span>
<span key={i} className="px-3 sm:px-5 py-1.5 sm:py-2 rounded-lg text-sm sm:text-base md:text-lg font-bold text-[var(--theme)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.3)] bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.03)]">{t.trim()}</span>
))}
</div>
</div>
@@ -164,22 +339,222 @@ export default function ProductPage() {
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* ── 桌面端:每个功能一行卡片(左文字 + 右截图) ── */}
<div className="hidden lg:flex flex-col gap-6">
{product.features.map((f, i) => (
<ScrollReveal key={i} delay={i * 0.05}>
<div className="relative bg-white rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[#043b8f]/5 transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)` }} />
<div className="absolute top-0 right-0 w-32 h-32 bg-[#043b8f]/[0.02] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-20 h-20 border border-[#043b8f]/[0.06] rounded-tr-3xl" />
<div className={`group relative flex rounded-2xl bg-white border border-gray-100 overflow-hidden transition-all duration-500 hover:border-[var(--theme)]/20 hover:shadow-[0_8px_32px_-8px_rgba(0,0,0,0.10)] shadow-[0_1px_8px_-2px_rgba(0,0,0,0.05)] ${i % 2 === 1 ? 'flex-row-reverse' : ''}`}>
{/* hover 边框光 */}
<div className="absolute inset-0 rounded-2xl ring-1 ring-inset ring-transparent group-hover:ring-[var(--theme)]/10 transition-all duration-700 pointer-events-none z-10" />
<div className="relative z-10">
<div className="flex items-center gap-3 mb-4">
<span className="flex items-center justify-center w-8 h-8 rounded-md bg-[#043b8f]/[0.06] text-xs font-black text-[#043b8f]">0{i + 1}</span>
<div className="h-px flex-1 bg-gradient-to-r from-[#043b8f]/10 to-transparent" />
{/* 文字区 */}
<div className={`relative w-1/2 flex flex-col justify-center p-12 xl:p-14 ${i % 2 === 1 ? 'pl-10 xl:pl-12' : 'pr-10 xl:pr-12'}`}>
{/* 序号 + 标签 */}
<div className="flex items-center gap-3 mb-5">
<span className="shrink-0 flex items-center justify-center w-10 h-10 rounded-xl text-white"
style={{ backgroundColor: product.themeColor, boxShadow: `0 4px 12px ${product.themeColor}30` }}>
{f.icon && iconMap[f.icon] ? (() => { const Icon = iconMap[f.icon]; return <Icon className="w-5 h-5" strokeWidth={2} /> })() : <span className="text-sm font-black">{String(i + 1).padStart(2, '0')}</span>}
</span>
<div>
<span className="text-[10px] font-bold tracking-[0.2em] uppercase text-[var(--theme)]/50">Core Feature</span>
</div>
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{f.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{f.desc}</p>
{/* 标题 */}
<h3 className="text-2xl lg:text-[1.65rem] font-extrabold text-[#0f172a] leading-[1.2] mb-4 tracking-tight">
{f.title}
</h3>
{/* 分隔线 */}
<div className="w-10 h-[3px] rounded-full mb-4" style={{ backgroundColor: product.themeColor, opacity: 0.25 }} />
{/* 描述 */}
<p className="text-[#475569] text-base leading-[1.8] max-w-md">
{f.desc}
</p>
</div>
{/* 截图区 */}
<div className="relative w-1/2 flex items-center justify-center p-6 lg:p-8 bg-[#f8fafc]">
{f.image ? (
<div className="relative w-full">
{f.small ? (
/* 手机模型外壳 */
<div className="relative mx-auto w-[140px]">
<div className="relative bg-[#1a1a2e] rounded-[18px] p-[3px] shadow-[0_12px_36px_-10px_rgba(0,0,0,0.25)] transition-all duration-500 group-hover:shadow-[0_18px_44px_-12px_rgba(0,0,0,0.30)] group-hover:-translate-y-1">
<div className="flex justify-center pt-0.5">
<div className="flex items-center gap-0.5">
<span className="w-1 h-1 rounded-full bg-[#0f0f1a]" />
<span className="w-4 h-0.5 bg-[#0f0f1a] rounded-full" />
</div>
</div>
<div className="mt-0.5 rounded-[6px] overflow-hidden bg-white">
<img src={f.image} alt={f.title} className="w-full h-auto object-contain max-h-[280px]" />
</div>
<div className="flex justify-center pt-1.5 pb-0.5">
<div className="w-5 h-[2px] bg-white/15 rounded-full" />
</div>
</div>
</div>
) : (
/* 浏览器窗口效果 */
<div className="rounded-lg overflow-hidden shadow-[0_6px_24px_-6px_rgba(0,0,0,0.10)] ring-1 ring-black/5 transition-all duration-500 group-hover:shadow-[0_10px_32px_-10px_rgba(0,0,0,0.15)] group-hover:-translate-y-1 w-4/5 mx-auto">
<div className="flex items-center gap-1 px-2.5 py-1.5 bg-[#1e293b]">
<span className="w-1.5 h-1.5 rounded-full bg-red-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-amber-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400/80" />
<span className="ml-2 text-[8px] text-white/30 font-mono truncate">{f.title}</span>
</div>
<img src={f.image} alt={f.title} className="w-full h-auto" />
</div>
)}
</div>
) : (
<div className="rounded-lg overflow-hidden ring-1 ring-gray-200 w-4/5 mx-auto">
<div className="flex items-center gap-0.5 px-2 py-1.5 bg-[#1e293b]">
<span className="w-1 h-1 rounded-full bg-red-400/60" />
<span className="w-1 h-1 rounded-full bg-amber-400/60" />
<span className="w-1 h-1 rounded-full bg-emerald-400/60" />
</div>
<div className="aspect-[4/3] bg-white border border-dashed border-gray-200 flex items-center justify-center">
<span className="text-gray-300 text-sm">暂无截图</span>
</div>
</div>
)}
</div>
</div>
</ScrollReveal>
))}
</div>
{/* ── 移动端:紧凑卡片 ── */}
<div className="lg:hidden flex flex-col gap-4">
{product.features.map((f, i) => (
<ScrollReveal key={i} delay={i * 0.05}>
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden shadow-[0_1px_8px_-2px_rgba(0,0,0,0.05)]">
<div className="p-5">
<div className="flex items-center gap-3 mb-3">
<span className="shrink-0 flex items-center justify-center w-8 h-8 rounded-lg text-white"
style={{ backgroundColor: product.themeColor }}>
{f.icon && iconMap[f.icon] ? (() => { const Icon = iconMap[f.icon]; return <Icon className="w-4 h-4" strokeWidth={2} /> })() : <span className="text-xs font-black">{String(i + 1).padStart(2, '0')}</span>}
</span>
<span className="text-[10px] font-bold tracking-[0.15em] uppercase text-[var(--theme)]/50">核心功能</span>
</div>
<h3 className="text-lg font-extrabold text-[#0f172a] mb-2 leading-snug">{f.title}</h3>
<p className="text-[#475569] text-sm leading-relaxed">{f.desc}</p>
</div>
{f.image && (
<div className="mx-3 mb-3 bg-[#f8fafc] rounded-xl p-3">
{f.small ? (
<div className="relative mx-auto w-[130px]">
<div className="relative bg-[#1a1a2e] rounded-[16px] p-[3px] shadow-[0_12px_36px_-10px_rgba(0,0,0,0.25)]">
<div className="flex justify-center pt-1">
<div className="flex items-center gap-1">
<span className="w-1 h-1 rounded-full bg-[#0f0f1a]" />
<span className="w-5 h-0.5 bg-[#0f0f1a] rounded-full" />
</div>
</div>
<div className="mt-0.5 rounded-[8px] overflow-hidden bg-white">
<img src={f.image} alt={f.title} className="w-full h-auto object-contain max-h-[240px]" />
</div>
<div className="flex justify-center pt-1 pb-0.5">
<div className="w-4 h-[2px] bg-white/15 rounded-full" />
</div>
</div>
</div>
) : (
<div className="rounded-lg overflow-hidden shadow-[0_4px_16px_-4px_rgba(0,0,0,0.08)] ring-1 ring-black/5 w-4/5 mx-auto">
<div className="flex items-center gap-0.5 px-2 py-1.5 bg-[#1e293b]">
<span className="w-1.5 h-1.5 rounded-full bg-red-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-amber-400/80" />
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400/80" />
<span className="ml-1.5 text-[7px] text-white/30 font-mono truncate">{f.title}</span>
</div>
<img src={f.image} alt={f.title} className="w-full h-auto" />
</div>
)}
</div>
)}
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ── Product Showcase ── */}
{product.screenshots && product.screenshots.length > 0 && (
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-lg bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Product Showcase</span>
</div>
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
产品展示
</h2>
</div>
</ScrollReveal>
<Carousel images={product.screenshots} themeColor={product.themeColor} />
</div>
</section>
)}
{/* ── 国产化支持 ── */}
<section className="py-24 px-6 bg-white">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-[11px] font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">Localization</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
</div>
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
国产化支持
</h2>
<p className="mt-3 md:mt-4 text-[#64748b] text-sm sm:text-base md:text-lg max-w-2xl mx-auto">
全面适配国产信创生态赋能教育信息化自主可控
</p>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[
{ icon: Cpu, title: '国产芯片兼容', desc: '全面适配鲲鹏、飞腾、龙芯等国产CPU架构,从底层保障系统安全可控' },
{ icon: Monitor, title: '国产操作系统', desc: '完美运行于统信UOS、麒麟等国产操作系统,桌面端与服务端全覆盖' },
{ icon: HardDrive, title: '国产数据库', desc: '支持达梦、人大金仓、GaussDB等国产数据库,数据存储自主可靠' },
].map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="group relative bg-white rounded-xl md:rounded-2xl p-6 sm:p-8 md:p-10 lg:p-12 h-full border border-gray-100 shadow-[0_2px_16px_-6px_rgba(0,0,0,0.06)] hover:shadow-[0_12px_40px_-12px_rgba(0,0,0,0.12)] transition-all duration-500 overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-1 transition-all duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)`, opacity: 0 }}
/>
<div className="absolute bottom-0 left-0 right-0 h-[3px] transition-all duration-500"
style={{
background: `linear-gradient(90deg, transparent, ${product.themeColor}60, transparent)`,
opacity: 0.5,
}}
/>
<div className="relative z-10 flex flex-col items-center text-center">
<div className="mb-5 md:mb-7">
<div className="w-16 h-16 sm:w-20 sm:h-20 md:w-[88px] md:h-[88px] rounded-2xl flex items-center justify-center transition-all duration-500 group-hover:scale-110 group-hover:shadow-lg"
style={{
background: `linear-gradient(135deg, ${product.themeColor}12, ${product.themeColor}08)`,
border: `1px solid ${product.themeColor}20`,
boxShadow: `0 4px 20px ${product.themeColor}15`,
}}
>
<item.icon className="w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 transition-all duration-500 group-hover:scale-110" strokeWidth={1.5} style={{ color: product.themeColor }} />
</div>
</div>
<h3 className="text-lg sm:text-xl md:text-2xl font-extrabold text-[#0f172a] mb-2 md:mb-3 tracking-tight">
{item.title}
</h3>
<div className="w-8 md:w-10 h-[3px] rounded-full mb-3 md:mb-4" style={{ backgroundColor: product.themeColor, opacity: 0.3 }} />
<p className="text-[#64748b] text-sm sm:text-base md:text-lg leading-relaxed">
{item.desc}
</p>
</div>
</div>
</ScrollReveal>
@@ -189,47 +564,116 @@ export default function ProductPage() {
</section>
{/* ── Advantages ── */}
<section className="py-24 px-6 bg-white">
<section className="py-24 md:py-32 px-6 bg-[#f8fafc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">Why Choose Us</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
<span className="text-[11px] font-bold tracking-[0.22em] uppercase text-[var(--theme)]/60">Why Choose Us</span>
<span className="w-8 h-[2px] rounded-full bg-[var(--theme)]/40" />
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
为什么选择我们
</h2>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
{[
{ icon: Shield, title: '安全可靠', desc: '等保三级认证,银行级加密存储,多机房容灾备份' },
{ icon: Clock, title: '快速部署', desc: 'SaaS 即开即用,私有化部署最快 3 天交付上线' },
{ icon: Headphones, title: '专属服务', desc: '7×24 小时响应,专属客户经理全程陪伴' },
].map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-[#fafbfc] rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[#043b8f]/5 transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)` }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[#043b8f]/[0.02] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[#043b8f]/[0.06] rounded-tr-3xl" />
<div className="group relative bg-white rounded-xl md:rounded-2xl p-6 sm:p-8 md:p-10 lg:p-12 h-full border border-gray-100 shadow-[0_2px_16px_-6px_rgba(0,0,0,0.06)] hover:shadow-[0_12px_40px_-12px_rgba(0,0,0,0.12)] transition-all duration-500 overflow-hidden cursor-pointer" onClick={() => handleStepClick(i)}>
{/* 顶部品牌色条 — hover 时滑入 */}
<div className="absolute top-0 left-0 right-0 h-1 transition-all duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)`, opacity: 0 }}
/>
{/* 底部品牌色条 — 始终可见 */}
<div className="absolute bottom-0 left-0 right-0 h-[3px] transition-all duration-500"
style={{
background: `linear-gradient(90deg, transparent, ${product.themeColor}60, transparent)`,
opacity: 0.5,
}}
/>
<div className="relative z-10">
<div className="flex items-center gap-3 mb-5">
<div className="w-11 h-11 rounded-xl flex items-center justify-center bg-[#043b8f]/[0.06] border border-[#043b8f]/10">
<item.icon className="w-5 h-5" style={{ color: '#043b8f' }} />
<div className="relative z-10 flex flex-col items-center text-center">
{/* 图标 — 大圆形容器 */}
<div className="mb-5 md:mb-7">
<div className="w-16 h-16 sm:w-20 sm:h-20 md:w-[88px] md:h-[88px] rounded-2xl flex items-center justify-center transition-all duration-500 group-hover:scale-110 group-hover:shadow-lg"
style={{
background: `linear-gradient(135deg, ${product.themeColor}12, ${product.themeColor}08)`,
border: `1px solid ${product.themeColor}20`,
boxShadow: `0 4px 20px ${product.themeColor}15`,
}}
>
<item.icon className="w-8 h-8 sm:w-9 sm:h-9 md:w-10 md:h-10 transition-all duration-500 group-hover:scale-110" strokeWidth={1.5} style={{ color: product.themeColor }} />
</div>
<div className="h-px flex-1 bg-gradient-to-r from-[#043b8f]/10 to-transparent" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{item.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{item.desc}</p>
{/* 标题 */}
<h3 className="text-lg sm:text-xl md:text-2xl font-extrabold text-[#0f172a] mb-2 md:mb-3 tracking-tight">
{item.title}
</h3>
{/* 分隔线 */}
<div className="w-8 md:w-10 h-[3px] rounded-full mb-3 md:mb-4" style={{ backgroundColor: product.themeColor, opacity: 0.3 }} />
{/* 描述 */}
<p className="text-[#64748b] text-sm sm:text-base md:text-lg leading-relaxed">
{item.desc}
</p>
</div>
</div>
</ScrollReveal>
))}
</div>
{/* ── 流动光速时间线 ── */}
<div className="relative max-w-md mx-auto mt-8 md:mt-24 mb-4">
{/* 光束外层光晕 */}
<div className="absolute top-1/2 -translate-y-1/2 left-[8%] right-[8%] h-4 rounded-full"
style={{
background: `linear-gradient(90deg, transparent, ${product.themeColor}15, ${product.themeColor}25, ${product.themeColor}15, transparent)`,
filter: 'blur(6px)',
}}
/>
{/* 光束中层 */}
<div className="absolute top-1/2 -translate-y-1/2 left-[8%] right-[8%] h-1.5 rounded-full"
style={{
background: `linear-gradient(90deg, transparent, ${product.themeColor}30, ${product.themeColor}60, ${product.themeColor}30, transparent)`,
filter: 'blur(3px)',
}}
/>
{/* 光束核心 */}
<div className="relative h-1 rounded-full"
style={{
background: `linear-gradient(90deg, transparent, ${product.themeColor}60, ${product.themeColor}, ${product.themeColor}60, transparent)`,
boxShadow: `0 0 12px ${product.themeColor}40, 0 0 24px ${product.themeColor}20`,
}}>
{/* 流动光点 */}
<div className="absolute top-1/2 -translate-y-1/2 w-3 h-3 rounded-full transition-all duration-700 ease-out"
style={{
left: `${15 + lightPos * 35}%`,
background: '#ffffff',
boxShadow: `0 0 12px ${product.themeColor}, 0 0 28px ${product.themeColor}, 0 0 56px ${product.themeColor}, 0 0 84px ${product.themeColor}`,
}}
/>
{/* 3 个节点标记 */}
{[0, 1, 2].map(n => (
<div key={n} className="absolute top-1/2 -translate-y-1/2 w-3 h-3 rounded-full border-2 transition-all duration-500"
style={{
left: `${15 + n * 35}%`,
borderColor: n <= activeStep ? product.themeColor : `${product.themeColor}40`,
background: n <= activeStep ? product.themeColor : '#ffffff',
boxShadow: n <= activeStep ? `0 0 10px ${product.themeColor}80, 0 0 20px ${product.themeColor}40` : `0 0 4px ${product.themeColor}20`,
}}
/>
))}
</div>
</div>
</div>
</section>
@@ -240,10 +684,10 @@ export default function ProductPage() {
<ScrollReveal className="mb-14 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[#043b8f]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[#043b8f]">FAQ</span>
<span className="w-5 h-[2px] rounded-lg bg-[var(--theme)]" />
<span className="text-sm font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">FAQ</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
<h2 className="text-3xl sm:text-4xl md:text-5xl lg:text-[5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
常见问题
</h2>
</div>
@@ -252,21 +696,21 @@ export default function ProductPage() {
<div className="space-y-4 max-w-3xl mx-auto">
{product.faq.map((item, i) => (
<ScrollReveal key={i} delay={i * 0.04}>
<details className="group relative bg-white rounded-2xl border border-gray-100 hover:shadow-lg hover:shadow-[#043b8f]/5 transition-all duration-300 overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-[#043b8f]/10 group-open:bg-[#043b8f] transition-colors duration-300" />
<div className="absolute top-0 right-0 w-24 h-24 bg-[#043b8f]/[0.02] rounded-bl-full" />
<summary className="relative flex items-center justify-between px-8 py-6 cursor-pointer list-none select-none">
<div className="flex items-center gap-4 pr-4">
<span className="flex items-center justify-center w-8 h-8 rounded-lg bg-[#043b8f]/[0.06] text-xs font-black text-[#043b8f] shrink-0">Q{i + 1}</span>
<h3 className="text-lg font-bold text-[#0f172a]">{item.q}</h3>
<details className="group relative bg-white rounded-2xl border border-gray-100 hover:shadow-lg hover:shadow-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.05)] transition-all duration-300 overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] group-open:bg-[var(--theme)] transition-colors duration-300" />
<div className="absolute top-0 right-0 w-24 h-24 bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-bl-full" />
<summary className="relative flex items-center justify-between px-4 sm:px-6 md:px-8 py-4 sm:py-5 md:py-6 cursor-pointer list-none select-none">
<div className="flex items-center gap-3 md:gap-4 pr-4">
<span className="flex items-center justify-center w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] text-xs font-black text-[var(--theme)] shrink-0">Q{i + 1}</span>
<h3 className="text-base sm:text-lg md:text-xl font-bold text-[#0f172a]">{item.q}</h3>
</div>
<div className="w-8 h-8 rounded-full bg-[#043b8f]/[0.06] border border-[#043b8f]/10 flex items-center justify-center shrink-0 group-open:rotate-180 transition-transform duration-300">
<ChevronDown className="w-4 h-4 text-[#043b8f]" />
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] flex items-center justify-center shrink-0 group-open:rotate-180 transition-transform duration-300">
<ChevronDown className="w-3.5 h-3.5 sm:w-4 sm:h-4 text-[var(--theme)]" />
</div>
</summary>
<div className="relative px-8 pb-7">
<div className="ml-12 pl-5 border-l-2 border-[#043b8f]/10 bg-[#043b8f]/[0.02] rounded-r-lg py-4 pr-4">
<p className="text-[#64748b] text-lg leading-relaxed">{item.a}</p>
<div className="relative px-4 sm:px-6 md:px-8 pb-5 sm:pb-6 md:pb-7">
<div className="ml-10 sm:ml-12 pl-4 sm:pl-5 border-l-2 border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-r-lg py-3 sm:py-4 pr-3 sm:pr-4">
<p className="text-[#64748b] text-sm sm:text-base md:text-lg lg:text-xl leading-relaxed">{item.a}</p>
</div>
</div>
</details>
@@ -279,10 +723,10 @@ export default function ProductPage() {
{/* ── CTA ── */}
<section className="py-40 px-4 relative overflow-hidden"
style={{ background: 'linear-gradient(135deg, #0a1628, #0f1f3a)' }}>
style={{ background: `linear-gradient(135deg, ${product.themeFrom || '#0a1628'}, ${product.themeTo || '#0f1f3a'})` }}>
<div className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: 'radial-gradient(#004ec4 1px, transparent 1px)',
backgroundImage: `radial-gradient(${product.themeColor} 1px, transparent 1px)`,
backgroundSize: '24px 24px',
}}
/>
@@ -310,8 +754,6 @@ export default function ProductPage() {
</ScrollReveal>
</div>
</section>
</main>
)
}
}
+16 -2
View File
@@ -3,6 +3,20 @@ import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
plugins: [
react(),
tailwindcss(),
{
name: 'txt-charset',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url.endsWith('.txt')) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
}
next()
})
},
},
],
base: '/',
})
})