首页已完成

This commit is contained in:
小潘
2026-05-28 09:01:30 +08:00
parent acaf262f3f
commit 0cf72dc47f
30 changed files with 7227 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
{
"permissions": {
"allow": [
"Bash(npm install *)",
"Bash(npx vite *)",
"Bash(npx tailwindcss *)",
"Bash(timeout 10)"
]
}
}
+2
View File
@@ -0,0 +1,2 @@
{
}
@@ -0,0 +1,163 @@
# 产品营销网站设计文档
## 概述
为广西职业院校提供教育信息化产品的营销推广网站。纯前端静态站点,部署到 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: '/'`
+16
View File
@@ -0,0 +1,16 @@
# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
+21
View File
@@ -0,0 +1,21 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
globals: globals.browser,
parserOptions: { ecmaFeatures: { jsx: true } },
},
},
])
+14
View File
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="zh-CN">
<head>
<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>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
+2864
View File
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
{
"name": "website",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"echarts": "^6.1.0",
"lucide-react": "^1.16.0",
"react": "^19.2.6",
"react-dom": "^19.2.6"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@tailwindcss/vite": "^4.3.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^10.3.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.6.0",
"react-router-dom": "^7.15.1",
"tailwindcss": "^4.3.0",
"vite": "^8.0.12"
}
}
+2049
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+31
View File
@@ -0,0 +1,31 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import Navbar from './components/Navbar'
import Footer from './components/Footer'
import Home from './pages/Home'
import ProductPage from './pages/ProductPage'
import Contact from './pages/Contact'
function ScrollToTop() {
const { pathname } = useLocation()
useEffect(() => {
window.scrollTo(0, 0)
}, [pathname])
return null
}
export default function App() {
return (
<BrowserRouter>
<ScrollToTop />
<Navbar />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/product/:slug" element={<ProductPage />} />
<Route path="/contact" element={<Contact />} />
</Routes>
<Footer />
</BrowserRouter>
)
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

+106
View File
@@ -0,0 +1,106 @@
import { useState } from 'react';
const productOptions = [
'校企益邦',
'商咖五级进阶',
'智慧党建平台',
'科研管理系统',
];
const initialFormState = {
name: '',
phone: '',
company: '',
product: '',
message: '',
};
export default function ContactForm() {
const [form, setForm] = useState(initialFormState);
function handleChange(e) {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
}
function handleSubmit(e) {
e.preventDefault();
alert(
'感谢您的咨询!\n\n' +
'姓名:' + form.name + '\n' +
'联系电话:' + form.phone + '\n' +
'单位名称:' + form.company + '\n' +
'关注产品:' + form.product + '\n' +
'留言内容:' + form.message + '\n\n' +
'我们将在1个工作日内与您联系!'
);
setForm(initialFormState);
}
const inputClass = 'w-full px-4 py-2.5 border border-gray-200 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-[#004ec4]/20 focus:border-[#004ec4]/40 transition-shadow placeholder-[#94a3b8] bg-white text-[#334155]';
return (
<section className="py-20 px-4 bg-white">
<div className="max-w-2xl mx-auto">
<div className="text-center mb-12">
<p className="text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Inquiry</p>
<h2 className="text-3xl md:text-4xl font-bold text-[#0f172a] mb-4">在线咨询</h2>
<p className="text-[#64748b] text-base">填写表单我们将在 1 个工作日内与您联系</p>
</div>
<div className="bg-[#f8fafc] border border-gray-100 rounded-2xl p-8">
<form onSubmit={handleSubmit} className="space-y-5">
<div>
<label htmlFor="name" className="block text-sm font-medium text-[#334155] mb-1.5">
姓名 <span className="text-red-400">*</span>
</label>
<input type="text" id="name" name="name" required value={form.name} onChange={handleChange}
placeholder="请输入您的姓名" className={inputClass} />
</div>
<div>
<label htmlFor="phone" className="block text-sm font-medium text-[#334155] mb-1.5">
联系电话 <span className="text-red-400">*</span>
</label>
<input type="tel" id="phone" name="phone" required value={form.phone} onChange={handleChange}
placeholder="请输入您的联系电话" className={inputClass} />
</div>
<div>
<label htmlFor="company" className="block text-sm font-medium text-[#334155] mb-1.5">单位名称</label>
<input type="text" id="company" name="company" value={form.company} onChange={handleChange}
placeholder="请输入您的单位名称" className={inputClass} />
</div>
<div>
<label htmlFor="product" className="block text-sm font-medium text-[#334155] mb-1.5">
关注产品 <span className="text-[#94a3b8] text-xs">(可选)</span>
</label>
<select id="product" name="product" value={form.product} onChange={handleChange}
className="w-full px-4 py-2.5 border border-gray-200 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-[#004ec4]/20 focus:border-[#004ec4]/40 transition-shadow bg-white text-[#64748b]">
<option value="">请选择关注的产品</option>
{productOptions.map((product) => (
<option key={product} value={product}>{product}</option>
))}
</select>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-[#334155] mb-1.5">留言内容</label>
<textarea id="message" name="message" rows={4} value={form.message} onChange={handleChange}
placeholder="请输入您的留言内容" className={inputClass + ' resize-none'} />
</div>
<button
type="submit"
className="w-full py-3 text-white font-semibold rounded-xl text-sm transition-all focus:outline-none focus:ring-2 focus:ring-[#004ec4]/20 focus:ring-offset-2 hover:shadow-[0_4px_15px_rgba(0,78,196,0.25)]"
style={{ background: 'linear-gradient(135deg, #0078b8, #004ec4)' }}
>
提交咨询
</button>
</form>
</div>
</div>
</section>
);
}
+81
View File
@@ -0,0 +1,81 @@
import { ExternalLink, Lock, QrCode } from 'lucide-react';
export default function DemoSection({ demo, themeColor }) {
if (!demo) return null;
const { url, account, password } = demo;
return (
<section className="py-20 px-4 bg-[#f8fafc]">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-[#0f172a] mb-3">在线演示体验</h2>
<p className="text-[#64748b] text-base">即刻体验产品功能感受智慧教育魅力</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-white border border-gray-100 rounded-2xl p-6 shadow-sm border-t-2 border-t-transparent"
style={{ borderTopColor: themeColor }}>
<div className="w-11 h-11 rounded-xl flex items-center justify-center mb-4"
style={{ backgroundColor: themeColor + '10' }}>
<ExternalLink className="w-5 h-5" style={{ color: themeColor }} />
</div>
<h3 className="text-base font-semibold text-[#0f172a] mb-3">演示地址</h3>
<a href={url} target="_blank" rel="noopener noreferrer"
className="text-sm break-all leading-relaxed transition-colors hover:underline"
style={{ color: themeColor }}>
{url}
</a>
<p className="text-[#94a3b8] text-xs mt-2">点击链接在新窗口打开演示</p>
</div>
<div className="bg-white border border-gray-100 rounded-2xl p-6 shadow-sm border-t-2 border-t-transparent"
style={{ borderTopColor: themeColor }}>
<div className="w-11 h-11 rounded-xl flex items-center justify-center mb-4"
style={{ backgroundColor: themeColor + '10' }}>
<QrCode className="w-5 h-5" style={{ color: themeColor }} />
</div>
<h3 className="text-base font-semibold text-[#0f172a] mb-3">扫码体验</h3>
<div className="flex justify-center">
<div className="border border-dashed border-gray-200 rounded-xl w-36 h-36 flex flex-col items-center justify-center gap-2">
<svg viewBox="0 0 100 100" width="56" height="56" className="opacity-20">
<rect x="5" y="5" width="25" height="25" rx="2" fill="currentColor" className="text-gray-400" />
<rect x="8" y="8" width="19" height="19" rx="1" fill="white" />
<rect x="11" y="11" width="13" height="13" rx="1" fill="currentColor" className="text-gray-400" />
<rect x="70" y="5" width="25" height="25" rx="2" fill="currentColor" className="text-gray-400" />
<rect x="73" y="8" width="19" height="19" rx="1" fill="white" />
<rect x="76" y="11" width="13" height="13" rx="1" fill="currentColor" className="text-gray-400" />
<rect x="5" y="70" width="25" height="25" rx="2" fill="currentColor" className="text-gray-400" />
<rect x="8" y="73" width="19" height="19" rx="1" fill="white" />
<rect x="11" y="76" width="13" height="13" rx="1" fill="currentColor" className="text-gray-400" />
</svg>
<span className="text-[#94a3b8] text-xs">二维码</span>
</div>
</div>
<p className="text-[#94a3b8] text-xs mt-3 text-center">请使用微信扫码体验</p>
</div>
<div className="bg-white border border-gray-100 rounded-2xl p-6 shadow-sm border-t-2 border-t-transparent"
style={{ borderTopColor: themeColor }}>
<div className="w-11 h-11 rounded-xl flex items-center justify-center mb-4"
style={{ backgroundColor: themeColor + '10' }}>
<Lock className="w-5 h-5" style={{ color: themeColor }} />
</div>
<h3 className="text-base font-semibold text-[#0f172a] mb-3">体验账号</h3>
<div className="space-y-3">
<div className="bg-[#f8fafc] rounded-xl p-3 border border-gray-100">
<span className="text-xs text-[#94a3b8] block mb-1">账号</span>
<span className="font-mono text-sm text-[#334155]">{account}</span>
</div>
<div className="bg-[#f8fafc] rounded-xl p-3 border border-gray-100">
<span className="text-xs text-[#94a3b8] block mb-1">密码</span>
<span className="font-mono text-sm text-[#334155]">{password}</span>
</div>
</div>
<p className="text-[#94a3b8] text-xs mt-3">使用以上账号密码登录体验</p>
</div>
</div>
</div>
</section>
);
}
+71
View File
@@ -0,0 +1,71 @@
import { Link } from 'react-router-dom';
import { products } from '../data/products';
export default function Footer() {
return (
<footer className="bg-[#0a1628] text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-14">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16">
{/* Left: Brand + desc */}
<div className="lg:col-span-5">
<div className="flex items-center gap-2 mb-4">
<img src="/favicon.ico" alt="讯驰" className="w-20 h-20" />
<h3 className="text-3xl font-bold text-white">讯驰科技</h3>
</div>
<p className="text-sm text-white mb-5 font-medium leading-7">智慧校园解决方案提供商</p>
<p className="text-sm text-white mb-4 flex items-start gap-1.5 leading-7">
<svg className="w-3.5 h-3.5 mt-[7px] shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
<span>南宁市总部路3号中国东盟科技企业孵化基地<br />二期9号厂房二楼212房</span>
</p>
</div>
{/* Right: Two columns */}
<div className="lg:col-span-7 grid grid-cols-1 sm:grid-cols-2 gap-10">
<div>
<h4 className="text-sm font-semibold text-white mb-6 tracking-wide">产品中心</h4>
<ul className="space-y-4">
{products.map((product) => (
<li key={product.slug}>
<Link
to={`/product/${product.slug}`}
className="text-sm text-white hover:text-white/80 transition-colors"
>
{product.name}
</Link>
</li>
))}
</ul>
</div>
<div>
<h4 className="text-sm font-semibold text-white mb-6 tracking-wide">联系与合作</h4>
<ul className="space-y-4 text-sm">
<li>
<Link to="/contact" className="inline-flex items-center gap-1.5 text-white hover:text-white/80 transition-colors">
在线咨询
<svg className="w-3.5 h-3.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>
</li>
<li>
<Link to="/contact" className="inline-flex items-center gap-1.5 text-white hover:text-white/80 transition-colors">
成为代理商
<svg className="w-3.5 h-3.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>
</li>
</ul>
</div>
</div>
</div>
</div>
{/* Bottom bar */}
<div className="border-t border-white/[0.06]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="text-center text-xs text-white">
<span>&copy; 2024 讯驰科技 版权所有 · 桂ICP备XXXXXXXX号</span>
</div>
</div>
</div>
</footer>
);
}
+249
View File
@@ -0,0 +1,249 @@
import { ArrowRight } from 'lucide-react';
import { useEffect, useRef } from 'react';
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;
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 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;
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);
}
init();
draw();
window.addEventListener('resize', () => { resize(); });
return () => cancelAnimationFrame(animId);
}, []);
return <canvas ref={canvasRef} className="absolute inset-0 w-full h-full z-[1]" />;
}
export default function HeroSection() {
return (
<section className="relative h-[100vh] flex items-center justify-center overflow-hidden bg-[#050d1a]">
{/* Background video */}
<video
autoPlay
loop
muted
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" />
</video>
{/* Cinematic overlay */}
<div className="absolute inset-0 z-0 bg-gradient-to-b from-[#050d1a]/70 via-[#0a1628]/50 to-[#050d1a]/80" />
<div className="absolute inset-0 z-0 bg-gradient-to-r from-[#050d1a]/40 via-transparent to-[#050d1a]/40" />
{/* Ambient orbs - very subtle */}
<div className="absolute inset-0 z-0">
<div className="absolute top-[15%] right-[20%] w-[500px] h-[500px] rounded-full bg-[#004ec4]/[0.06] blur-[180px]"
style={{ animation: 'pulse-slow 10s ease-in-out infinite' }} />
<div className="absolute bottom-[15%] left-[15%] w-[400px] h-[400px] rounded-full bg-[#f08519]/[0.04] blur-[140px]"
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" />
<div className="absolute bottom-0 left-0 right-0 z-10 h-px bg-gradient-to-r from-transparent via-white/[0.04] to-transparent" />
{/* Side vignette */}
<div className="absolute inset-0 z-[2] pointer-events-none"
style={{ boxShadow: 'inset 80px 0 120px -60px rgba(5,13,26,0.6), inset -80px 0 120px -60px rgba(5,13,26,0.6)' }} />
<style>{`
@keyframes fadeInUp {
0% { transform: translateY(50px); opacity: 0; filter: blur(12px); }
100% { transform: translateY(0); opacity: 1; filter: blur(0); }
}
@keyframes fadeIn {
0% { opacity: 0; filter: blur(6px); }
100% { opacity: 1; filter: blur(0); }
}
@keyframes pulse-slow {
0%, 100% { opacity: 0.5; }
50% { opacity: 0.8; }
}
@keyframes lineExpand {
0% { width: 0; opacity: 0; }
100% { width: 60px; opacity: 1; }
}
@keyframes scrollFlow {
0% { transform: translateY(-100%); opacity: 0; }
30% { opacity: 1; }
70% { opacity: 1; }
100% { transform: translateY(200%); opacity: 0; }
}
@keyframes gentleFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-4px); }
}
@keyframes titleShimmer {
0% { background-position: 200% center; }
100% { background-position: -200% center; }
}
.hero-main-title {
font-family: "ZCOOL XiaoWei", "STSong", "SimSun", serif;
letter-spacing: 0.6em;
color: white;
text-shadow: 0 4px 60px rgba(0, 162, 234, 0.3),
0 2px 4px rgba(0, 0, 0, 0.4);
}
.hero-sec-title {
font-family: "Ma Shan Zheng", "STXingkai", cursive;
letter-spacing: 0.5em;
color: white;
text-shadow: 0 2px 40px rgba(0, 162, 234, 0.25),
0 0 80px rgba(0, 162, 234, 0.15);
}
.hero-desc {
font-family: "Ma Shan Zheng", "STXingkai", cursive;
letter-spacing: 0.3em;
color: rgba(255, 255, 255, 1);
text-shadow: 0 2px 40px rgba(0, 162, 234, 0.25),
0 0 80px rgba(0, 162, 234, 0.15);
}
.cta-btn {
font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
letter-spacing: 0.08em;
background: transparent;
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.3);
border-radius: 6px;
color: white;
box-shadow: none;
transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.cta-btn:hover {
background: rgba(255,255,255,0.06);
border-color: rgba(255,255,255,0.5);
box-shadow: 0 0 30px rgba(255,255,255,0.05);
}
`}</style>
{/* Content */}
<div className="relative z-10 text-center px-4 sm:px-6 lg:px-8 max-w-5xl mx-auto">
{/* 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-7xl md:text-9xl lg:text-[110px] 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>
</h1>
{/* Divider */}
<div className="flex items-center justify-center mt-2 mb-2"
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>
</div>
{/* CTA */}
<div className="mt-12 flex justify-center"
style={{ animation: 'fadeInUp 1s cubic-bezier(0.16, 1, 0.3, 1) 1.3s both' }}>
<a href="#products"
className="cta-btn group relative inline-flex items-center gap-3 px-12 py-[15px] rounded-full text-white font-medium text-[14px]">
<span className="relative z-10">预约产品演示</span>
<ArrowRight className="w-[14px] h-[14px] relative z-10 transition-all duration-500 group-hover:translate-x-1.5 group-hover:opacity-80" />
</a>
</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>
);
}
+126
View File
@@ -0,0 +1,126 @@
import { useState, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Menu, X, Phone } from 'lucide-react';
import { products } from '../data/products';
export default function Navbar() {
const [scrolled, setScrolled] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const location = useLocation();
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > window.innerHeight * 0.7);
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
setMobileOpen(false);
}, [location.pathname]);
const navItems = [
{ to: '/', label: '首页' },
...products.map(p => ({ to: `/product/${p.slug}`, label: p.name })),
{ to: '/contact', label: '联系我们' },
];
return (
<>
<style>{`
.nav-active-tab {
margin-top: -60px;
padding-top: calc(2.5rem + 25px);
padding-bottom: 12px;
}
`}</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}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center h-[70px] overflow-visible">
{/* Left: Logo + Nav */}
<div className="flex items-center gap-2 shrink-0 overflow-visible">
<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-[10px] tracking-[0.2em] uppercase font-medium text-white/40 transition-colors duration-500`}>XUNCHI TECH</span>
</div>
</Link>
<div className="hidden lg:flex items-center gap-1 transition-colors duration-500 overflow-visible">
<div className="w-px h-6 bg-white mr-2" />
{navItems.map((item) => (
<Link
key={item.to}
to={item.to}
className={`relative px-5 font-medium rounded-sm transition-all duration-300 ${
location.pathname === item.to
? 'nav-active-tab text-white font-semibold bg-[#043b8f] text-[18px]'
: 'text-white hover:text-white hover:bg-white/10 text-[14px] py-3'
}`}
>
{item.label}
</Link>
))}
</div>
</div>
{/* Right */}
<div className="flex-1" />
<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-[13px] font-medium text-white hover:text-white transition-all duration-300"
>
<Phone className="w-3.5 h-3.5" />
0771-3864294
</a>
</div>
{/* Mobile toggle */}
<button
onClick={() => setMobileOpen(!mobileOpen)}
className="lg:hidden p-2 rounded-lg text-white hover:text-white hover:bg-white/10 transition-colors duration-300"
aria-label="Toggle menu"
>
{mobileOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
</div>
</div>
{/* Mobile menu */}
<div
className={`lg:hidden overflow-hidden transition-all duration-400 ease-in-out ${
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">
{navItems.map((item) => (
<Link
key={item.to}
to={item.to}
className={`block px-4 py-3 rounded-xl text-[14px] font-medium transition-colors ${
location.pathname === item.to
? 'text-white bg-white/[0.06]'
: 'text-white/50 hover:bg-white/[0.04]'
}`}
>
{item.label}
</Link>
))}
<div className="pt-3 mt-2 border-t border-white/[0.06]">
<a href="tel:07713864294" className="flex items-center gap-2 px-4 py-3 text-[#004ec4] text-[14px] font-medium">
<Phone className="w-4 h-4" />
0771-3864294
</a>
</div>
</div>
</div>
</nav>
</>
);
}
+91
View File
@@ -0,0 +1,91 @@
import { Link } from 'react-router-dom';
import { Calendar } from 'lucide-react';
const newsItems = [
{
id: 1,
date: '2026-05-20',
title: '讯驰智慧校园3.0版本正式发布,全面升级AI智能分析能力',
description:
'新版本引入AI大数据分析引擎,实现校园数据的智能洞察与预警,帮助学校管理者做出更科学的决策。',
},
{
id: 2,
date: '2026-05-10',
title: '与广西水利电力职业技术学院签署战略合作协议',
description:
'双方将在智慧校园建设、教学资源开发、教师信息化能力提升等领域展开深度合作,共同推进职业教育数字化转型。',
},
{
id: 3,
date: '2026-04-28',
title: '讯驰亮相2026中国(南宁)智慧教育装备展览会',
description:
'携智慧校园、实习实训、考试测评等全系列产品参展,吸引百余所职业院校前来交流洽谈。',
},
{
id: 4,
date: '2026-04-15',
title: '实习实训管理平台助力广西机电职业技术学院获自治区教学成果奖',
description:
'该院校依托讯驰实习实训管理平台,构建了"校企协同、过程可控"的实习管理体系,人才培养质量显著提升。',
},
];
function formatDate(dateStr) {
const [year, month, day] = dateStr.split('-');
return { year, month, day };
}
export default function NewsSection() {
return (
<section className="py-20 px-4 sm:px-6 lg:px-8 bg-white">
<div className="max-w-6xl mx-auto">
{/* Section title */}
<h2 className="text-3xl md:text-4xl font-bold text-center text-gray-900 mb-12">
新闻动态
</h2>
{/* News grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{newsItems.map((item) => {
const { year, month, day } = formatDate(item.date);
return (
<article
key={item.id}
className="bg-gray-50 rounded-2xl p-6 hover:shadow-lg transition-shadow border border-gray-100"
>
{/* Date badge */}
<div className="flex items-center mb-4">
<span className="inline-flex items-center bg-blue-600 text-white text-xs font-semibold px-3 py-1.5 rounded-full">
<Calendar className="w-3.5 h-3.5 mr-1.5" />
{year}{parseInt(month)}{parseInt(day)}
</span>
</div>
{/* Title */}
<h3 className="text-lg font-bold text-gray-900 mb-2 leading-snug">
{item.title}
</h3>
{/* Description */}
<p className="text-gray-600 text-sm leading-relaxed mb-4">
{item.description}
</p>
{/* Link */}
<Link
to={`/news/${item.id}`}
className="inline-flex items-center text-sm font-medium text-blue-600 hover:text-blue-700 transition-colors"
>
查看详情
<span className="ml-1">&rarr;</span>
</Link>
</article>
);
})}
</div>
</div>
</section>
);
}
+48
View File
@@ -0,0 +1,48 @@
import { Link } from 'react-router-dom';
export default function ProductCard({ product }) {
const Icon = product.icon;
return (
<div className="bg-white border border-gray-100 rounded-2xl p-6 flex flex-col hover:shadow-md hover:border-gray-200 transition-all duration-300 group h-full">
<div
className="w-11 h-11 rounded-xl flex items-center justify-center mb-4 transition-transform duration-300 group-hover:scale-105"
style={{ backgroundColor: product.themeColor + '10' }}
>
<Icon className="w-5 h-5" style={{ color: product.themeColor }} />
</div>
<h3 className="text-lg font-bold text-[#0f172a] mb-1">{product.name}</h3>
<p className="text-[#94a3b8] text-xs mb-3">{product.slogan}</p>
<p className="text-[#64748b] text-sm line-clamp-3 leading-relaxed mb-4">
{product.brief}
</p>
{product.tags && product.tags.length > 0 && (
<div className="flex flex-wrap gap-1.5 mb-4">
{product.tags.slice(0, 3).map((tag, i) => (
<span
key={i}
className="text-[11px] px-2 py-0.5 rounded bg-gray-50 text-[#64748b]"
>
{tag}
</span>
))}
</div>
)}
<div className="mt-auto pt-2">
<Link
to={`/product/${product.slug}`}
className="inline-flex items-center text-sm font-semibold transition-all group-hover:gap-2"
style={{ color: product.themeColor }}
>
了解更多
<span className="ml-1 transition-transform group-hover:translate-x-1">&rarr;</span>
</Link>
</div>
</div>
);
}
+153
View File
@@ -0,0 +1,153 @@
import { useRef, useEffect, useState } from 'react';
import * as echarts from 'echarts';
const chartData = [
{ value: 25, name: '校企益邦' },
{ value: 25, name: '科研管理系统' },
{ value: 25, name: '智慧党建平台' },
{ value: 25, name: '商咖五级进阶' },
];
const color = '#004ec4';
// ECharts startAngle=90, 各扇形视觉中心 (0°=12点, 顺时针)
// slice0: 1:30(45°) slice1: 4:30(135°) slice2: 7:30(225°) slice3: 10:30(315°)
// 要让选中扇形中心对准右侧90°(3点方向), CSS rotate需要的角度
const rotationNeeded = [45, -45, -135, -225];
function normalizeAngle(a) {
return ((a % 360) + 360) % 360;
}
function shortestRotation(from, to) {
const fromN = normalizeAngle(from);
const toN = normalizeAngle(to);
let diff = toN - fromN;
if (diff > 180) diff -= 360;
if (diff < -180) diff += 360;
return from + diff;
}
export default function RosePieChart({ onSelect, activeIndex }) {
const containerRef = useRef(null);
const chartRef = useRef(null);
const rotationRef = useRef(0);
const [rotation, setRotation] = useState(0);
// 初始化图表(只执行一次)
useEffect(() => {
if (!containerRef.current) return;
chartRef.current = echarts.init(containerRef.current, null, { renderer: 'canvas' });
chartRef.current.setOption({
tooltip: {
trigger: 'item',
backgroundColor: '#fff',
borderColor: '#e2e8f0',
borderWidth: 1,
textStyle: { color: '#334155', fontSize: 13 },
formatter: '{b}',
},
series: [{
type: 'pie',
radius: ['20%', '68%'],
center: ['50%', '50%'],
startAngle: 90,
padAngle: 2,
itemStyle: { borderRadius: 4, borderColor: '#fff', borderWidth: 3 },
label: {
show: true,
position: 'inside',
color: '#004ec4',
fontSize: 16,
fontWeight: 'bold',
rotate: 'tangential',
formatter: '{b}',
},
emphasis: {
scaleSize: 10,
itemStyle: {
color: '#0088cc',
shadowBlur: 30,
shadowColor: 'rgba(0,78,196,0.35)',
borderColor: '#fff',
borderWidth: 4,
},
label: { fontSize: 15, fontWeight: 'bold', color: '#0f172a' },
},
data: chartData.map(d => ({
...d,
itemStyle: { color, opacity: 1, borderRadius: 4, borderColor: '#fff', borderWidth: 3 },
})),
animationType: 'scale',
animationEasing: 'elasticOut',
animationDuration: 1000,
}],
});
const handleResize = () => chartRef.current?.resize();
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
chartRef.current?.dispose();
chartRef.current = null;
};
}, []);
// 更新颜色高亮 + CSS旋转
useEffect(() => {
const chart = chartRef.current;
if (!chart) return;
const isActive = activeIndex !== null && activeIndex !== undefined;
const target = isActive ? rotationNeeded[activeIndex] : 0;
const newAngle = shortestRotation(rotationRef.current, target);
rotationRef.current = newAngle;
setRotation(newAngle);
chart.setOption({
series: [{
data: chartData.map((d, i) => ({
...d,
label: { color: isActive && activeIndex === i ? '#fff' : '#004ec4' },
itemStyle: {
color: isActive && activeIndex !== i ? '#dfedf8' : color,
opacity: 1,
borderRadius: 4,
borderColor: '#fff',
borderWidth: 3,
},
})),
}],
});
}, [activeIndex]);
// 点击事件
useEffect(() => {
const chart = chartRef.current;
if (!chart) return;
const handler = (params) => {
if (onSelect && params.dataIndex !== undefined) {
onSelect(params.dataIndex);
}
};
chart.on('click', handler);
return () => chart.off('click', handler);
}, [onSelect]);
return (
<div
className="inline-block w-full"
style={{
transform: `rotate(${rotation}deg)`,
transition: 'transform 1s cubic-bezier(0.4, 0, 0.2, 1)',
}}
>
<div ref={containerRef} className="w-full h-[420px] md:h-[480px]" />
</div>
);
}
+69
View File
@@ -0,0 +1,69 @@
import { useEffect, useRef, useState } from 'react';
export default function ScrollReveal({
children,
className = '',
style = {},
direction = 'up',
distance = 50,
duration = 0.7,
delay = 0,
once = true,
threshold = 0.15,
as = 'div',
}) {
const ref = useRef(null);
const [visible, setVisible] = useState(false);
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' }
);
observer.observe(el);
return () => observer.disconnect();
}, [once, threshold]);
const transforms = {
up: `translateY(${distance}px)`,
down: `translateY(-${distance}px)`,
left: `translateX(${distance}px)`,
right: `translateX(-${distance}px)`,
scale: `scale(0.92)`,
};
const hiddenStyle = {
opacity: 0,
transform: transforms[direction] || transforms.up,
transition: `opacity ${duration}s cubic-bezier(0.22,1,0.36,1) ${delay}s, transform ${duration}s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
};
const visibleStyle = {
opacity: 1,
transform: 'translateY(0) translateX(0) scale(1)',
transition: `opacity ${duration}s cubic-bezier(0.22,1,0.36,1) ${delay}s, transform ${duration}s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
};
const Tag = as;
return (
<Tag
ref={ref}
className={className}
style={{ ...style, ...(visible ? visibleStyle : hiddenStyle) }}
>
{children}
</Tag>
);
}
+88
View File
@@ -0,0 +1,88 @@
import { Quote, Building2 } from 'lucide-react';
const stats = [
{ number: '100+', label: '合作院校' },
{ number: '50万+', label: '服务师生' },
{ number: '99.9%', label: '系统稳定率' },
];
const schools = [
'广西职业技术学院',
'南宁职业技术学院',
'柳州职业技术学院',
'广西机电职业技术学院',
'广西建设职业技术学院',
'广西水利电力职业技术学院',
];
export default function TrustSection() {
return (
<section className="py-20 px-4 sm:px-6 lg:px-8" style={{ backgroundColor: '#f8fafc' }}>
<div className="max-w-6xl mx-auto">
{/* Section title */}
<h2 className="text-3xl md:text-4xl font-bold text-center text-gray-900 mb-12">
深受广西职业院校信赖
</h2>
{/* Stats cards */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6 mb-16">
{stats.map((stat, index) => (
<div
key={index}
className="bg-white rounded-2xl p-8 text-center shadow-sm border border-gray-100"
>
<div className="text-4xl md:text-5xl font-bold text-blue-600 mb-2">
{stat.number}
</div>
<div className="text-gray-600 text-base">
{stat.label}
</div>
</div>
))}
</div>
{/* School logo placeholders */}
<h3 className="text-lg font-semibold text-center text-gray-700 mb-8">
合作院校
</h3>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4 mb-16">
{schools.map((school, index) => (
<div
key={index}
className="bg-gray-100 rounded-xl h-24 flex items-center justify-center p-3 border border-gray-200 hover:border-blue-300 hover:shadow-md transition-all"
>
<div className="flex items-center justify-center text-center">
<Building2 className="w-6 h-6 text-gray-400 shrink-0 mr-2" />
<span className="text-xs text-gray-500 font-medium leading-tight">
{school}
</span>
</div>
</div>
))}
</div>
{/* Customer testimonial */}
<div className="bg-white rounded-2xl shadow-md p-8 md:p-10 max-w-3xl mx-auto border border-gray-100">
<Quote className="w-10 h-10 text-blue-200 mb-4" />
<blockquote className="text-gray-700 text-base md:text-lg leading-relaxed mb-6">
"讯驰智慧校园管理平台极大提升了我校的管理效率,从教务排课到后勤报修,
全部实现线上化操作。系统运行稳定,服务响应及时,是我们信赖的长期合作伙伴。"
</blockquote>
<div className="flex items-center">
<div className="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center mr-4">
<span className="text-blue-600 font-bold text-sm"></span>
</div>
<div>
<div className="font-semibold text-gray-900">
韦明远
</div>
<div className="text-sm text-gray-500">
广西职业技术学院 信息化建设办公室主任
</div>
</div>
</div>
</div>
</div>
</section>
);
}
+154
View File
@@ -0,0 +1,154 @@
import {
Building2, GraduationCap, BookOpen, ClipboardCheck
} from 'lucide-react';
export const products = [
{
slug: 'smart-campus',
name: '校企益邦',
slogan: '实习过程全掌控,就业数据一目了然',
themeColor: '#2563EB',
themeFrom: '#1e40af',
themeTo: '#3b82f6',
icon: Building2,
brief: '面向职业院校实习就业管理的专业化平台,覆盖实习计划制定、过程跟踪、数据采集、鉴定评价全流程,已服务广西多所职业院校,帮助学校轻松应对教育厅数据上报要求。',
tags: ['📋 实习计划管理', '📍 GPS签到+电子围栏', '📝 问卷式日志', '📊 多维鉴定评价'],
stats: [
{ num: '20+所', label: '覆盖学校' },
{ num: '50,000+', label: '服务学生' },
{ num: '100万+', label: '累计签到' },
],
features: [
{ emoji: '📋', title: '实习计划管理', desc: '按学期、专业、班级灵活制定实习计划,绑定不同实习规则,实现多批次差异化管理' },
{ emoji: '📍', title: 'GPS签到+电子围栏', desc: '基于地理位置的真实考勤,自动判断签到有效性,未签到学生一键导出名单' },
{ emoji: '📝', title: '问卷式日志', desc: '预设定向提问模板,引导学生深度反思,"流水账"变"成长档案"' },
{ emoji: '📊', title: '多维鉴定评价', desc: '综合考勤、日志、教师评价、企业反馈,自动生成实习鉴定分数,客观公正' },
{ emoji: '🔄', title: '一键同步教育厅平台', desc: '数据审核后直接同步至"广西职业院校学生实习管理平台",无缝对接省级管理要求' },
{ emoji: '📱', title: '微信小程序加持', desc: '学生通过小程序签到、写日志、上传附件,无需下载额外App,使用门槛极低' },
],
target: '职业院校教务处、实习管理科、各院系指导教师',
faq: [
{ q: '支持哪些部署方式?', a: '支持云端SaaS部署和本地私有化部署两种方式。SaaS部署无需购买服务器,开通即用;本地部署可保障数据完全自主可控,适合对数据安全要求较高的院校。' },
{ q: '能否与学校现有系统对接?', a: '支持。我们提供标准API接口,可与学校现有的教务系统、学工系统、一卡通系统等进行数据对接,实现统一身份认证和数据共享。' },
{ q: '售后服务如何保障?', a: '我们提供完整的培训服务:管理员培训、教师培训、学生培训。售后阶段配备专属客户经理,7×24小时响应,定期回访优化。' },
{ q: '数据安全怎么保证?', a: '系统通过等保三级认证,采用银行级数据加密存储,每日自动备份,多机房容灾。所有数据存储在国内合规云服务器,确保数据安全合规。' },
],
demo: {
url: 'https://demo.xunchi.com/smart-campus',
account: 'demo',
password: 'demo123',
},
},
{
slug: 'exam-system',
name: '科研管理系统',
slogan: '让科研管理告别纸质时代',
themeColor: '#EA580C',
themeFrom: '#c2410c',
themeTo: '#f97316',
icon: ClipboardCheck,
brief: '面向高校及科研院所的全生命周期科研业务管理平台。覆盖项目申报、评审立项、经费管控、成果登记到数据统计的全流程数字化,已帮助多所高校实现科研管理从纸质流转到"一网通办"的跨越。',
tags: ['📝 在线申报审批', '🔍 智能查重防重复', '💰 经费精细管控', '🏆 成果自动赋分'],
stats: [
{ num: '10+所', label: '服务高校' },
{ num: '3,000+', label: '管理项目' },
{ num: '8,000+', label: '登记成果' },
],
features: [
{ emoji: '📝', title: '在线申报审批', desc: '项目申报、审核、立项全流程在线办理,智能校验引擎自动检查相似度和限项规则' },
{ emoji: '🔍', title: '智能查重防重复', desc: '项目名称相似度超80%自动拦截,同一主持人月度限申报1次,杜绝重复申报' },
{ emoji: '💰', title: '经费精细管控', desc: '纵向/横向分类管理,预算—报销实时关联,超预算自动预警,经费使用透明合规' },
{ emoji: '🏆', title: '成果自动赋分', desc: '论文、专利、著作按级别自动量化评分,一次登记多处复用,告别重复填报' },
{ emoji: '⏰', title: '关键节点预警', desc: '经费超期、结题到期、审批退回等场景自动推送提醒,确保每个环节不遗漏' },
{ emoji: '📄', title: '年度报表一键导出', desc: '科研成果统计、项目经费汇总等年度报表自动生成,效率提升数十倍' },
],
target: '高校科研处、财务处、各院系教师及科研项目负责人',
faq: [
{ q: '支持哪些部署方式?', a: '支持云端SaaS部署和本地私有化部署两种方式。SaaS部署无需购买服务器,开通即用;本地部署可保障数据完全自主可控,适合对数据安全要求较高的院校。' },
{ q: '能否与学校现有系统对接?', a: '支持。我们提供标准API接口,可与学校现有的教务系统、学工系统、一卡通系统等进行数据对接,实现统一身份认证和数据共享。' },
{ q: '售后服务如何保障?', a: '我们提供完整的培训服务:管理员培训、教师培训、学生培训。售后阶段配备专属客户经理,7×24小时响应,定期回访优化。' },
{ q: '数据安全怎么保证?', a: '系统通过等保三级认证,采用银行级数据加密存储,每日自动备份,多机房容灾。所有数据存储在国内合规云服务器,确保数据安全合规。' },
],
demo: {
url: 'https://demo.xunchi.com/exam-system',
account: 'demo',
password: 'demo123',
},
},
{
slug: 'teaching-resource',
name: '智慧党建平台',
slogan: '党建工作有痕,年终考核无忧',
themeColor: '#7C3AED',
themeFrom: '#6d28d9',
themeTo: '#8b5cf6',
icon: BookOpen,
brief: '面向高校及职业院校各级党组织的智慧党建管理系统。以"党业融合、指标驱动、全程留痕"为核心理念,将三会一课、党员活动、工作记录等日常工作自动关联考核指标,实现年终考核"零重复填报"。',
tags: ['📊 三级考核指标', '📋 三会一课数字化', '✍️ 万能工作记录', '🔔 实时进度看板'],
stats: [
{ num: '150+', label: '服务支部' },
{ num: '5,000+', label: '管理党员' },
{ num: '200+', label: '考核指标' },
],
features: [
{ emoji: '📊', title: '三级考核指标', desc: '灵活构建校—院—支部三级指标树,分值自动汇总,考核部门与执行部门逐一对应' },
{ emoji: '📋', title: '三会一课数字化', desc: '通知—签到—记录全流程闭环,二维码签到5分钟刷新,杜绝"代签""补签"' },
{ emoji: '✍️', title: '万能工作记录', desc: '不受模块限制,任何业务工作均可灵活录入并关联考核指标,实现考核全覆盖' },
{ emoji: '🔔', title: '实时进度看板', desc: '领导层穿透查看各项指标完成进度,哪项落后一目了然,变"年底算账"为"过程管控"' },
{ emoji: '👥', title: '发展党员管理', desc: '内置入党全流程节点模型,支持自定义配置,关键节点到期自动提醒支部管理员' },
{ emoji: '📡', title: '党建数据中心', desc: '全校党建数据可视化大屏——组织健康度、党员活跃度、重点工作督办一览无余' },
],
target: '高校党委组织部、各二级党委/党总支、基层党支部',
faq: [
{ q: '支持哪些部署方式?', a: '支持云端SaaS部署和本地私有化部署两种方式。SaaS部署无需购买服务器,开通即用;本地部署可保障数据完全自主可控,适合对数据安全要求较高的院校。' },
{ q: '能否与学校现有系统对接?', a: '支持。我们提供标准API接口,可与学校现有的教务系统、学工系统、一卡通系统等进行数据对接,实现统一身份认证和数据共享。' },
{ q: '售后服务如何保障?', a: '我们提供完整的培训服务:管理员培训、教师培训、学生培训。售后阶段配备专属客户经理,7×24小时响应,定期回访优化。' },
{ q: '数据安全怎么保证?', a: '系统通过等保三级认证,采用银行级数据加密存储,每日自动备份,多机房容灾。所有数据存储在国内合规云服务器,确保数据安全合规。' },
],
demo: {
url: 'https://demo.xunchi.com/teaching-resource',
account: 'demo',
password: 'demo123',
},
},
{
slug: 'internship',
name: '商咖五级进阶',
slogan: '五级渐进,让每一次成长都看得见',
themeColor: '#059669',
themeFrom: '#047857',
themeTo: '#10b981',
icon: GraduationCap,
brief: '专为职业院校商科专业群打造的实训项目管理系统。以"项目—模块—任务"三级结构为骨架,以"咖豆积分+技能画像"为引擎,将抽象的五级进阶培养模式变成可量化、可追踪、可激励的线上实战平台。',
tags: ['📦 三级精细管理', '🫘 咖豆积分引擎', '⭐ 五级自动晋级', '🎯 动态技能画像'],
stats: [
{ num: '500+', label: '实训项目库' },
{ num: '15+', label: '覆盖专业' },
{ num: '200万+', label: '累计咖豆' },
],
features: [
{ emoji: '📦', title: '三级精细管理', desc: '项目→模块→任务三级结构,权重灵活分配,实训教学内容标准化、可复用' },
{ emoji: '🫘', title: '咖豆积分引擎', desc: '成绩自动转换为虚拟积分,支持积分排行、兑换、晋级,让学习像游戏一样上瘾' },
{ emoji: '⭐', title: '五级自动晋级', desc: '自定义等级名称、门槛积分和专属权益,达标自动晋升,成长路径一目了然' },
{ emoji: '🎯', title: '动态技能画像', desc: '基于任务完成情况自动生成个人雷达图,技能点逐一点亮,优势短板清晰可见' },
{ emoji: '📈', title: '人才培养驾驶舱', desc: '全校实训数据一屏呈现——项目热度、参与度、完成率、经费使用,决策有据可依' },
{ emoji: '🎁', title: '积分商城', desc: '咖豆可兑换实物或虚拟礼品,构建"学、练、评、换"完整激励闭环' },
],
target: '商科专业群、实训中心、教务处',
faq: [
{ q: '支持哪些部署方式?', a: '支持云端SaaS部署和本地私有化部署两种方式。SaaS部署无需购买服务器,开通即用;本地部署可保障数据完全自主可控,适合对数据安全要求较高的院校。' },
{ q: '能否与学校现有系统对接?', a: '支持。我们提供标准API接口,可与学校现有的教务系统、学工系统、一卡通系统等进行数据对接,实现统一身份认证和数据共享。' },
{ q: '售后服务如何保障?', a: '我们提供完整的培训服务:管理员培训、教师培训、学生培训。售后阶段配备专属客户经理,7×24小时响应,定期回访优化。' },
{ q: '数据安全怎么保证?', a: '系统通过等保三级认证,采用银行级数据加密存储,每日自动备份,多机房容灾。所有数据存储在国内合规云服务器,确保数据安全合规。' },
],
demo: {
url: 'https://demo.xunchi.com/internship',
account: 'demo',
password: 'demo123',
},
},
];
export function getProduct(slug) {
return products.find(p => p.slug === slug);
}
+53
View File
@@ -0,0 +1,53 @@
@import "tailwindcss";
@theme {
--font-sans: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
}
@utility line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
@utility line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-sans);
background: #ffffff;
color: #0f172a;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
::selection {
background: rgba(0, 162, 234, 0.15);
color: #0f172a;
}
::-webkit-scrollbar {
width: 5px;
}
::-webkit-scrollbar-track {
background: #f1f5f9;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
+119
View File
@@ -0,0 +1,119 @@
import { Phone, MapPin, Clock, Package, Users, TrendingUp, ShieldCheck, ArrowRight } from 'lucide-react'
import ContactForm from '../components/ContactForm'
import ScrollReveal from '../components/ScrollReveal'
import { Link } from 'react-router-dom'
const agentBenefits = [
{ icon: Package, title: '成熟产品体系', desc: '四大产品线覆盖职业院校核心需求,经多所院校实际使用验证,功能稳定可靠。' },
{ icon: TrendingUp, title: '广阔市场空间', desc: '广西区内 80+ 所职业院校,信息化建设正加速,合作前景可观。' },
{ icon: ShieldCheck, title: '全方位赋能支持', desc: '产品培训、售前演示、方案撰写、招投标支撑,一站式协助代理商展业。' },
{ icon: Users, title: '丰厚利润回报', desc: '合理代理价格 + 完善区域保护,保障代理商长期稳定收益。' },
]
const requirements = [
'具有广西区内教育行业资源或政府关系',
'具备法人资格或合法经营资质',
'认可教育信息化行业,愿与讯驰长期共同发展',
'具备基本的技术支持和客户服务能力',
]
export default function Contact() {
return (
<main>
{/* Hero (深色) */}
<div className="relative -mt-[70px]">
<section className="py-20 text-white relative overflow-hidden"
style={{ background: 'linear-gradient(135deg, #0a1628, #0f1f3a)' }}>
<div className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: 'radial-gradient(#004ec4 1px, transparent 1px)',
backgroundSize: '30px 30px',
}}
/>
<div className="relative max-w-7xl mx-auto px-4 text-center">
<p className="text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">合作洽谈</p>
<h1 className="text-4xl md:text-5xl font-bold mb-4">携手合作共建智慧校园</h1>
<p className="text-[#94a3b8] text-lg max-w-2xl mx-auto">
诚邀广西各界教育伙伴共同为职业院校提供优质的数字化解决方案
</p>
</div>
</section>
</div>
{/* Contact info (白底) */}
<section className="py-20 px-4 bg-white">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-12">
<p className="text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Contact</p>
<h2 className="text-3xl md:text-4xl font-bold text-[#0f172a] mb-4">联系方式</h2>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-20">
{[
{ icon: MapPin, title: '公司地址', text: '南宁市总部路3号中国东盟科技企业孵化基地二期9号厂房二楼212房' },
{ icon: Phone, title: '联系电话', text: '0771-3864294' },
{ icon: Clock, title: '工作时间', text: '周一至周五 9:00 - 18:00' },
].map((item, i) => {
const Icon = item.icon
return (
<ScrollReveal key={i} delay={i * 0.1}>
<div className="text-center p-8 bg-[#f8fafc] rounded-2xl">
<div className="w-12 h-12 bg-[#004ec4]/[0.08] rounded-xl flex items-center justify-center mx-auto mb-4">
<Icon className="w-6 h-6 text-[#004ec4]" />
</div>
<h3 className="font-bold text-[#0f172a] mb-2">{item.title}</h3>
<p className="text-[#64748b] text-sm">{item.text}</p>
</div>
</ScrollReveal>
)
})}
</div>
</div>
</section>
{/* Agent benefits (白底) */}
<section className="py-20 px-4 bg-[#f8fafc]">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-12">
<p className="text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Partnership</p>
<h2 className="text-3xl md:text-4xl font-bold text-[#0f172a] mb-4">成为代理商</h2>
<p className="text-[#64748b] text-base max-w-2xl mx-auto">
诚邀广西各地区具有教育行业资源的合作伙伴共享职业院校信息化发展机遇
</p>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-14">
{agentBenefits.map((item, i) => {
const Icon = item.icon
return (
<ScrollReveal key={i} delay={i * 0.1}>
<div className="bg-white rounded-2xl p-6 shadow-sm border border-gray-100 h-full">
<div className="w-10 h-10 rounded-xl bg-[#004ec4]/[0.08] flex items-center justify-center mb-4">
<Icon className="w-5 h-5 text-[#004ec4]" />
</div>
<h3 className="text-base font-bold text-[#0f172a] mb-2">{item.title}</h3>
<p className="text-[#64748b] text-sm leading-relaxed">{item.desc}</p>
</div>
</ScrollReveal>
)
})}
</div>
<ScrollReveal>
<div className="max-w-2xl mx-auto">
<h3 className="text-lg font-bold text-[#0f172a] text-center mb-6">合作基本要求</h3>
<div className="space-y-3">
{requirements.map((req, i) => (
<div key={i} className="flex items-center gap-3 bg-white rounded-xl px-5 py-4 border border-gray-100">
<span className="w-7 h-7 rounded-full bg-[#004ec4]/[0.08] flex items-center justify-center text-[#004ec4] text-sm font-bold shrink-0">{i + 1}</span>
<span className="text-[#475569] text-sm">{req}</span>
</div>
))}
</div>
</div>
</ScrollReveal>
</div>
</section>
<ContactForm />
</main>
)
}
+440
View File
@@ -0,0 +1,440 @@
import { Link } from 'react-router-dom';
import HeroSection from '../components/HeroSection'
import ProductCard from '../components/ProductCard'
import ScrollReveal from '../components/ScrollReveal'
import { products } from '../data/products'
import RosePieChart from '../components/RosePieChart'
import hunBg from '../assets/images/hun.jpg'
import {
MapPin, CheckCircle2, HeadphonesIcon, ShieldCheck,
ArrowRight, Play, Quote, Award, Users, Building2, BookOpen, Briefcase
} from 'lucide-react'
/* ─── Section 3: 数据亮点 ─── */
const stats = [
{ num: '40', suffix: '+', label: '软件著作权' },
{ num: '5', suffix: '项', label: 'ISO体系认证' },
{ num: '15', suffix: '年', label: '行业深耕经验' },
{ num: '94', suffix: '+', label: '合作院校覆盖' },
]
/* ─── Section 4: 开放能力 ─── */
const capabilities = [
{
title: '校企益邦', sub: '实习过程全掌控,就业数据一目了然',
desc: '面向职业院校实习就业管理的专业化平台,覆盖实习计划制定、过程跟踪、数据采集、鉴定评价全流程,已服务广西多所职业院校,帮助学校轻松应对教育厅数据上报要求。',
tip: "实习计划管理、GPS签到+电子围栏、问卷式日志、多维鉴定评价、一键同步教育厅平台、微信小程序加持"
},
{
title: '科研项目管理', sub: '让科研管理告别纸质时代',
desc: '面向高校及科研院所的全生命周期科研业务管理平台。覆盖项目申报、评审立项、经费管控、成果登记到数据统计的全流程数字化,已帮助多所高校实现科研管理从纸质流转到"一网通办"的跨越。',
tip: "在线申报审批、智能查重防重复、经费精细管控、成果自动赋分、关键节点预警、年度报表一键导出"
},
{
title: '智慧党建平台', sub: '党建工作有痕,年终考核无忧',
desc: '面向高校及职业院校各级党组织的智慧党建管理系统。以"党业融合、指标驱动、全程留痕"为核心理念,将三会一课、党员活动、工作记录等日常工作自动关联考核指标,实现年终考核"零重复填报"。',
tip: "三级考核指标、三会一课数字化、万能工作记录、实时进度看板、发展党员管理、党建数据中心"
},
{
title: '实训教学管理', sub: '五级渐进,让每一次成长都看得见',
desc: '专为职业院校商科专业群打造的实训项目管理系统。以"项目—模块—任务"三级结构为骨架,以"咖豆积分+技能画像"为引擎,将抽象的五级进阶培养模式变成可量化、可追踪、可激励的线上实战平台。',
tip: "三级精细管理、咖豆积分引擎、五级自动晋级、动态技能画像、人才培养驾驶舱、积分商城"
},
]
/* ─── 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 信息技术服务管理体系',
},
]
/* ─── Section 8: 荣誉展示 ─── */
const honors = [
{ icon: Award, title: '高新技术企业', desc: '国家级高新技术企业认定,技术创新能力获官方认可' },
{ icon: ShieldCheck, title: 'AAA级信用企业', desc: '企业信用等级AAA级,商业信誉优良' },
{ icon: BookOpen, title: 'ISO五项认证', desc: '质量、环境、安全、信息、服务五大国际体系认证' },
{ icon: CheckCircle2, title: '软件著作权40+', desc: '拥有40余项软件著作权,自主知识产权体系完善' },
{ icon: Briefcase, title: '等保三级备案', desc: '信息系统安全等级保护三级,数据安全保障有力' },
{ icon: HeadphonesIcon, title: '全周期服务', desc: '需求调研到运维保障,提供一站式本地化服务' },
]
const certifications = [
'ISO 9001 质量管理体系',
'ISO 14001 环境管理体系',
'ISO 45001 职业健康安全管理体系',
'ISO 27001 信息安全管理体系',
'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)
return (
<main>
{/* ─── 2. 首屏 Banner(深色) ─── */}
<div className="relative -mt-[70px]">
<HeroSection />
</div>
{/* ─── 3. 数据亮点区(大卡片,浮于 Hero 底部 1/3) ─── */}
<section id="data" className="relative z-20 px-4 -mt-[116px] pb-16">
<div className="max-w-5xl mx-auto">
<ScrollReveal>
<div className="bg-white rounded-xl shadow-xl shadow-black/[0.08] border border-gray-100 py-10 md:py-14 px-8 md:px-10">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-10">
{stats.map((s, i) => (
<div key={i} className="text-center">
<div className="text-4xl md:text-5xl font-black text-[#004ec4] tabular-nums">
<StatNumber target={s.num} suffix={s.suffix} />
</div>
<div className="mt-3 text-sm text-[#64748b] font-medium">{s.label}</div>
</div>
))}
</div>
</div>
</ScrollReveal>
</div>
</section>
{/* ─── 4. 开放能力区(白底) ─── */}
<section id="products" className="py-20 px-4 bg-white">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-14">
<p className="text-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Core Capabilities</p>
<h2 className="text-5xl md:text-6xl font-bold text-[#0f172a] mb-6">四大产品体系</h2>
<p className="text-[#64748b] text-base mx-auto whitespace-nowrap mb-4">全面覆盖职业院校教学管理党建科研四大核心场景为数字化校园建设提供一站式解决方案</p>
</ScrollReveal>
{/* Tab bar */}
<ScrollReveal>
<div className="flex justify-center gap-1 mb-12 flex-wrap">
{products.map((p, i) => (
<button
key={p.slug}
onClick={() => setActiveCapability(i)}
className={`flex items-center gap-2 px-5 py-2.5 rounded-lg text-[13px] font-medium transition-all border ${activeCapability === i
? 'text-[#004ec4] border-[#004ec4]'
: 'text-[#475569] border-gray-200 hover:text-[#004ec4] hover:border-[#004ec4]'
}`}
>
<p.icon className="w-4 h-4" style={{ color: activeCapability === i ? '#004ec4' : 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>
{/* 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-300 cursor-pointer group overflow-hidden ${activeCapability === idx
? 'bg-white border border-[#004ec4]/15 shadow-[0_4px_24px_rgba(0,78,196,0.08)]'
: 'hover:bg-gray-50/80 border border-gray-100'
}`} onClick={() => setActiveCapability(idx)}>
{activeCapability === idx && (
<div className="absolute top-0 left-0 right-0 h-[3px] bg-gradient-to-r from-[#004ec4] via-[#004ec4]/80 to-[#004ec4]/40" />
)}
<h3 className={`text-xl font-bold transition-colors duration-300 ${activeCapability === idx ? 'text-[#004ec4]' : 'text-[#0f172a]'
}`}>{c.title}</h3>
<p className={`text-[15px] mt-1 font-medium transition-colors duration-300 ${activeCapability === idx ? 'text-[#004ec4]/60' : 'text-[#94a3b8]'
}`}>{c.sub}</p>
<p className="text-[15px] text-[#334155] leading-[2] mt-4">{c.desc}</p>
{c.tip && (
<div className="mt-4 flex flex-wrap gap-2.5">
{c.tip.split('、').map((t, ti) => (
<span key={ti} className={`inline-block px-4 py-2 text-[13px] rounded-lg transition-colors duration-200 ${
activeCapability === idx
? 'bg-[#004ec4]/[0.06] text-[#004ec4] border border-[#004ec4]/12'
: 'bg-gray-50 text-[#94a3b8] border border-gray-100'
}`}>{t}</span>
))}
</div>
)}
{activeCapability === idx && (
<div className="mt-5">
<button onClick={() => setActiveCapability(null)}
className="text-sm text-[#004ec4]/60 hover:text-[#004ec4] 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>
</button>
</div>
)}
</div>
)
})}
</div>
</div>
</ScrollReveal>
{/* Bottom button */}
<ScrollReveal className="text-center">
<Link
to="/contact"
className="inline-flex items-center gap-2 px-7 py-2.5 rounded-lg text-sm font-medium text-[#475569] border border-gray-200 hover:border-[#004ec4] hover:text-[#004ec4] transition-all"
>
了解更多能力
<ArrowRight className="w-4 h-4" />
</Link>
</ScrollReveal>
</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` }}>
<div className="relative max-w-5xl mx-auto text-center">
<ScrollReveal>
<p className="text-xs text-white/80 tracking-[0.3em] uppercase mb-4 font-semibold">Why Choose Us</p>
<h2 className="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-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>
</ScrollReveal>
{/* Surrounding tags */}
<ScrollReveal>
<div className="relative w-[320px] h-[320px] md:w-[420px] md:h-[420px] mx-auto mb-16">
{/* Outer rings */}
<div className="absolute inset-0 rounded-full border border-white/[0.08]" />
<div className="absolute inset-8 rounded-full border border-white/[0.06]" />
<div className="absolute inset-16 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 = 68
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-6 py-3.5 text-base 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-2 h-2 rounded-full bg-white/80 ml-2 align-middle" /></>
: <><span className="inline-block w-2 h-2 rounded-full bg-white/80 mr-2 align-middle" />{tag.text}</>
}
</div>
</div>
)
})}
</div>
</ScrollReveal>
{/* Bottom button */}
<ScrollReveal>
<Link
to="/contact"
className="inline-flex items-center gap-2 px-8 py-3 rounded-full 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-xs text-[#004ec4] tracking-[0.25em] uppercase mb-3 font-semibold">Recognition</p>
<h2 className="text-5xl md:text-6xl font-bold text-[#0f172a] mb-4">资质与认证</h2>
<p className="text-[#64748b] 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
return (
<ScrollReveal key={i} delay={i * 0.1}>
<div className="group bg-white rounded-2xl p-8 border border-gray-100 text-center h-full flex flex-col items-center transition-all duration-300 hover:shadow-xl hover:shadow-blue-100/50 hover:-translate-y-1">
{/* 图标 */}
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#004ec4]/10 to-[#004ec4]/5 flex items-center justify-center mb-5 group-hover:from-[#004ec4]/20 group-hover:to-[#004ec4]/10 transition-all duration-300">
<Icon className="w-8 h-8 text-[#004ec4]" />
</div>
{/* 数字 */}
<div className="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-lg font-bold text-[#0f172a] mb-3">{card.title}</h3>
{/* 分隔线 */}
<div className="w-8 h-0.5 bg-gradient-to-r from-transparent via-[#004ec4]/30 to-transparent mb-3" />
{/* 描述 */}
<p className="text-sm text-[#64748b] leading-relaxed">{card.desc}</p>
</div>
</ScrollReveal>
)
})}
</div>
</div>
</section>
{/* ─── 9. 合作引导区(深色) ─── */}
<section className="py-40 px-4 relative overflow-hidden"
style={{ background: 'linear-gradient(135deg, #0a1628, #0f1f3a)' }}>
{/* Texture */}
<div className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: 'radial-gradient(#004ec4 1px, transparent 1px)',
backgroundSize: '24px 24px',
}}
/>
<div className="relative max-w-3xl mx-auto text-center">
<ScrollReveal>
<h2 className="text-5xl md:text-6xl font-bold text-white mb-8">携手讯驰共建智慧校园</h2>
<p className="text-white text-lg mb-14 max-w-xl mx-auto leading-relaxed">
获取产品演示与个性化方案迈出数字化转型第一步
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link
to="/contact"
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"
>
联系我们
<ArrowRight className="w-4 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"
>
致电 0771-3864294
</a>
</div>
</ScrollReveal>
</div>
</section>
</main>
)
}
+159
View File
@@ -0,0 +1,159 @@
import { useParams, Link } from 'react-router-dom'
import { ChevronDown, ArrowRight } from 'lucide-react'
import { getProduct, products } from '../data/products'
import DemoSection from '../components/DemoSection'
import ProductCard from '../components/ProductCard'
import ScrollReveal from '../components/ScrollReveal'
export default function ProductPage() {
const { slug } = useParams()
const product = getProduct(slug)
if (!product) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="text-center">
<h1 className="text-4xl font-bold text-[#0f172a] mb-4">产品未找到</h1>
<p className="text-[#94a3b8] mb-8">请检查链接是否正确</p>
<Link to="/" className="text-[#004ec4] hover:underline">返回首页</Link>
</div>
</div>
)
}
const Icon = product.icon
const otherProducts = products.filter(p => p.slug !== slug)
return (
<main>
{/* Hero Header (深色) */}
<div className="relative -mt-[70px]">
<section className="relative py-24 md:py-32 text-white overflow-hidden"
style={{ background: 'linear-gradient(135deg, #0a1628, #0f1f3a)' }}>
{/* Tech texture */}
<div className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: 'radial-gradient(#004ec4 1px, transparent 1px)',
backgroundSize: '30px 30px',
}}
/>
<div className="relative max-w-7xl mx-auto px-4 pt-16 text-center">
<div className="w-16 h-16 rounded-2xl flex items-center justify-center mx-auto mb-6"
style={{ backgroundColor: product.themeColor + '20', border: `1px solid ${product.themeColor}30` }}>
<Icon className="w-8 h-8" style={{ color: product.themeColor }} />
</div>
<h1 className="text-4xl md:text-5xl font-bold mb-3">{product.name}</h1>
<p className="text-lg text-[#94a3b8] mb-2">{product.slogan}</p>
<p className="text-base text-[#64748b] max-w-2xl mx-auto">{product.brief}</p>
{product.stats && (
<div className="flex flex-wrap justify-center gap-8 mt-10">
{product.stats.map((s, i) => (
<div key={i} className="text-center">
<div className="text-3xl md:text-4xl font-black" style={{ color: product.themeColor }}>{s.num}</div>
<div className="text-sm text-[#94a3b8] mt-1">{s.label}</div>
</div>
))}
</div>
)}
</div>
</section>
</div>
{/* Core Features (白底) */}
<section className="py-20 px-4 bg-white">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-14">
<h2 className="text-3xl md:text-4xl font-bold text-[#0f172a] mb-4">核心功能</h2>
<p className="text-[#64748b] text-base">全面覆盖业务场景灵活适配不同规模院校的管理需求</p>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{product.features.map((f, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="bg-[#f8fafc] rounded-2xl p-6 hover:shadow-sm transition-all duration-300 h-full">
<div className="text-3xl mb-4">{f.emoji}</div>
<h3 className="text-lg font-bold text-[#0f172a] mb-2">{f.title}</h3>
<p className="text-[#64748b] text-sm leading-relaxed">{f.desc}</p>
</div>
</ScrollReveal>
))}
</div>
{product.target && (
<ScrollReveal>
<p className="text-center text-[#94a3b8] text-sm mt-8">适用对象{product.target}</p>
</ScrollReveal>
)}
</div>
</section>
<DemoSection demo={product.demo} themeColor={product.themeColor} />
{/* FAQ (白底) */}
{product.faq && product.faq.length > 0 && (
<section className="py-20 px-4 bg-white">
<div className="max-w-3xl mx-auto">
<ScrollReveal className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold text-[#0f172a] mb-4">常见问题</h2>
</ScrollReveal>
<div className="space-y-3">
{product.faq.map((item, i) => (
<ScrollReveal key={i} delay={i * 0.06}>
<details className="group bg-[#f8fafc] rounded-xl hover:bg-gray-50 transition-colors">
<summary className="flex items-center justify-between px-6 py-5 cursor-pointer list-none">
<h3 className="text-[15px] font-semibold text-[#334155]">{item.q}</h3>
<ChevronDown className="w-5 h-5 text-[#94a3b8] group-open:rotate-180 transition-transform shrink-0 ml-4" />
</summary>
<div className="px-6 pb-5">
<p className="text-[#64748b] text-sm leading-relaxed">{item.a}</p>
</div>
</details>
</ScrollReveal>
))}
</div>
</div>
</section>
)}
{/* CTA (深色) */}
<section className="py-20 px-4 relative overflow-hidden"
style={{ background: 'linear-gradient(135deg, #0a1628, #0f1f3a)' }}>
<div className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: 'radial-gradient(#004ec4 1px, transparent 1px)',
backgroundSize: '24px 24px',
}}
/>
<div className="relative max-w-3xl mx-auto text-center">
<ScrollReveal>
<h2 className="text-3xl md:text-4xl font-bold text-white mb-5">{product.name}感兴趣</h2>
<p className="text-[#94a3b8] text-base mb-8">立即联系我们获取专属解决方案和详细报价</p>
<Link
to="/contact"
className="inline-flex items-center gap-2 px-8 py-3 rounded-xl text-sm font-semibold text-white border border-white/20 hover:bg-white/10 transition-all"
>
立即联系我们
<ArrowRight className="w-4 h-4" />
</Link>
</ScrollReveal>
</div>
</section>
{/* Other Products (白底) */}
<section className="py-20 px-4 bg-white">
<div className="max-w-6xl mx-auto">
<ScrollReveal className="text-center mb-14">
<h2 className="text-3xl md:text-4xl font-bold text-[#0f172a] mb-4">您可能还感兴趣</h2>
<p className="text-[#64748b] text-base">探索更多智慧教育产品</p>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{otherProducts.slice(0, 3).map((p, i) => (
<ScrollReveal key={p.slug} delay={i * 0.1}>
<ProductCard product={p} />
</ScrollReveal>
))}
</div>
</div>
</section>
</main>
)
}
+8
View File
@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
base: '/',
})