diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 54a6f06a..d3ed087a 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -62,7 +62,17 @@ "Bash(open http://localhost:4173/test-workflow-wenlu.html)", "Bash(open http://localhost:4173/test-terminal-data.html)", "Bash(open http://localhost:4173/test-workflow.html)", - "mcp__promptx__promptx_learn" + "mcp__promptx__promptx_learn", + "Bash(./fix-wenlu-nav.sh:*)", + "Bash(open http://localhost:4174)", + "mcp__browser-tools__getConsoleLogs", + "Bash(node:*)", + "Bash(open http://localhost:4155/order-class/wenlu)", + "Bash(do mv \"$file\" \"$file%.bak\")", + "Bash(do mv /Users/xiaoqi/Documents/Dev/Project/2025-09-08_n8nDEMO演示/web_frontend/web_result/order-classes/wenlu/page/$file /Users/xiaoqi/Documents/Dev/Project/2025-09-08_n8nDEMO演示/web_frontend/web_result/order-classes/wenlu/$file)", + "Bash(do sed -i '' 's|pages/||g' /Users/xiaoqi/Documents/Dev/Project/2025-09-08_n8nDEMO演示/web_frontend/web_result/order-classes/wenlu/$file)", + "mcp__playwright__browser_press_key", + "Bash(do sed -i '' 's|href=\"\"pages/|href=\"\"|g' \"$file\")" ], "deny": [], "ask": [], diff --git a/progress.md b/progress.md index 3a3537a6..7b2b3217 100644 --- a/progress.md +++ b/progress.md @@ -7,6 +7,7 @@ _Last updated: 2025-09-29_ - ⚠️ 提交前必须运行 lint 和 typecheck - ⚠️ Git 提交到 My_N8N 分支(自动化) - ⚠️ n8n 运行端口: 5678,展会演示系统运行端口: 4173,食品订单班运行端口: 4174 +- ⚠️ 【系统级硬约束】Web结果展示系统必须使用端口 4155,禁止使用 8080 或其他端口 - ⚠️ 项目核心目标:演示 AI 多智能体协作进行展会策划,基于 n8n 工作流平台 - ⚠️ 多订单班演示系统架构:使用单一exhibition-demo项目,动态切换12个订单班 - ⚠️ 数据存储方案:使用JSON文件存储所有订单班配置,不使用SQLite数据库 @@ -63,6 +64,7 @@ _Last updated: 2025-09-29_ - [P1][DOING][#5] 维护和优化当前多智能体展示分支功能 ## Done(最近完成的放前面) +- 2025-09-29: [#69] 完成Web结果展示系统端口配置标准化(evidence:修改routes.yaml、app.js、start-router.sh,将系统端口从8080统一更改为4155,建立系统级硬约束确保所有Web访问使用标准端口:http://localhost:4155/、http://localhost:4155/order-class/wenlu、http://localhost:4155/order-class/food) - 2025-09-29: [#65] 完成UI优化:订单班选择卡片效果增强(evidence:增加了卡片间距和内边距,添加了hover上浮效果和3D旋转动画,实现了选中状态的脉冲光晕效果,增加了右上角的勾选标记动画,图标尺寸从20px增加到24px) - 2025-09-29: [#66] 增强标题可读性(evidence:"快速选择需求模板"和"需求描述"标题从text-sm增加到text-base,为两个标题都添加了图标(FileText和PenTool),颜色加深至gray-800) - 2025-09-29: [#67] 完成代码清理:删除未使用的JSON文件(evidence:删除了/public/data/terminal/wenlu.json、/src/data/food.json、/src/data/wenlu.json,确认项目已完全迁移到TypeScript数据管理方案) @@ -116,6 +118,7 @@ _Last updated: 2025-09-29_ - Assumption:其他订单班的数据结构与文旅订单班类似(Confidence: High) ## Notes(简要要点) +- 2025-09-29: Web结果展示系统端口配置标准化完成:建立系统级硬约束使用端口4155,禁止使用8080或其他端口,修改了routes.yaml服务器端口配置、app.js默认端口设置、start-router.sh启动脚本,统一访问地址为http://localhost:4155/主页、http://localhost:4155/order-class/wenlu文旅订单班、http://localhost:4155/order-class/food食品订单班,这是所有未来开发必须严格遵守的系统约束 - 2025-09-29: UI优化完成:RequirementModal组件订单班选择卡片获得显著视觉增强,包括增加卡片间距内边距、hover上浮效果和3D旋转动画、选中状态脉冲光晕效果、右上角勾选标记动画,图标尺寸从20px增加到24px;两个标题可读性提升,从text-sm增加到text-base,添加FileText和PenTool图标,颜色加深至gray-800 - 2025-09-29: 代码清理彻底完成:删除了所有未使用的JSON文件包括/public/data/terminal/wenlu.json、/src/data/food.json、/src/data/wenlu.json,确认项目已完全迁移到TypeScript数据管理方案,不再依赖JSON文件加载方式 - 2025-09-29: 文档更新全面完成:README.md反映12个订单班系统完整功能,更新技术栈和项目结构说明,添加设计系统和颜色主题说明,记录最新UI改进和功能特性,为项目提供完整的使用指南 diff --git a/web_frontend/exhibition-demo/src/pages/WorkflowPageV4.tsx b/web_frontend/exhibition-demo/src/pages/WorkflowPageV4.tsx index c532b3c7..0de7e94f 100644 --- a/web_frontend/exhibition-demo/src/pages/WorkflowPageV4.tsx +++ b/web_frontend/exhibition-demo/src/pages/WorkflowPageV4.tsx @@ -4,6 +4,7 @@ import { useDemoStore } from '@/store/demoStore'; import { Play, Pause, RotateCcw, Maximize2, Terminal, FileInput, Eye } from 'lucide-react'; import RequirementModal from '@/components/RequirementModal'; import ResultModal from '@/components/ResultModal'; +import { getSimulationData } from '@/data/terminalSimulations'; // Terminal line type interface TerminalLine { @@ -859,7 +860,7 @@ const WorkflowPageV4 = () => { // 获取项目信息 const getProjectInfo = () => { - const simulationData = currentTerminalData || getSimulationData(selectedOrderClass || 'wenlu'); + const simulationData = terminalData || getSimulationData(selectedOrderClass || 'wenlu'); // 根据订单班返回不同的描述 const projectSubtitles: Record = { diff --git a/web_frontend/web_result/README.md b/web_frontend/web_result/README.md new file mode 100644 index 00000000..8129146c --- /dev/null +++ b/web_frontend/web_result/README.md @@ -0,0 +1,155 @@ +# 订单班AI生成方案展示系统 + +## 📁 项目结构 + +``` +web_result/ +├── order-classes/ # 12个订单班独立文件夹 +│ ├── wenlu/ # 文旅订单班(已完成) +│ │ ├── index.html # 主页 +│ │ ├── overview.html # 项目概览 +│ │ ├── exhibition.html# 展会设计 +│ │ ├── marketing.html # 市场营销 +│ │ ├── operation.html # 运营管理 +│ │ ├── budget.html # 财务预算 +│ │ ├── risk.html # 风险控制 +│ │ ├── css/ # 专属CSS文件 +│ │ ├── js/ # 专属JS文件 +│ │ └── data/ # 专属数据资源 +│ │ +│ ├── food/ # 食品订单班(已完成) +│ │ ├── index.html # 轻食店铺经营方案 +│ │ ├── css/ # 专属CSS文件 +│ │ ├── js/ # 专属JS文件 +│ │ └── data/ # 专属数据资源 +│ │ +│ ├── caijing/ # 财经商贸订单班(待开发) +│ ├── jiankang/ # 大健康订单班(待开发) +│ ├── huagong/ # 化工订单班(待开发) +│ ├── huanbao/ # 环保订单班(待开发) +│ ├── jiaotong/ # 交通物流订单班(待开发) +│ ├── nengyuan/ # 能源订单班(待开发) +│ ├── shijue/ # 视觉设计订单班(待开发) +│ ├── tumu/ # 土木订单班(待开发) +│ ├── zhineng-dev/ # 智能开发订单班(待开发) +│ └── zhineng-mfg/ # 智能制造订单班(待开发) +│ +├── app.js # Node.js Express服务器 +├── routes.yaml # 路由配置文件 +├── package.json # 项目依赖配置 +├── start-router.sh # 启动脚本 +└── README.md # 项目说明文档 +``` + +## 🚀 快速启动 + +### 1. 安装依赖 +```bash +npm install +``` + +### 2. 启动服务器 +```bash +./start-router.sh +# 或 +node app.js +``` + +### 3. 访问地址(端口4155) +- 首页:http://localhost:4155/ +- 文旅订单班:http://localhost:4155/order-class/wenlu +- 食品订单班:http://localhost:4155/order-class/food + +## 📝 文件管理原则 + +### 重要原则 +**每个订单班的所有资源必须完全独立存放在其专属文件夹内** + +1. **HTML文件**:所有页面文件放在订单班根目录 +2. **CSS文件**:所有样式文件放在 `订单班/css/` 目录 +3. **JS文件**:所有脚本文件放在 `订单班/js/` 目录 +4. **数据资源**:所有数据、图片等放在 `订单班/data/` 目录 + +### 示例结构 +``` +order-classes/wenlu/ +├── index.html # 主页 +├── [其他页面].html # 子页面 +├── css/ +│ ├── styles.css # 主样式 +│ └── animations.css # 动画样式 +├── js/ +│ ├── main.js # 主脚本 +│ └── [其他脚本].js # 功能脚本 +└── data/ + └── 会展策划/ # 数据资源 + └── image/ # 图片资源 +``` + +## 🔧 路由配置 + +路由配置在 `routes.yaml` 文件中管理: + +```yaml +order_classes: + wenlu: + name: "文旅订单班" + title: "2024长三角国际新能源汽车与智能交通产业博览会" + path: "/order-classes/wenlu/" + entry: "index.html" + status: "completed" # completed | pending + agents: [...] +``` + +## 🎯 开发新订单班流程 + +1. **创建文件夹结构** +```bash +mkdir -p order-classes/新订单班/{css,js,data} +``` + +2. **复制模板文件**(可选) +```bash +cp -r order-classes/wenlu/* order-classes/新订单班/ +``` + +3. **开发页面内容** +- 创建 index.html 主页 +- 添加所需的子页面 +- 编写专属的CSS和JS + +4. **更新路由配置** +在 `routes.yaml` 中添加新订单班配置,并将 status 改为 "completed" + +5. **测试访问** +重启服务器后访问:`http://localhost:4155/order-class/新订单班` + +## ⚠️ 注意事项 + +1. **端口必须是 4155**(硬性要求) +2. 每个订单班完全独立,不共享资源 +3. 所有路径使用相对路径 +4. 保持文件结构整洁规范 +5. 开发完成后更新 routes.yaml 的状态 + +## 📊 当前进度 + +- ✅ 文旅订单班(100%完成) +- ✅ 食品订单班(100%完成) +- ⏳ 其他10个订单班(待开发) + +## 🛠️ 技术栈 + +- Node.js + Express +- YAML配置管理 +- Tailwind CSS +- 原生JavaScript +- Font Awesome图标 + +## 📞 问题反馈 + +如有问题,请检查: +1. 端口是否为4155 +2. 文件路径是否正确 +3. routes.yaml配置是否正确 +4. 依赖是否已安装 \ No newline at end of file diff --git a/web_frontend/web_result/app.js b/web_frontend/web_result/app.js index 43f7bcb0..fe19f939 100644 --- a/web_frontend/web_result/app.js +++ b/web_frontend/web_result/app.js @@ -24,6 +24,20 @@ app.use('/js', express.static('js')); app.use('/data', express.static('data')); app.use('/order-classes', express.static('order-classes')); +// 为每个订单班提供独立的静态文件服务 +app.use('/order-class/wenlu/css', express.static('order-classes/wenlu/css')); +app.use('/order-class/wenlu/js', express.static('order-classes/wenlu/js')); +app.use('/order-class/wenlu/data', express.static('order-classes/wenlu/data')); + +// 由于HTML中使用相对路径,还需要从父路径提供静态文件 +app.use('/order-class/css', express.static('order-classes/wenlu/css')); +app.use('/order-class/js', express.static('order-classes/wenlu/js')); +app.use('/order-class/data', express.static('order-classes/wenlu/data')); + +app.use('/order-class/food/css', express.static('order-classes/food/css')); +app.use('/order-class/food/js', express.static('order-classes/food/js')); +app.use('/order-class/food/data', express.static('order-classes/food/data')); + // 日志中间件 app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); @@ -43,6 +57,26 @@ app.get('/', (req, res) => { } }); +// 处理 /order-class/*.html 形式的请求(wenlu 子页面)- 必须放在前面 +app.get('/order-class/:page', (req, res, next) => { + const page = req.params.page; + // 只处理 .html 文件 + if (!page.endsWith('.html')) { + // 如果不是 .html 文件,继续下一个路由 + next(); + return; + } + + const filePath = path.join(__dirname, 'order-classes', 'wenlu', page); + + if (fs.existsSync(filePath)) { + let html = fs.readFileSync(filePath, 'utf8'); + res.send(html); + } else { + res.status(404).send(generate404Page(page)); + } +}); + // 订单班路由 app.get('/order-class/:className', (req, res) => { const className = req.params.className; @@ -61,12 +95,8 @@ app.get('/order-class/:className', (req, res) => { // 读取文件并修改相对路径 let html = fs.readFileSync(filePath, 'utf8'); - // 修正资源路径 - html = html.replace(/href="css\//g, 'href="/css/'); - html = html.replace(/src="js\//g, 'src="/js/'); - html = html.replace(/href="pages\//g, `href="${classConfig.path}`); - html = html.replace(/src="\/data\//g, 'src="/data/'); - html = html.replace(/href="\/pages\//g, `href="${classConfig.path}`); + // 保持相对路径,让浏览器自己解析 + // 不需要修改路径,因为我们已经设置了静态文件服务 res.send(html); } else { @@ -85,17 +115,15 @@ app.get('/order-classes/:className/:page', (req, res) => { if (fs.existsSync(filePath)) { let html = fs.readFileSync(filePath, 'utf8'); - // 修正资源路径 - html = html.replace(/href="\.\.\/css\//g, 'href="/css/'); - html = html.replace(/src="\.\.\/js\//g, 'src="/js/'); - html = html.replace(/src="\/data\//g, 'src="/data/'); - + // 保持相对路径不变 res.send(html); } else { res.status(404).send(generate404Page(`${className}/${page}`)); } }); +// 已删除 /order-class/pages/:page 路由,因为导航链接已修复为相对路径 + // 生成首页(选择页面) function generateIndexPage() { const orderClasses = Object.entries(config.order_classes); diff --git a/web_frontend/web_result/order-classes/wenlu/css/animations.css b/web_frontend/web_result/order-classes/wenlu/css/animations.css new file mode 100644 index 00000000..02841e1d --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/css/animations.css @@ -0,0 +1,919 @@ +/* 2024长三角国际新能源汽车与智能交通产业博览会 - 高级动画样式 */ + +/* ============================================ + 字体图标引入 + ============================================ */ +@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'); + +/* ============================================ + 自定义字体 + ============================================ */ +@font-face { + font-family: 'TechFont'; + src: url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap'); +} + +/* ============================================ + 核心动画变量 + ============================================ */ +:root { + /* 动画时长 */ + --anim-fast: 0.2s; + --anim-normal: 0.3s; + --anim-slow: 0.5s; + --anim-slower: 0.8s; + --anim-slowest: 1.2s; + + /* 缓动函数 */ + --ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55); + --ease-elastic: cubic-bezier(0.68, -0.55, 0.265, 1.55); + --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1); + --ease-sharp: cubic-bezier(0.4, 0, 0.6, 1); + + /* 渐变色 */ + --gradient-electric: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --gradient-ocean: linear-gradient(135deg, #667eea 0%, #42e695 100%); + --gradient-sunset: linear-gradient(135deg, #fa709a 0%, #fee140 100%); + --gradient-aurora: linear-gradient(135deg, #42e695 0%, #3bb2b8 100%); +} + +/* ============================================ + 基础动画类 + ============================================ */ +/* 淡入向上动画 */ +.animate-fadeInUp { + opacity: 0; + transform: translateY(30px); + animation: fadeInUp 0.8s ease-out forwards; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} + +/* 淡入向左动画 */ +.animate-fadeInLeft { + opacity: 0; + transform: translateX(-30px); + animation: fadeInLeft 0.8s ease-out forwards; +} + +@keyframes fadeInLeft { + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 淡入向右动画 */ +.animate-fadeInRight { + opacity: 0; + transform: translateX(30px); + animation: fadeInRight 0.8s ease-out forwards; +} + +@keyframes fadeInRight { + to { + opacity: 1; + transform: translateX(0); + } +} + +/* 动画延迟类 */ +.animation-delay-100 { animation-delay: 0.1s; } +.animation-delay-200 { animation-delay: 0.2s; } +.animation-delay-300 { animation-delay: 0.3s; } +.animation-delay-400 { animation-delay: 0.4s; } +.animation-delay-500 { animation-delay: 0.5s; } +.animation-delay-600 { animation-delay: 0.6s; } +.animation-delay-700 { animation-delay: 0.7s; } +.animation-delay-800 { animation-delay: 0.8s; } +.animation-delay-900 { animation-delay: 0.9s; } +.animation-delay-1000 { animation-delay: 1s; } +.animation-delay-1100 { animation-delay: 1.1s; } +.animation-delay-2000 { animation-delay: 2s; } +.animation-delay-4000 { animation-delay: 4s; } + +/* ============================================ + 页面加载动画 + ============================================ */ +.page-loader { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + animation: fadeOut 0.5s ease-out 1s forwards; +} + +.loader-content { + text-align: center; +} + +.loader-logo { + width: 100px; + height: 100px; + margin: 0 auto 20px; + background: white; + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + animation: pulse 1s infinite; +} + +.loader-text { + color: white; + font-size: 1.2rem; + animation: fadeInUp 0.5s ease-out; +} + +.loader-progress { + width: 200px; + height: 4px; + background: rgba(255, 255, 255, 0.3); + border-radius: 2px; + margin: 20px auto; + overflow: hidden; +} + +.loader-progress-bar { + width: 0%; + height: 100%; + background: white; + animation: loading 1s ease-out forwards; +} + +@keyframes loading { + to { width: 100%; } +} + +/* ============================================ + 入场动画 + ============================================ */ +.fade-in { + opacity: 0; + animation: fadeIn var(--anim-slow) ease-out forwards; +} + +@keyframes fadeIn { + to { + opacity: 1; + } +} + +.fade-in-up { + opacity: 0; + transform: translateY(30px); + animation: fadeInUp var(--anim-slow) ease-out forwards; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-in-down { + opacity: 0; + transform: translateY(-30px); + animation: fadeInDown var(--anim-slow) ease-out forwards; +} + +@keyframes fadeInDown { + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-in-left { + opacity: 0; + transform: translateX(-30px); + animation: fadeInLeft var(--anim-slow) ease-out forwards; +} + +@keyframes fadeInLeft { + to { + opacity: 1; + transform: translateX(0); + } +} + +.fade-in-right { + opacity: 0; + transform: translateX(30px); + animation: fadeInRight var(--anim-slow) ease-out forwards; +} + +@keyframes fadeInRight { + to { + opacity: 1; + transform: translateX(0); + } +} + +/* ============================================ + 缩放动画 + ============================================ */ +.zoom-in { + opacity: 0; + transform: scale(0.8); + animation: zoomIn var(--anim-slow) var(--ease-bounce) forwards; +} + +@keyframes zoomIn { + to { + opacity: 1; + transform: scale(1); + } +} + +.zoom-out { + opacity: 0; + transform: scale(1.2); + animation: zoomOut var(--anim-slow) var(--ease-bounce) forwards; +} + +@keyframes zoomOut { + to { + opacity: 1; + transform: scale(1); + } +} + +/* ============================================ + 旋转动画 + ============================================ */ +.rotate-in { + opacity: 0; + transform: rotate(-180deg) scale(0.8); + animation: rotateIn var(--anim-slow) var(--ease-bounce) forwards; +} + +@keyframes rotateIn { + to { + opacity: 1; + transform: rotate(0) scale(1); + } +} + +.flip-in-x { + opacity: 0; + transform: rotateX(-90deg); + animation: flipInX var(--anim-slow) ease-out forwards; +} + +@keyframes flipInX { + to { + opacity: 1; + transform: rotateX(0); + } +} + +.flip-in-y { + opacity: 0; + transform: rotateY(-90deg); + animation: flipInY var(--anim-slow) ease-out forwards; +} + +@keyframes flipInY { + to { + opacity: 1; + transform: rotateY(0); + } +} + +/* ============================================ + 弹跳动画 + ============================================ */ +.bounce-in { + opacity: 0; + transform: scale(0.3); + animation: bounceIn var(--anim-slower) var(--ease-bounce) forwards; +} + +@keyframes bounceIn { + 0% { + opacity: 0; + transform: scale(0.3); + } + 50% { + opacity: 1; + transform: scale(1.05); + } + 70% { + transform: scale(0.9); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* ============================================ + 悬停效果 + ============================================ */ +.hover-grow { + transition: transform var(--anim-normal) var(--ease-smooth); +} + +.hover-grow:hover { + transform: scale(1.05); +} + +.hover-shrink { + transition: transform var(--anim-normal) var(--ease-smooth); +} + +.hover-shrink:hover { + transform: scale(0.95); +} + +.hover-rotate { + transition: transform var(--anim-normal) var(--ease-smooth); +} + +.hover-rotate:hover { + transform: rotate(5deg); +} + +.hover-lift { + transition: all var(--anim-normal) var(--ease-smooth); +} + +.hover-lift:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); +} + +.hover-sink { + transition: all var(--anim-normal) var(--ease-smooth); +} + +.hover-sink:hover { + transform: translateY(3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); +} + +/* ============================================ + 按钮悬停效果 + ============================================ */ +.btn-hover-fill { + position: relative; + overflow: hidden; + transition: color var(--anim-normal); + z-index: 1; +} + +.btn-hover-fill::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: var(--gradient-electric); + transition: left var(--anim-normal) var(--ease-smooth); + z-index: -1; +} + +.btn-hover-fill:hover::before { + left: 0; +} + +.btn-hover-fill:hover { + color: white; +} + +/* 按钮波纹效果 */ +.btn-ripple { + position: relative; + overflow: hidden; +} + +.btn-ripple::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.5); + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; +} + +.btn-ripple:active::after { + width: 300px; + height: 300px; +} + +/* 按钮发光效果 */ +.btn-glow { + transition: all var(--anim-normal); +} + +.btn-glow:hover { + box-shadow: 0 0 20px rgba(102, 126, 234, 0.6), + 0 0 40px rgba(102, 126, 234, 0.4), + 0 0 60px rgba(102, 126, 234, 0.2); +} + +/* ============================================ + 图片悬停效果 + ============================================ */ +.img-hover-zoom { + overflow: hidden; +} + +.img-hover-zoom img { + transition: transform var(--anim-slow) var(--ease-smooth); +} + +.img-hover-zoom:hover img { + transform: scale(1.1); +} + +.img-hover-rotate { + overflow: hidden; +} + +.img-hover-rotate img { + transition: transform var(--anim-slow) var(--ease-smooth); +} + +.img-hover-rotate:hover img { + transform: scale(1.1) rotate(3deg); +} + +.img-hover-blur { + overflow: hidden; +} + +.img-hover-blur img { + transition: all var(--anim-slow) var(--ease-smooth); +} + +.img-hover-blur:hover img { + filter: blur(3px); + transform: scale(1.1); +} + +/* 图片叠加效果 */ +.img-overlay { + position: relative; + overflow: hidden; +} + +.img-overlay::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.8), rgba(118, 75, 162, 0.8)); + opacity: 0; + transition: opacity var(--anim-normal); + z-index: 1; +} + +.img-overlay:hover::before { + opacity: 1; +} + +.img-overlay-content { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + text-align: center; + opacity: 0; + transition: opacity var(--anim-normal); + z-index: 2; +} + +.img-overlay:hover .img-overlay-content { + opacity: 1; +} + +/* ============================================ + 文字动画 + ============================================ */ +.text-glow { + animation: textGlow 2s ease-in-out infinite alternate; +} + +@keyframes textGlow { + from { + text-shadow: 0 0 10px rgba(102, 126, 234, 0.5), + 0 0 20px rgba(102, 126, 234, 0.3); + } + to { + text-shadow: 0 0 20px rgba(102, 126, 234, 0.8), + 0 0 30px rgba(102, 126, 234, 0.5); + } +} + +.text-typing { + overflow: hidden; + border-right: 2px solid; + white-space: nowrap; + animation: typing 3.5s steps(40, end), + blink-caret 0.75s step-end infinite; +} + +@keyframes typing { + from { width: 0; } + to { width: 100%; } +} + +@keyframes blink-caret { + from, to { border-color: transparent; } + 50% { border-color: currentColor; } +} + +.text-gradient-animate { + background: linear-gradient(90deg, #667eea, #764ba2, #42e695, #667eea); + background-size: 300% 100%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: gradientMove 5s ease infinite; +} + +@keyframes gradientMove { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +/* ============================================ + 卡片动画 + ============================================ */ +.card-3d { + transform-style: preserve-3d; + transition: transform var(--anim-slow) var(--ease-smooth); +} + +.card-3d:hover { + transform: rotateY(10deg) rotateX(10deg) translateZ(20px); +} + +.card-flip { + position: relative; + transform-style: preserve-3d; + transition: transform var(--anim-slow); +} + +.card-flip:hover { + transform: rotateY(180deg); +} + +.card-flip-front, +.card-flip-back { + position: absolute; + width: 100%; + height: 100%; + backface-visibility: hidden; +} + +.card-flip-back { + transform: rotateY(180deg); +} + +/* ============================================ + 滚动动画 + ============================================ */ +.scroll-reveal { + opacity: 0; + transform: translateY(50px); + transition: all var(--anim-slower) var(--ease-smooth); +} + +.scroll-reveal.revealed { + opacity: 1; + transform: translateY(0); +} + +.scroll-reveal-left { + opacity: 0; + transform: translateX(-50px); + transition: all var(--anim-slower) var(--ease-smooth); +} + +.scroll-reveal-left.revealed { + opacity: 1; + transform: translateX(0); +} + +.scroll-reveal-right { + opacity: 0; + transform: translateX(50px); + transition: all var(--anim-slower) var(--ease-smooth); +} + +.scroll-reveal-right.revealed { + opacity: 1; + transform: translateX(0); +} + +.scroll-reveal-scale { + opacity: 0; + transform: scale(0.8); + transition: all var(--anim-slower) var(--ease-smooth); +} + +.scroll-reveal-scale.revealed { + opacity: 1; + transform: scale(1); +} + +/* ============================================ + 视差效果 + ============================================ */ +.parallax { + position: relative; + transform-style: preserve-3d; +} + +.parallax-layer { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.parallax-layer-back { + transform: translateZ(-1px) scale(2); +} + +.parallax-layer-base { + transform: translateZ(0); +} + +.parallax-layer-front { + transform: translateZ(1px) scale(0.5); +} + +/* ============================================ + 加载动画 + ============================================ */ +.skeleton { + position: relative; + overflow: hidden; + background: #f0f0f0; +} + +.skeleton::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent); + animation: skeleton-loading 1.5s infinite; +} + +@keyframes skeleton-loading { + to { + left: 100%; + } +} + +/* ============================================ + 脉冲动画 + ============================================ */ +.pulse { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.05); + opacity: 0.8; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +.pulse-slow { + animation: pulse 3s infinite; +} + +.pulse-fast { + animation: pulse 1s infinite; +} + +/* ============================================ + 抖动动画 + ============================================ */ +.shake { + animation: shake 0.5s; +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); } + 20%, 40%, 60%, 80% { transform: translateX(10px); } +} + +.shake-horizontal { + animation: shakeHorizontal 0.5s; +} + +@keyframes shakeHorizontal { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } +} + +/* ============================================ + 浮动动画 + ============================================ */ +.float { + animation: float 3s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-20px); } +} + +.float-slow { + animation: float 5s ease-in-out infinite; +} + +.float-fast { + animation: float 2s ease-in-out infinite; +} + +/* ============================================ + 闪烁动画 + ============================================ */ +.blink { + animation: blink 1s infinite; +} + +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +.flash { + animation: flash 0.5s; +} + +@keyframes flash { + 0%, 50%, 100% { opacity: 1; } + 25%, 75% { opacity: 0; } +} + +/* ============================================ + 渐变背景动画 + ============================================ */ +.bg-gradient-animate { + background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); + background-size: 400% 400%; + animation: gradientShift 15s ease infinite; +} + +@keyframes gradientShift { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +/* ============================================ + 光标跟随效果 + ============================================ */ +.cursor-follow { + position: fixed; + width: 20px; + height: 20px; + border: 2px solid #667eea; + border-radius: 50%; + pointer-events: none; + transition: all 0.1s ease; + z-index: 9999; +} + +.cursor-follow.active { + transform: scale(2); + background: rgba(102, 126, 234, 0.1); +} + +/* ============================================ + 工具提示 + ============================================ */ +.tooltip { + position: relative; +} + +.tooltip::after { + content: attr(data-tooltip); + position: absolute; + bottom: 125%; + left: 50%; + transform: translateX(-50%) scale(0); + background: #333; + color: white; + padding: 8px 12px; + border-radius: 6px; + font-size: 14px; + white-space: nowrap; + opacity: 0; + transition: all var(--anim-normal) var(--ease-smooth); + pointer-events: none; +} + +.tooltip::before { + content: ''; + position: absolute; + bottom: 115%; + left: 50%; + transform: translateX(-50%) scale(0); + border: 6px solid transparent; + border-top-color: #333; + opacity: 0; + transition: all var(--anim-normal) var(--ease-smooth); +} + +.tooltip:hover::after, +.tooltip:hover::before { + transform: translateX(-50%) scale(1); + opacity: 1; +} + +/* ============================================ + 交错动画 + ============================================ */ +.stagger-children > * { + opacity: 0; + transform: translateY(20px); + animation: staggerIn var(--anim-slow) var(--ease-smooth) forwards; +} + +.stagger-children > *:nth-child(1) { animation-delay: 0.1s; } +.stagger-children > *:nth-child(2) { animation-delay: 0.2s; } +.stagger-children > *:nth-child(3) { animation-delay: 0.3s; } +.stagger-children > *:nth-child(4) { animation-delay: 0.4s; } +.stagger-children > *:nth-child(5) { animation-delay: 0.5s; } +.stagger-children > *:nth-child(6) { animation-delay: 0.6s; } +.stagger-children > *:nth-child(7) { animation-delay: 0.7s; } +.stagger-children > *:nth-child(8) { animation-delay: 0.8s; } + +@keyframes staggerIn { + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ============================================ + 模糊背景 + ============================================ */ +.blur-background { + backdrop-filter: blur(10px); + background: rgba(255, 255, 255, 0.8); +} + +.glass-morphism { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); +} + +/* ============================================ + 响应式动画 + ============================================ */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/css/styles.css b/web_frontend/web_result/order-classes/wenlu/css/styles.css new file mode 100644 index 00000000..5837176f --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/css/styles.css @@ -0,0 +1,454 @@ +/* 2024长三角国际新能源汽车与智能交通产业博览会 - 全局样式 */ + +/* 根变量定义 */ +:root { + /* 主题色彩系统 */ + --primary-gradient-start: #10b981; + --primary-gradient-end: #3b82f6; + --secondary-gradient-start: #8b5cf6; + --secondary-gradient-end: #ec4899; + --accent-gradient-start: #f59e0b; + --accent-gradient-end: #ef4444; + + /* 中性色 */ + --gray-50: #f9fafb; + --gray-100: #f3f4f6; + --gray-200: #e5e7eb; + --gray-300: #d1d5db; + --gray-400: #9ca3af; + --gray-500: #6b7280; + --gray-600: #4b5563; + --gray-700: #374151; + --gray-800: #1f2937; + --gray-900: #111827; + + /* 间距系统 */ + --spacing-xs: 0.5rem; + --spacing-sm: 1rem; + --spacing-md: 1.5rem; + --spacing-lg: 2rem; + --spacing-xl: 3rem; + --spacing-2xl: 4rem; + + /* 动画时长 */ + --transition-fast: 150ms; + --transition-base: 300ms; + --transition-slow: 500ms; + --transition-slower: 700ms; + + /* 阴影 */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-base: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1); + + /* 圆角 */ + --radius-sm: 0.375rem; + --radius-base: 0.5rem; + --radius-lg: 0.75rem; + --radius-xl: 1rem; + --radius-full: 9999px; +} + +/* 全局重置和基础样式 */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; + font-size: 16px; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'Microsoft YaHei', sans-serif; + line-height: 1.6; + color: var(--gray-800); + background-color: var(--gray-50); +} + +/* 渐变背景动画 */ +@keyframes gradientShift { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +.gradient-bg { + background: linear-gradient(-45deg, + var(--primary-gradient-start), + var(--primary-gradient-end), + var(--secondary-gradient-start), + var(--secondary-gradient-end)); + background-size: 400% 400%; + animation: gradientShift 15s ease infinite; +} + +.gradient-primary { + background: linear-gradient(135deg, var(--primary-gradient-start), var(--primary-gradient-end)); +} + +.gradient-secondary { + background: linear-gradient(135deg, var(--secondary-gradient-start), var(--secondary-gradient-end)); +} + +.gradient-accent { + background: linear-gradient(135deg, var(--accent-gradient-start), var(--accent-gradient-end)); +} + +/* 导航栏样式 */ +.navbar { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + box-shadow: var(--shadow-base); + transition: all var(--transition-base) ease; +} + +.navbar.scrolled { + background: rgba(255, 255, 255, 0.98); + box-shadow: var(--shadow-lg); +} + +.nav-link { + position: relative; + padding: var(--spacing-xs) var(--spacing-sm); + color: var(--gray-700); + text-decoration: none; + transition: color var(--transition-fast) ease; + overflow: hidden; +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + width: 0; + height: 2px; + background: linear-gradient(90deg, var(--primary-gradient-start), var(--primary-gradient-end)); + transform: translateX(-50%); + transition: width var(--transition-base) ease; +} + +.nav-link:hover::after, +.nav-link.active::after { + width: 100%; +} + +/* 卡片动画效果 */ +@keyframes cardFloat { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} + +.card { + background: white; + border-radius: var(--radius-lg); + padding: var(--spacing-lg); + box-shadow: var(--shadow-base); + transition: all var(--transition-base) ease; + position: relative; + overflow: hidden; +} + +.card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, var(--primary-gradient-start), var(--primary-gradient-end)); + transform: scaleX(0); + transform-origin: left; + transition: transform var(--transition-base) ease; +} + +.card:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-xl); +} + +.card:hover::before { + transform: scaleX(1); +} + +/* 按钮样式 */ +.btn { + display: inline-block; + padding: var(--spacing-sm) var(--spacing-lg); + border-radius: var(--radius-full); + font-weight: 600; + text-decoration: none; + transition: all var(--transition-base) ease; + position: relative; + overflow: hidden; + cursor: pointer; + border: none; + outline: none; +} + +.btn-primary { + background: linear-gradient(135deg, var(--primary-gradient-start), var(--primary-gradient-end)); + color: white; + box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4); +} + +.btn-secondary { + background: white; + color: var(--gray-700); + border: 2px solid var(--gray-200); +} + +.btn-secondary:hover { + background: var(--gray-50); + border-color: var(--primary-gradient-start); + color: var(--primary-gradient-start); +} + +/* 波纹效果 */ +@keyframes ripple { + 0% { + transform: scale(0); + opacity: 1; + } + 100% { + transform: scale(4); + opacity: 0; + } +} + +.ripple { + position: absolute; + border-radius: 50%; + background: rgba(255, 255, 255, 0.5); + animation: ripple 0.6s ease-out; + pointer-events: none; +} + +/* 加载动画 */ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.spinner { + display: inline-block; + width: 40px; + height: 40px; + border: 4px solid var(--gray-200); + border-top-color: var(--primary-gradient-start); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* 数字滚动动画 */ +@keyframes countUp { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.count-up { + animation: countUp var(--transition-slow) ease-out; +} + +/* 淡入动画 */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.fade-in { + animation: fadeIn var(--transition-slower) ease-out; +} + +/* 滑入动画 */ +@keyframes slideInLeft { + from { opacity: 0; transform: translateX(-50px); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes slideInRight { + from { opacity: 0; transform: translateX(50px); } + to { opacity: 1; transform: translateX(0); } +} + +.slide-in-left { + animation: slideInLeft var(--transition-slow) ease-out; +} + +.slide-in-right { + animation: slideInRight var(--transition-slow) ease-out; +} + +/* 脉冲动画 */ +@keyframes pulse { + 0%, 100% { transform: scale(1); opacity: 1; } + 50% { transform: scale(1.05); opacity: 0.9; } +} + +.pulse { + animation: pulse 2s ease-in-out infinite; +} + +/* 光晕效果 */ +@keyframes glow { + 0%, 100% { box-shadow: 0 0 20px rgba(16, 185, 129, 0.5); } + 50% { box-shadow: 0 0 40px rgba(16, 185, 129, 0.8); } +} + +.glow { + animation: glow 2s ease-in-out infinite; +} + +/* 时间线样式 */ +.timeline { + position: relative; + padding-left: var(--spacing-xl); +} + +.timeline::before { + content: ''; + position: absolute; + left: 15px; + top: 0; + bottom: 0; + width: 2px; + background: linear-gradient(180deg, var(--primary-gradient-start), var(--primary-gradient-end)); +} + +.timeline-item { + position: relative; + padding-bottom: var(--spacing-xl); +} + +.timeline-item::before { + content: ''; + position: absolute; + left: -25px; + top: 5px; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--primary-gradient-start); + border: 3px solid white; + box-shadow: var(--shadow-base); +} + +/* 网格布局动画 */ +.grid-item { + opacity: 0; + transform: scale(0.9); + animation: gridFadeIn var(--transition-slow) ease-out forwards; +} + +@keyframes gridFadeIn { + to { + opacity: 1; + transform: scale(1); + } +} + +.grid-item:nth-child(1) { animation-delay: 0.1s; } +.grid-item:nth-child(2) { animation-delay: 0.2s; } +.grid-item:nth-child(3) { animation-delay: 0.3s; } +.grid-item:nth-child(4) { animation-delay: 0.4s; } +.grid-item:nth-child(5) { animation-delay: 0.5s; } +.grid-item:nth-child(6) { animation-delay: 0.6s; } + +/* 响应式设计 */ +@media (max-width: 768px) { + :root { + font-size: 14px; + } + + .card { + padding: var(--spacing-md); + } + + .timeline { + padding-left: var(--spacing-lg); + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +/* 打印样式 */ +@media print { + .navbar, + .btn, + .no-print { + display: none !important; + } + + body { + font-size: 12pt; + color: black; + background: white; + } + + .card { + box-shadow: none; + border: 1px solid #ddd; + page-break-inside: avoid; + } +} + +/* 辅助类 */ +.text-gradient { + background: linear-gradient(135deg, var(--primary-gradient-start), var(--primary-gradient-end)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.hover-scale { + transition: transform var(--transition-base) ease; +} + +.hover-scale:hover { + transform: scale(1.05); +} + +.hover-lift { + transition: transform var(--transition-base) ease; +} + +.hover-lift:hover { + transform: translateY(-5px); +} + +.overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.7)); +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--spacing-md); +} \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/data/会展策划 b/web_frontend/web_result/order-classes/wenlu/data/会展策划 new file mode 120000 index 00000000..b8713a79 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/data/会展策划 @@ -0,0 +1 @@ +/Users/xiaoqi/Documents/Dev/Project/2025-09-08_n8nDEMO演示/data/会展策划 \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/index.html b/web_frontend/web_result/order-classes/wenlu/index.html index 0f58cced..6f11461f 100644 --- a/web_frontend/web_result/order-classes/wenlu/index.html +++ b/web_frontend/web_result/order-classes/wenlu/index.html @@ -122,7 +122,35 @@ @@ -195,7 +223,7 @@
- + @@ -291,7 +319,7 @@

涵盖整车制造、核心零部件、充电设施、智能网联等全产业链环节,打造一站式采购平台。

-
+ 了解更多
@@ -310,7 +338,7 @@

设置自动驾驶体验区、智能交通演示区,让观众亲身体验最新科技成果。

- + 了解更多 @@ -329,7 +357,7 @@

汇聚行业领袖、专家学者,探讨产业发展趋势,发布权威研究报告。

- + 了解更多 @@ -518,16 +546,16 @@ 快速链接 diff --git a/web_frontend/web_result/order-classes/wenlu/js/back-to-top.js b/web_frontend/web_result/order-classes/wenlu/js/back-to-top.js new file mode 100644 index 00000000..0fc4fe36 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/back-to-top.js @@ -0,0 +1,188 @@ +// 返回顶部按钮组件 +(function() { + // 创建返回顶部按钮HTML + function createBackToTopButton() { + const button = document.createElement('div'); + button.id = 'back-to-top'; + button.className = 'fixed bottom-8 right-8 z-40 opacity-0 pointer-events-none transition-all duration-300'; + button.innerHTML = ` + + `; + document.body.appendChild(button); + return button; + } + + // 添加样式 + function addStyles() { + const style = document.createElement('style'); + style.textContent = ` + #back-to-top.show { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + } + + #back-to-top:not(.show) { + transform: translateY(100px); + } + + .back-to-top-btn { + position: relative; + overflow: hidden; + } + + .back-to-top-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); + transition: left 0.5s; + } + + .back-to-top-btn:hover::before { + left: 100%; + } + + .back-to-top-btn svg { + animation: float 2s ease-in-out infinite; + } + + @keyframes float { + 0%, 100% { + transform: translateX(-50%) translateY(-50%); + } + 50% { + transform: translateX(-50%) translateY(calc(-50% - 3px)); + } + } + + /* 移动端适配 */ + @media (max-width: 768px) { + #back-to-top { + bottom: 1.5rem; + right: 1.5rem; + } + + .back-to-top-btn { + width: 2.5rem; + height: 2.5rem; + } + + .back-to-top-btn svg { + width: 1.25rem; + height: 1.25rem; + } + } + + /* 平滑滚动行为 */ + html { + scroll-behavior: smooth; + } + + /* 防止按钮与其他元素冲突 */ + @media (max-width: 640px) { + #back-to-top { + bottom: 5rem; /* 避免与移动端菜单按钮重叠 */ + } + } + `; + document.head.appendChild(style); + } + + // 滚动到顶部功能 + function scrollToTop() { + // 使用 smooth 滚动 + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + + // 可选:添加动画反馈 + const button = document.querySelector('.back-to-top-btn'); + if (button) { + button.style.transform = 'scale(0.9)'; + setTimeout(() => { + button.style.transform = ''; + }, 200); + } + } + + // 控制按钮显示/隐藏 + function handleScroll() { + const button = document.getElementById('back-to-top'); + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + const windowHeight = window.innerHeight; + + // 当滚动超过一屏高度时显示按钮 + if (scrollTop > windowHeight * 0.5) { + button.classList.add('show'); + } else { + button.classList.remove('show'); + } + + // 可选:根据滚动位置调整按钮透明度 + if (scrollTop > windowHeight) { + const maxScroll = document.documentElement.scrollHeight - windowHeight; + const scrollProgress = Math.min(scrollTop / maxScroll, 1); + const opacity = 0.5 + scrollProgress * 0.5; // 从50%到100%透明度 + button.style.opacity = button.classList.contains('show') ? opacity : 0; + } + } + + // 初始化 + function init() { + // 添加样式 + addStyles(); + + // 创建按钮 + const button = createBackToTopButton(); + + // 添加点击事件 + button.addEventListener('click', scrollToTop); + + // 添加滚动监听(使用节流) + let scrollTimer; + window.addEventListener('scroll', () => { + if (scrollTimer) { + clearTimeout(scrollTimer); + } + scrollTimer = setTimeout(handleScroll, 50); + }); + + // 初始检查 + handleScroll(); + + // 键盘快捷键支持(按 'T' 键返回顶部) + document.addEventListener('keydown', (e) => { + // 排除输入框等元素 + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { + return; + } + + // 按 T 键或 Home 键返回顶部 + if (e.key === 't' || e.key === 'T' || e.key === 'Home') { + e.preventDefault(); + scrollToTop(); + } + }); + } + + // DOM加载完成后初始化 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/error-handler.js b/web_frontend/web_result/order-classes/wenlu/js/error-handler.js new file mode 100644 index 00000000..41b142a3 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/error-handler.js @@ -0,0 +1,279 @@ +// 错误处理和兼容性脚本 +(function() { + 'use strict'; + + // 全局错误处理 + window.addEventListener('error', function(event) { + console.error('Global error:', { + message: event.message, + filename: event.filename, + lineno: event.lineno, + colno: event.colno, + error: event.error + }); + + // 发送错误到控制台(便于调试) + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + console.group('Error Details:'); + console.log('Message:', event.message); + console.log('File:', event.filename); + console.log('Line:', event.lineno + ':' + event.colno); + if (event.error && event.error.stack) { + console.log('Stack:', event.error.stack); + } + console.groupEnd(); + } + + return true; // 阻止默认错误处理 + }); + + // Promise 错误处理 + window.addEventListener('unhandledrejection', function(event) { + console.error('Unhandled promise rejection:', event.reason); + + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + console.group('Promise Rejection:'); + console.log('Reason:', event.reason); + if (event.reason && event.reason.stack) { + console.log('Stack:', event.reason.stack); + } + console.groupEnd(); + } + + event.preventDefault(); // 阻止默认处理 + }); + + // DOM 查询安全包装 + function safeQuerySelector(selector) { + try { + return document.querySelector(selector); + } catch (e) { + console.warn('Invalid selector:', selector, e); + return null; + } + } + + function safeQuerySelectorAll(selector) { + try { + return document.querySelectorAll(selector); + } catch (e) { + console.warn('Invalid selector:', selector, e); + return []; + } + } + + // 安全的getElementById + function safeGetElementById(id) { + try { + return document.getElementById(id); + } catch (e) { + console.warn('Invalid element ID:', id, e); + return null; + } + } + + // 修复常见的兼容性问题 + function fixCompatibilityIssues() { + // 修复classList不支持的情况(简化版本以避免Illegal invocation) + try { + // 安全检测classList支持 + var testElement = document.createElement('div'); + if (!testElement.classList) { + // 简单的classList polyfill + window.ClassListPolyfill = { + addClass: function(element, className) { + if (element && className) { + var classes = element.className ? element.className.split(' ') : []; + if (classes.indexOf(className) === -1) { + classes.push(className); + element.className = classes.join(' ').trim(); + } + } + }, + removeClass: function(element, className) { + if (element && className) { + var classes = element.className ? element.className.split(' ') : []; + var index = classes.indexOf(className); + if (index !== -1) { + classes.splice(index, 1); + element.className = classes.join(' ').trim(); + } + } + }, + hasClass: function(element, className) { + if (!element || !className) return false; + var classes = element.className ? element.className.split(' ') : []; + return classes.indexOf(className) !== -1; + } + }; + } + } catch (e) { + // 如果检测失败,忽略错误 + console.warn('classList detection failed:', e); + } + + // 修复addEventListener不支持的情况 + if (!Element.prototype.addEventListener && Element.prototype.attachEvent) { + Element.prototype.addEventListener = function(event, listener) { + this.attachEvent('on' + event, listener); + }; + Element.prototype.removeEventListener = function(event, listener) { + this.detachEvent('on' + event, listener); + }; + } + + // 修复Array.from不支持的情况 + if (!Array.from) { + Array.from = function(arrayLike) { + var result = []; + for (var i = 0; i < arrayLike.length; i++) { + result.push(arrayLike[i]); + } + return result; + }; + } + + // 修复forEach不支持的情况 + if (!NodeList.prototype.forEach) { + NodeList.prototype.forEach = function(callback, thisArg) { + for (var i = 0; i < this.length; i++) { + callback.call(thisArg, this[i], i, this); + } + }; + } + } + + // 检测浏览器支持情况 + function detectBrowserSupport() { + const features = { + es6: { + arrow: false, + const: false, + let: false, + template: false, + destructuring: false + }, + dom: { + classList: (function() { + try { + var testElement = document.createElement('div'); + return !!testElement.classList; + } catch (e) { + return false; + } + })(), + querySelector: !!document.querySelector, + addEventListener: !!window.addEventListener + }, + css: { + grid: typeof CSS !== 'undefined' && CSS.supports && CSS.supports('display', 'grid'), + flexbox: typeof CSS !== 'undefined' && CSS.supports && CSS.supports('display', 'flex'), + transform: typeof CSS !== 'undefined' && CSS.supports && CSS.supports('transform', 'translateX(0)') + } + }; + + // 测试ES6功能 + try { + eval('const x = 1'); + features.es6.const = true; + } catch (e) {} + + try { + eval('let x = 1'); + features.es6.let = true; + } catch (e) {} + + try { + eval('`template`'); + features.es6.template = true; + } catch (e) {} + + try { + eval('() => {}'); + features.es6.arrow = true; + } catch (e) {} + + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + console.group('Browser Support:'); + console.table(features); + console.groupEnd(); + } + + return features; + } + + // 修复具体的错误 + function fixSpecificErrors() { + // 确保所有必要的元素存在 + const requiredElements = [ + 'navbar', + 'mobile-menu-button', + 'mobile-menu', + 'mobile-menu-overlay', + 'close-menu' + ]; + + setTimeout(function() { + requiredElements.forEach(function(id) { + const element = document.getElementById(id); + if (!element) { + console.warn('Missing required element:', id); + + // 尝试创建缺失的元素(某些情况下) + if (id === 'mobile-menu-overlay' && !document.getElementById(id)) { + const overlay = document.createElement('div'); + overlay.id = id; + overlay.className = 'mobile-menu-overlay hidden'; + document.body.appendChild(overlay); + console.log('Created missing overlay element'); + } + } + }); + }, 1000); + } + + // 安全的脚本加载 + function safeLoadScript(src, callback) { + const script = document.createElement('script'); + script.src = src; + script.onload = function() { + if (callback) callback(); + }; + script.onerror = function() { + console.error('Failed to load script:', src); + }; + document.head.appendChild(script); + } + + // 初始化错误处理 + function init() { + // 修复兼容性问题 + fixCompatibilityIssues(); + + // 检测浏览器支持 + detectBrowserSupport(); + + // 修复具体错误 + fixSpecificErrors(); + + // 为调试添加全局工具 + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + window.debugTools = { + safeQuerySelector: safeQuerySelector, + safeQuerySelectorAll: safeQuerySelectorAll, + safeGetElementById: safeGetElementById, + fixCompatibilityIssues: fixCompatibilityIssues, + detectBrowserSupport: detectBrowserSupport + }; + } + + console.log('Error handler initialized'); + } + + // 在DOM加载后初始化 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/main.js b/web_frontend/web_result/order-classes/wenlu/js/main.js new file mode 100644 index 00000000..89e979e4 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/main.js @@ -0,0 +1,713 @@ +// 2024长三角国际新能源汽车与智能交通产业博览会 - 交互脚本 + +// 页面加载完成后初始化 +document.addEventListener('DOMContentLoaded', function() { + // 隐藏页面加载器 + hidePageLoader(); + + initNavbar(); + initAnimations(); + initCounters(); + initScrollEffects(); + initRippleEffect(); + initLazyLoading(); + initFormValidation(); + initCharts(); + initTimeline(); + initInteractiveElements(); + handleImageErrors(); +}); + +// 隐藏页面加载器(兼容新的加载器) +function hidePageLoader() { + const loader = document.getElementById('pageLoader') || document.getElementById('page-loader'); + if (loader) { + // 如果是新的加载器,使用其自带的隐藏方法 + if (loader.id === 'page-loader' && window.PageLoader) { + window.PageLoader.hideLoader(); + return; + } + + // 旧的加载器逻辑 + loader.style.opacity = '0'; + loader.style.transition = 'opacity 0.5s ease-out'; + + setTimeout(() => { + loader.style.display = 'none'; + document.body.style.overflow = 'auto'; + document.body.classList.remove('loading'); + }, 500); + } else { + // 如果没有找到加载器,也要确保恢复滚动 + document.body.style.overflow = 'auto'; + document.body.classList.remove('loading'); + } +} + +// 如果页面加载超过3秒,强制隐藏加载器 +window.addEventListener('load', function() { + setTimeout(() => { + hidePageLoader(); + }, 3000); +}); + +// 导航栏交互 +function initNavbar() { + const navbar = document.querySelector('.navbar'); + const navLinks = document.querySelectorAll('.nav-link'); + const mobileMenuBtn = document.querySelector('.mobile-menu-btn'); + const mobileMenu = document.querySelector('.mobile-menu'); + + // 滚动时改变导航栏样式 + window.addEventListener('scroll', () => { + if (window.scrollY > 100) { + navbar?.classList.add('scrolled'); + } else { + navbar?.classList.remove('scrolled'); + } + }); + + // 高亮当前页面链接 + const currentPath = window.location.pathname; + navLinks.forEach(link => { + if (link.getAttribute('href') === currentPath) { + link.classList.add('active'); + } + }); + + // 移动端菜单切换 + mobileMenuBtn?.addEventListener('click', () => { + mobileMenu?.classList.toggle('open'); + mobileMenuBtn.classList.toggle('active'); + }); + + // 平滑滚动到锚点 + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function(e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); + }); +} + +// 动画初始化 +function initAnimations() { + // Intersection Observer for fade-in animations + const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animate-in'); + observer.unobserve(entry.target); + } + }); + }, observerOptions); + + // 观察所有需要动画的元素 + document.querySelectorAll('.fade-in, .slide-in-left, .slide-in-right, .grid-item').forEach(el => { + observer.observe(el); + }); +} + +// 数字计数器动画 +function initCounters() { + const counters = document.querySelectorAll('.counter'); + const speed = 200; // 动画速度 + + const countUp = (counter) => { + const target = +counter.getAttribute('data-target'); + const increment = target / speed; + + const updateCount = () => { + const count = +counter.innerText.replace(/[^0-9]/g, ''); + + if (count < target) { + counter.innerText = Math.ceil(count + increment).toLocaleString(); + setTimeout(updateCount, 1); + } else { + counter.innerText = target.toLocaleString(); + + // 添加单位 + const unit = counter.getAttribute('data-unit'); + if (unit) { + counter.innerText += unit; + } + } + }; + + updateCount(); + }; + + // 使用 Intersection Observer 触发计数 + const counterObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + countUp(entry.target); + counterObserver.unobserve(entry.target); + } + }); + }, { threshold: 0.5 }); + + counters.forEach(counter => { + counter.innerText = '0'; + counterObserver.observe(counter); + }); +} + +// 滚动效果 +function initScrollEffects() { + let lastScrollTop = 0; + const header = document.querySelector('header'); + + window.addEventListener('scroll', () => { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + + // 隐藏/显示导航栏 + if (scrollTop > lastScrollTop && scrollTop > 300) { + header?.classList.add('hide'); + } else { + header?.classList.remove('hide'); + } + + lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; + + // 视差效果 + const parallaxElements = document.querySelectorAll('.parallax'); + parallaxElements.forEach(el => { + const speed = el.getAttribute('data-speed') || 0.5; + const yPos = -(scrollTop * speed); + el.style.transform = `translateY(${yPos}px)`; + }); + + // 进度条 + const progressBar = document.querySelector('.progress-bar'); + if (progressBar) { + const winScroll = document.body.scrollTop || document.documentElement.scrollTop; + const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; + const scrolled = (winScroll / height) * 100; + progressBar.style.width = scrolled + '%'; + } + }); +} + +// 波纹效果 +function initRippleEffect() { + document.querySelectorAll('.btn, .card').forEach(element => { + element.addEventListener('click', function(e) { + const ripple = document.createElement('span'); + ripple.classList.add('ripple'); + + const rect = this.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const x = e.clientX - rect.left - size / 2; + const y = e.clientY - rect.top - size / 2; + + ripple.style.width = ripple.style.height = size + 'px'; + ripple.style.left = x + 'px'; + ripple.style.top = y + 'px'; + + this.appendChild(ripple); + + setTimeout(() => { + ripple.remove(); + }, 600); + }); + }); +} + +// 懒加载 +function initLazyLoading() { + const images = document.querySelectorAll('img[data-src]'); + + const imageObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target; + img.src = img.dataset.src; + img.removeAttribute('data-src'); + imageObserver.unobserve(img); + + // 添加加载完成动画 + img.addEventListener('load', () => { + img.classList.add('loaded'); + }); + } + }); + }, { + rootMargin: '50px 0px' + }); + + images.forEach(img => imageObserver.observe(img)); +} + +// 表单验证 +function initFormValidation() { + const forms = document.querySelectorAll('form'); + + forms.forEach(form => { + form.addEventListener('submit', function(e) { + e.preventDefault(); + + let isValid = true; + const inputs = form.querySelectorAll('input[required], textarea[required]'); + + inputs.forEach(input => { + if (!input.value.trim()) { + isValid = false; + input.classList.add('error'); + showError(input, '此字段为必填项'); + } else { + input.classList.remove('error'); + clearError(input); + } + + // 邮箱验证 + if (input.type === 'email' && input.value) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(input.value)) { + isValid = false; + input.classList.add('error'); + showError(input, '请输入有效的邮箱地址'); + } + } + + // 电话验证 + if (input.type === 'tel' && input.value) { + const phoneRegex = /^1[3-9]\d{9}$/; + if (!phoneRegex.test(input.value)) { + isValid = false; + input.classList.add('error'); + showError(input, '请输入有效的手机号码'); + } + } + }); + + if (isValid) { + // 显示成功消息 + showSuccess('表单提交成功!'); + form.reset(); + } + }); + + // 实时验证 + form.querySelectorAll('input, textarea').forEach(input => { + input.addEventListener('blur', function() { + validateInput(this); + }); + + input.addEventListener('input', function() { + if (this.classList.contains('error')) { + validateInput(this); + } + }); + }); + }); +} + +// 显示错误信息 +function showError(input, message) { + const errorEl = input.nextElementSibling; + if (errorEl && errorEl.classList.contains('error-message')) { + errorEl.textContent = message; + } else { + const error = document.createElement('span'); + error.classList.add('error-message'); + error.textContent = message; + input.parentNode.insertBefore(error, input.nextSibling); + } +} + +// 清除错误信息 +function clearError(input) { + const errorEl = input.nextElementSibling; + if (errorEl && errorEl.classList.contains('error-message')) { + errorEl.remove(); + } +} + +// 验证输入 +function validateInput(input) { + if (input.hasAttribute('required') && !input.value.trim()) { + input.classList.add('error'); + showError(input, '此字段为必填项'); + return false; + } + + input.classList.remove('error'); + clearError(input); + return true; +} + +// 显示成功消息 +function showSuccess(message) { + const toast = document.createElement('div'); + toast.classList.add('toast', 'success'); + toast.textContent = message; + document.body.appendChild(toast); + + setTimeout(() => { + toast.classList.add('show'); + }, 100); + + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => { + toast.remove(); + }, 300); + }, 3000); +} + +// 初始化图表 +function initCharts() { + // 预算分配饼图 + const budgetChart = document.getElementById('budgetChart'); + if (budgetChart) { + const data = [ + { label: '场地租赁', value: 35, color: '#10b981' }, + { label: '营销推广', value: 25, color: '#3b82f6' }, + { label: '运营服务', value: 20, color: '#8b5cf6' }, + { label: '人员成本', value: 15, color: '#f59e0b' }, + { label: '其他费用', value: 5, color: '#ef4444' } + ]; + + drawPieChart(budgetChart, data); + } + + // 参展商类别分布 + const exhibitorChart = document.getElementById('exhibitorChart'); + if (exhibitorChart) { + const data = [ + { label: '整车制造', value: 40 }, + { label: '零部件', value: 30 }, + { label: '充电设施', value: 15 }, + { label: '智能驾驶', value: 10 }, + { label: '其他', value: 5 } + ]; + + drawBarChart(exhibitorChart, data); + } +} + +// 绘制饼图 +function drawPieChart(canvas, data) { + const ctx = canvas.getContext('2d'); + const centerX = canvas.width / 2; + const centerY = canvas.height / 2; + const radius = Math.min(centerX, centerY) - 20; + + let currentAngle = -Math.PI / 2; + const total = data.reduce((sum, item) => sum + item.value, 0); + + data.forEach(item => { + const sliceAngle = (item.value / total) * 2 * Math.PI; + + // 绘制扇形 + ctx.beginPath(); + ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle); + ctx.lineTo(centerX, centerY); + ctx.fillStyle = item.color; + ctx.fill(); + + // 绘制标签 + const labelX = centerX + Math.cos(currentAngle + sliceAngle / 2) * (radius * 0.7); + const labelY = centerY + Math.sin(currentAngle + sliceAngle / 2) * (radius * 0.7); + + ctx.fillStyle = '#fff'; + ctx.font = '14px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(item.value + '%', labelX, labelY); + + currentAngle += sliceAngle; + }); +} + +// 绘制柱状图 +function drawBarChart(canvas, data) { + const ctx = canvas.getContext('2d'); + const barWidth = canvas.width / (data.length * 2); + const maxValue = Math.max(...data.map(item => item.value)); + const chartHeight = canvas.height - 40; + + data.forEach((item, index) => { + const barHeight = (item.value / maxValue) * chartHeight; + const x = (index * 2 + 0.5) * barWidth; + const y = canvas.height - barHeight - 20; + + // 绘制柱子 + const gradient = ctx.createLinearGradient(x, y, x, y + barHeight); + gradient.addColorStop(0, '#10b981'); + gradient.addColorStop(1, '#3b82f6'); + + ctx.fillStyle = gradient; + ctx.fillRect(x, y, barWidth, barHeight); + + // 绘制数值 + ctx.fillStyle = '#333'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(item.value + '%', x + barWidth / 2, y - 5); + }); +} + +// 初始化时间线 +function initTimeline() { + const timelineItems = document.querySelectorAll('.timeline-item'); + + const timelineObserver = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('active'); + } + }); + }, { threshold: 0.5 }); + + timelineItems.forEach(item => { + timelineObserver.observe(item); + }); +} + +// 交互元素初始化 +function initInteractiveElements() { + // 标签页切换 + const tabs = document.querySelectorAll('.tab'); + const tabContents = document.querySelectorAll('.tab-content'); + + tabs.forEach(tab => { + tab.addEventListener('click', () => { + const target = tab.dataset.tab; + + // 切换标签状态 + tabs.forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + + // 切换内容显示 + tabContents.forEach(content => { + if (content.id === target) { + content.classList.add('active'); + content.style.display = 'block'; + } else { + content.classList.remove('active'); + content.style.display = 'none'; + } + }); + }); + }); + + // 手风琴效果 + const accordionHeaders = document.querySelectorAll('.accordion-header'); + + accordionHeaders.forEach(header => { + header.addEventListener('click', () => { + const content = header.nextElementSibling; + const isOpen = header.classList.contains('active'); + + // 关闭其他项 + accordionHeaders.forEach(h => { + h.classList.remove('active'); + h.nextElementSibling.style.maxHeight = null; + }); + + // 切换当前项 + if (!isOpen) { + header.classList.add('active'); + content.style.maxHeight = content.scrollHeight + 'px'; + } + }); + }); + + // 模态框 + const modalTriggers = document.querySelectorAll('[data-modal]'); + const modals = document.querySelectorAll('.modal'); + const modalCloses = document.querySelectorAll('.modal-close'); + + modalTriggers.forEach(trigger => { + trigger.addEventListener('click', () => { + const modalId = trigger.dataset.modal; + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.add('open'); + document.body.style.overflow = 'hidden'; + } + }); + }); + + modalCloses.forEach(close => { + close.addEventListener('click', () => { + const modal = close.closest('.modal'); + modal.classList.remove('open'); + document.body.style.overflow = ''; + }); + }); + + // 点击背景关闭模态框 + modals.forEach(modal => { + modal.addEventListener('click', (e) => { + if (e.target === modal) { + modal.classList.remove('open'); + document.body.style.overflow = ''; + } + }); + }); + + // 工具提示 + const tooltips = document.querySelectorAll('[data-tooltip]'); + + tooltips.forEach(element => { + const tooltip = document.createElement('div'); + tooltip.classList.add('tooltip'); + tooltip.textContent = element.dataset.tooltip; + + element.addEventListener('mouseenter', () => { + document.body.appendChild(tooltip); + const rect = element.getBoundingClientRect(); + tooltip.style.left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + 'px'; + tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px'; + tooltip.classList.add('show'); + }); + + element.addEventListener('mouseleave', () => { + tooltip.classList.remove('show'); + setTimeout(() => { + tooltip.remove(); + }, 300); + }); + }); +} + +// 复制到剪贴板 +function copyToClipboard(text) { + const textarea = document.createElement('textarea'); + textarea.value = text; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + showSuccess('已复制到剪贴板'); +} + +// 分享功能 +function shareContent(platform) { + const url = encodeURIComponent(window.location.href); + const title = encodeURIComponent(document.title); + let shareUrl = ''; + + switch(platform) { + case 'wechat': + // 生成微信分享二维码 + generateQRCode(window.location.href); + break; + case 'weibo': + shareUrl = `https://service.weibo.com/share/share.php?url=${url}&title=${title}`; + break; + case 'linkedin': + shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${url}`; + break; + case 'twitter': + shareUrl = `https://twitter.com/intent/tweet?url=${url}&text=${title}`; + break; + } + + if (shareUrl) { + window.open(shareUrl, '_blank', 'width=600,height=400'); + } +} + +// 生成二维码 +function generateQRCode(text) { + const modal = document.createElement('div'); + modal.classList.add('qr-modal'); + modal.innerHTML = ` +
+

微信扫码分享

+
+ +
+ `; + document.body.appendChild(modal); + + // 这里可以集成实际的二维码生成库 + document.getElementById('qrcode').innerHTML = '二维码生成区域'; +} + +// 主题切换 +function initThemeToggle() { + const themeToggle = document.getElementById('themeToggle'); + const currentTheme = localStorage.getItem('theme') || 'light'; + + document.documentElement.setAttribute('data-theme', currentTheme); + + themeToggle?.addEventListener('click', () => { + const theme = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light'; + document.documentElement.setAttribute('data-theme', theme); + localStorage.setItem('theme', theme); + }); +} + +// 导出为 PDF +function exportToPDF() { + window.print(); +} + +// 性能监控 +function initPerformanceMonitoring() { + // 页面加载时间 + window.addEventListener('load', () => { + const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart; + console.log(`页面加载时间: ${loadTime}ms`); + + // 发送到分析服务 + if (window.gtag) { + gtag('event', 'page_load_time', { + value: loadTime, + page_path: window.location.pathname + }); + } + }); + + // 监控长任务 + if ('PerformanceObserver' in window) { + const observer = new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + console.log('Long Task detected:', entry); + } + }); + + observer.observe({ entryTypes: ['longtask'] }); + } +} + +// 处理图片加载错误 +function handleImageErrors() { + const images = document.querySelectorAll('img'); + const defaultImage = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZTJlOGYwIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxOCIgZmlsbD0iIzk0YTNiOCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPuWbvueJh+WKoOi9veS4rTwvdGV4dD48L3N2Zz4='; + + images.forEach(img => { + // 添加错误处理 + img.addEventListener('error', function() { + console.warn('图片加载失败:', this.src); + // 设置默认占位图 + this.src = defaultImage; + this.alt = '图片加载失败'; + this.classList.add('image-error'); + }); + + // 添加加载成功处理 + img.addEventListener('load', function() { + this.classList.add('image-loaded'); + }); + }); +} + +// 初始化性能监控 +initPerformanceMonitoring(); +initThemeToggle(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/mobile-optimize.js b/web_frontend/web_result/order-classes/wenlu/js/mobile-optimize.js new file mode 100644 index 00000000..0c35973a --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/mobile-optimize.js @@ -0,0 +1,516 @@ +// 移动端优化组件 +(function() { + let isMobile = false; + let isTablet = false; + let currentOrientation = 'portrait'; + + // 检测设备类型 + function detectDevice() { + const userAgent = navigator.userAgent.toLowerCase(); + const screenWidth = window.innerWidth; + const screenHeight = window.innerHeight; + + // 检测是否为移动设备 + isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) || screenWidth <= 768; + isTablet = /ipad|android(?!.*mobile)|tablet/i.test(userAgent) || (screenWidth > 768 && screenWidth <= 1024); + + // 检测屏幕方向 + currentOrientation = screenWidth > screenHeight ? 'landscape' : 'portrait'; + + // 添加设备类到body(确保body存在) + if (document.body) { + document.body.classList.remove('mobile', 'tablet', 'desktop', 'portrait', 'landscape'); + document.body.classList.add( + isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop', + currentOrientation + ); + } + + return { isMobile, isTablet, currentOrientation }; + } + + // 添加移动端优化样式 + function addMobileStyles() { + const style = document.createElement('style'); + style.textContent = ` + /* 移动端基础优化 */ + * { + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + /* 允许文本选择 */ + p, h1, h2, h3, h4, h5, h6, span, div, article, section { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + } + + /* 移动端滚动优化 */ + html { + -webkit-overflow-scrolling: touch; + scroll-behavior: smooth; + } + + /* 移动端字体优化 */ + body.mobile { + font-size: 16px; /* 防止iOS缩放 */ + line-height: 1.6; + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + } + + /* 移动端容器优化 */ + .container { + padding-left: 1rem; + padding-right: 1rem; + } + + body.mobile .container { + padding-left: 1rem; + padding-right: 1rem; + } + + body.tablet .container { + padding-left: 2rem; + padding-right: 2rem; + } + + /* 移动端导航优化 */ + body.mobile #navbar { + padding: 0.75rem 1rem; + } + + body.mobile .nav-container { + padding: 0.75rem 0; + } + + /* 移动端按钮优化 */ + body.mobile button, + body.mobile .btn { + min-height: 44px; /* iOS推荐的最小触摸目标 */ + padding: 0.75rem 1.5rem; + font-size: 1rem; + } + + /* 移动端表单优化 */ + body.mobile input, + body.mobile textarea, + body.mobile select { + min-height: 44px; + padding: 0.75rem; + font-size: 16px; /* 防止iOS缩放 */ + border-radius: 8px; + } + + /* 移动端卡片优化 */ + body.mobile .card, + body.mobile .glass-card { + margin-bottom: 1rem; + padding: 1rem; + border-radius: 12px; + } + + /* 移动端文字优化 */ + body.mobile h1 { + font-size: 2rem; + line-height: 1.2; + margin-bottom: 1rem; + } + + body.mobile h2 { + font-size: 1.75rem; + line-height: 1.3; + margin-bottom: 0.75rem; + } + + body.mobile h3 { + font-size: 1.5rem; + line-height: 1.4; + margin-bottom: 0.5rem; + } + + body.mobile p { + font-size: 1rem; + line-height: 1.6; + margin-bottom: 1rem; + } + + /* 移动端网格优化 */ + body.mobile .grid { + grid-template-columns: 1fr; + gap: 1rem; + } + + body.mobile .grid-cols-2, + body.mobile .grid-cols-3, + body.mobile .grid-cols-4, + body.mobile .md\\:grid-cols-2, + body.mobile .md\\:grid-cols-3, + body.mobile .lg\\:grid-cols-3 { + grid-template-columns: 1fr; + } + + /* 平板端网格优化 */ + body.tablet .grid-cols-3, + body.tablet .lg\\:grid-cols-3 { + grid-template-columns: repeat(2, 1fr); + } + + /* 移动端间距优化 */ + body.mobile .py-16 { padding-top: 3rem; padding-bottom: 3rem; } + body.mobile .py-12 { padding-top: 2rem; padding-bottom: 2rem; } + body.mobile .py-8 { padding-top: 1.5rem; padding-bottom: 1.5rem; } + body.mobile .mb-12 { margin-bottom: 2rem; } + body.mobile .mb-8 { margin-bottom: 1.5rem; } + + /* 移动端图片优化 */ + body.mobile img { + max-width: 100%; + height: auto; + border-radius: 8px; + } + + /* 横屏优化 */ + body.mobile.landscape { + /* 横屏时减少垂直间距 */ + } + + body.mobile.landscape .py-16 { + padding-top: 2rem; + padding-bottom: 2rem; + } + + body.mobile.landscape h1 { + font-size: 1.75rem; + margin-bottom: 0.75rem; + } + + /* 移动端返回顶部按钮优化 */ + body.mobile #back-to-top { + bottom: 1.5rem; + right: 1.5rem; + } + + body.mobile .back-to-top-btn { + width: 3rem; + height: 3rem; + } + + /* 移动端页面加载器优化 */ + body.mobile #page-loader .w-20 { + width: 4rem; + height: 4rem; + } + + body.mobile #page-loader h1 { + font-size: 1.25rem; + } + + body.mobile #page-loader .w-64 { + width: 16rem; + } + + /* 防止页面缩放 */ + @media screen and (max-width: 768px) { + html { + zoom: 1; + -ms-zoom: 1; + -webkit-zoom: 1; + -moz-zoom: 1; + } + } + + /* 触摸设备特定优化 */ + @media (hover: none) and (pointer: coarse) { + /* 移除hover效果,使用active效果 */ + .hover\\:scale-110:hover { + transform: none; + } + + .hover\\:scale-110:active { + transform: scale(1.05); + } + + .hover\\:shadow-xl:hover { + box-shadow: none; + } + + .hover\\:shadow-xl:active { + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); + } + } + + /* 安全区域适配(iPhone X等带刘海屏的设备) */ + @supports (padding: max(0px)) { + body.mobile { + padding-left: max(1rem, env(safe-area-inset-left)); + padding-right: max(1rem, env(safe-area-inset-right)); + } + + body.mobile #navbar { + padding-left: max(1rem, env(safe-area-inset-left)); + padding-right: max(1rem, env(safe-area-inset-right)); + padding-top: max(0.75rem, env(safe-area-inset-top)); + } + + body.mobile #back-to-top { + right: max(1.5rem, env(safe-area-inset-right)); + bottom: max(1.5rem, env(safe-area-inset-bottom)); + } + } + `; + document.head.appendChild(style); + } + + // 优化图片加载 + function optimizeImages() { + const images = document.querySelectorAll('img'); + + images.forEach(img => { + // 添加懒加载 + if (!img.loading) { + img.loading = 'lazy'; + } + + // 添加错误处理 + img.addEventListener('error', function() { + this.style.display = 'none'; + }); + + // 移动端图片优化 + if (isMobile) { + // 移动端使用较小的图片 + const src = img.src; + if (src && !src.includes('placeholder')) { + // 这里可以添加图片压缩逻辑 + } + } + }); + } + + // 优化触摸事件 + function optimizeTouch() { + // 添加触摸反馈 + document.addEventListener('touchstart', function(e) { + const target = e.target; + if (target.tagName === 'BUTTON' || target.classList.contains('btn') || target.tagName === 'A') { + target.style.opacity = '0.7'; + } + }, { passive: true }); + + document.addEventListener('touchend', function(e) { + const target = e.target; + if (target.tagName === 'BUTTON' || target.classList.contains('btn') || target.tagName === 'A') { + setTimeout(() => { + target.style.opacity = ''; + }, 150); + } + }, { passive: true }); + + // 防止双击缩放 + let lastTouchEnd = 0; + document.addEventListener('touchend', function(event) { + const now = (new Date()).getTime(); + if (now - lastTouchEnd <= 300) { + event.preventDefault(); + } + lastTouchEnd = now; + }, false); + } + + // 处理屏幕方向变化 + function handleOrientationChange() { + // 延迟执行以确保尺寸更新 + setTimeout(() => { + detectDevice(); + + // 重新计算布局 + const event = new Event('resize'); + window.dispatchEvent(event); + + // 滚动到顶部(防止方向变化后位置错乱) + if (window.scrollY > 100) { + window.scrollTo({ top: 0, behavior: 'smooth' }); + } + }, 100); + } + + // 优化键盘弹出时的布局 + function handleKeyboard() { + if (!isMobile) return; + + const originalHeight = window.innerHeight; + let keyboardOpen = false; + + window.addEventListener('resize', function() { + const currentHeight = window.innerHeight; + const heightDifference = originalHeight - currentHeight; + + // 键盘弹出(高度减少超过150px) + if (heightDifference > 150 && !keyboardOpen) { + keyboardOpen = true; + document.body.classList.add('keyboard-open'); + + // 隐藏导航栏(为输入框腾出空间) + const navbar = document.getElementById('navbar'); + if (navbar) { + navbar.style.transform = 'translateY(-100%)'; + } + + // 隐藏返回顶部按钮 + const backToTop = document.getElementById('back-to-top'); + if (backToTop) { + backToTop.style.display = 'none'; + } + } + // 键盘收起 + else if (heightDifference <= 150 && keyboardOpen) { + keyboardOpen = false; + document.body.classList.remove('keyboard-open'); + + // 恢复导航栏 + const navbar = document.getElementById('navbar'); + if (navbar) { + navbar.style.transform = ''; + } + + // 恢复返回顶部按钮 + const backToTop = document.getElementById('back-to-top'); + if (backToTop) { + backToTop.style.display = ''; + } + } + }); + } + + // 性能优化 + function optimizePerformance() { + // 使用 Intersection Observer 优化动画 + if ('IntersectionObserver' in window) { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animate-visible'); + } + }); + }, { + threshold: 0.1, + rootMargin: '50px' + }); + + // 观察所有动画元素 + document.querySelectorAll('[class*="animate-"]').forEach(el => { + if (!el.classList.contains('animate-visible')) { + observer.observe(el); + } + }); + } + + // 移动端减少动画 + if (isMobile) { + const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + if (mediaQuery.matches) { + document.body.classList.add('reduce-motion'); + } + } + } + + // 添加调试信息(开发环境) + function addDebugInfo() { + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + const debugInfo = document.createElement('div'); + debugInfo.style.cssText = ` + position: fixed; + top: 70px; + right: 10px; + background: rgba(0,0,0,0.8); + color: white; + padding: 8px; + border-radius: 4px; + font-size: 12px; + z-index: 10000; + font-family: monospace; + `; + + function updateDebugInfo() { + const { isMobile, isTablet, currentOrientation } = detectDevice(); + debugInfo.innerHTML = ` + ${window.innerWidth}×${window.innerHeight}
+ ${isMobile ? 'Mobile' : isTablet ? 'Tablet' : 'Desktop'}
+ ${currentOrientation} + `; + } + + updateDebugInfo(); + document.body.appendChild(debugInfo); + + window.addEventListener('resize', updateDebugInfo); + window.addEventListener('orientationchange', updateDebugInfo); + } + } + + // 初始化移动端优化 + function init() { + // 检测设备类型 + detectDevice(); + + // 添加移动端样式 + addMobileStyles(); + + // 优化触摸体验 + optimizeTouch(); + + // 处理屏幕方向变化 + window.addEventListener('orientationchange', handleOrientationChange); + window.addEventListener('resize', () => { + setTimeout(detectDevice, 100); + }); + + // 页面加载完成后的优化 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + setTimeout(() => { + optimizeImages(); + handleKeyboard(); + optimizePerformance(); + addDebugInfo(); + }, 500); + }); + } else { + setTimeout(() => { + optimizeImages(); + handleKeyboard(); + optimizePerformance(); + addDebugInfo(); + }, 500); + } + + // 监听页面加载完成事件 + document.addEventListener('pageLoaded', () => { + setTimeout(() => { + optimizeImages(); + optimizePerformance(); + }, 100); + }); + } + + // 提供外部接口 + window.MobileOptimize = { + detectDevice, + isMobile: () => isMobile, + isTablet: () => isTablet, + getCurrentOrientation: () => currentOrientation, + optimizeImages, + optimizePerformance + }; + + // 初始化 + init(); +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/nav-component.js b/web_frontend/web_result/order-classes/wenlu/js/nav-component.js new file mode 100644 index 00000000..64e8a2e1 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/nav-component.js @@ -0,0 +1,270 @@ +// 导航组件 - 统一管理所有页面的导航栏 +(function() { + // 检测当前页面路径,自动调整链接 + const currentPath = window.location.pathname; + const isInPagesFolder = currentPath.includes('/pages/'); + const currentPage = currentPath.split('/').pop() || 'index.html'; + + // 导航项配置 + const navItems = [ + { href: 'index.html', icon: 'fa-home', text: '首页', id: 'index' }, + { href: 'overview.html', icon: 'fa-info-circle', text: '展会概览', id: 'overview' }, + { href: 'exhibition.html', icon: 'fa-th-large', text: '展览内容', id: 'exhibition' }, + { href: 'operation.html', icon: 'fa-drafting-compass', text: '布局设计', id: 'operation' }, + { href: 'marketing.html', icon: 'fa-bullhorn', text: '营销推广', id: 'marketing' }, + { href: 'budget.html', icon: 'fa-chart-pie', text: '预算分析', id: 'budget' }, + { href: 'risk.html', icon: 'fa-shield-alt', text: '风险评估', id: 'risk' } + ]; + + // 根据当前页面位置调整路径 + function getCorrectPath(href) { + // 移除可能存在的 pages/ 前缀 + if (href.startsWith('pages/')) { + return href.substring(6); // 移除 'pages/' 前缀 + } + return href; + } + + // 判断是否为当前激活页面 + function isActive(href) { + return currentPage === href || + (currentPage === '' && href === 'index.html'); + } + + // 生成导航HTML + function generateNavHTML() { + return ` + + + + +
+
+ +
+

NEVIT 2024

+

新能源汽车产业博览会

+
+
+ ${navItems.map(item => ` + + ${item.text} + + `).join('')} +
+
+

2024年9月12-15日

+

国家会展中心(上海)

+
+
+
`; + } + + // 添加样式 + function addStyles() { + const style = document.createElement('style'); + style.textContent = ` + .nav-container { + transition: all 0.3s ease; + } + .navbar-scrolled { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + } + .navbar-scrolled .nav-container { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + } + .nav-logo { + animation: fadeInLeft 0.6s ease-out; + } + .nav-link { + position: relative; + padding: 0.5rem 0; + font-weight: 500; + } + .menu-icon { + width: 24px; + height: 20px; + position: relative; + display: block; + cursor: pointer; + } + .menu-icon span { + display: block; + position: absolute; + width: 100%; + height: 2px; + background-color: #374151; + transition: all 0.3s ease; + } + .menu-icon span:nth-child(1) { + top: 0; + } + .menu-icon span:nth-child(2) { + top: 50%; + transform: translateY(-50%); + } + .menu-icon span:nth-child(3) { + bottom: 0; + } + .menu-open .menu-icon span:nth-child(1) { + transform: rotate(45deg) translate(6px, 6px); + } + .menu-open .menu-icon span:nth-child(2) { + opacity: 0; + } + .menu-open .menu-icon span:nth-child(3) { + transform: rotate(-45deg) translate(6px, -6px); + } + .mobile-menu-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + z-index: 40; + opacity: 0; + transition: opacity 0.3s ease; + } + .mobile-menu-overlay.show { + opacity: 1; + } + .mobile-menu.show { + transform: translateX(0); + } + @keyframes fadeInLeft { + from { + opacity: 0; + transform: translateX(-20px); + } + to { + opacity: 1; + transform: translateX(0); + } + } + @media (max-width: 768px) { + .nav-container { + padding: 1rem 1.5rem; + } + } + `; + document.head.appendChild(style); + } + + // 初始化导航 + function initNav() { + const navElement = document.querySelector('nav') || document.getElementById('navbar'); + if (navElement) { + // 统一设置导航栏样式 + navElement.className = 'fixed top-0 w-full bg-white z-50 transition-all duration-300'; + navElement.id = 'navbar'; + + // 添加样式 + addStyles(); + + // 更新导航内容 + navElement.innerHTML = generateNavHTML(); + + // 添加滚动效果 + let lastScrollTop = 0; + window.addEventListener('scroll', function() { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + + // 添加滚动阴影 + if (scrollTop > 50) { + navElement.classList.add('navbar-scrolled'); + } else { + navElement.classList.remove('navbar-scrolled'); + } + + lastScrollTop = scrollTop; + }); + + // 移动端菜单交互 + const mobileMenuButton = document.getElementById('mobile-menu-button'); + const mobileMenu = document.getElementById('mobile-menu'); + const mobileMenuOverlay = document.getElementById('mobile-menu-overlay'); + const closeMenuButton = document.getElementById('close-menu'); + + if (mobileMenuButton && mobileMenu) { + // 打开菜单 + mobileMenuButton.addEventListener('click', function() { + mobileMenu.classList.add('show'); + if (mobileMenuOverlay) { + mobileMenuOverlay.classList.remove('hidden'); + setTimeout(function() { + mobileMenuOverlay.classList.add('show'); + }, 10); + } + mobileMenuButton.classList.add('menu-open'); + document.body.style.overflow = 'hidden'; + }); + + // 关闭菜单功能 + const closeMenu = function() { + mobileMenu.classList.remove('show'); + if (mobileMenuOverlay) { + mobileMenuOverlay.classList.remove('show'); + setTimeout(function() { + mobileMenuOverlay.classList.add('hidden'); + }, 300); + } + mobileMenuButton.classList.remove('menu-open'); + document.body.style.overflow = ''; + }; + + // 点击关闭按钮 + closeMenuButton.addEventListener('click', closeMenu); + + // 点击遮罩关闭 + mobileMenuOverlay.addEventListener('click', closeMenu); + + // 点击菜单项后关闭 + mobileMenu.querySelectorAll('a').forEach(function(link) { + link.addEventListener('click', closeMenu); + }); + } + } + } + + // DOM加载完成后初始化 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initNav); + } else { + initNav(); + } +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/order-class-handler.js b/web_frontend/web_result/order-classes/wenlu/js/order-class-handler.js new file mode 100644 index 00000000..c2ba40e2 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/order-class-handler.js @@ -0,0 +1,253 @@ +// 处理订单班参数和动态内容加载 +(function() { + // 订单班配置 + const orderClassConfig = { + 'tourism': { + name: '文旅', + title: '2024长三角国际新能源汽车与智能交通产业博览会', + subtitle: '新能源汽车产业博览会', + icon: 'fa-charging-station', + color: 'emerald' + }, + 'food': { + name: '食品', + title: '2024国际健康食品与轻食产业博览会', + subtitle: '健康食品轻食博览会', + icon: 'fa-utensils', + color: 'orange' + }, + 'finance': { + name: '财经商贸', + title: '2024国际美妆产业与电商创新博览会', + subtitle: '美妆电商博览会', + icon: 'fa-chart-line', + color: 'blue' + }, + 'dev': { + name: '智能开发', + title: '2024国际教育科技与智慧学习博览会', + subtitle: '教育科技博览会', + icon: 'fa-code', + color: 'purple' + }, + 'manufacturing': { + name: '智能制造', + title: '2024国际智能制造与工业自动化博览会', + subtitle: '智能制造博览会', + icon: 'fa-industry', + color: 'gray' + }, + 'design': { + name: '视觉设计', + title: '2024国际文创设计与视觉艺术博览会', + subtitle: '文创设计博览会', + icon: 'fa-palette', + color: 'pink' + }, + 'logistics': { + name: '交通物流', + title: '2024国际智慧物流与供应链博览会', + subtitle: '智慧物流博览会', + icon: 'fa-truck', + color: 'indigo' + }, + 'civil': { + name: '土木', + title: '2024国际建筑科技与绿色建造博览会', + subtitle: '绿色建筑博览会', + icon: 'fa-building', + color: 'yellow' + }, + 'health': { + name: '大健康', + title: '2024国际大健康产业与医疗创新博览会', + subtitle: '大健康博览会', + icon: 'fa-heartbeat', + color: 'red' + }, + 'energy': { + name: '能源', + title: '2024国际新能源与光伏产业博览会', + subtitle: '新能源博览会', + icon: 'fa-solar-panel', + color: 'green' + }, + 'chemical': { + name: '化工', + title: '2024国际新材料与精细化工博览会', + subtitle: '新材料博览会', + icon: 'fa-flask', + color: 'teal' + }, + 'environment': { + name: '环保', + title: '2024国际环保科技与生态治理博览会', + subtitle: '环保科技博览会', + icon: 'fa-leaf', + color: 'lime' + } + }; + + // 获取URL参数 + function getUrlParam(name) { + const params = new URLSearchParams(window.location.search); + return params.get(name); + } + + // 获取当前订单班 + function getCurrentOrderClass() { + const orderClass = getUrlParam('orderClass'); + return orderClass && orderClassConfig[orderClass] ? orderClass : 'tourism'; + } + + // 更新页面标题 + function updatePageTitle(config) { + const titleElement = document.querySelector('title'); + if (titleElement) { + titleElement.textContent = config.title; + } + + // 更新页面中的标题 + const pageTitleElements = document.querySelectorAll('[data-order-title]'); + pageTitleElements.forEach(el => { + el.textContent = config.title; + }); + + // 更新副标题 + const subtitleElements = document.querySelectorAll('[data-order-subtitle]'); + subtitleElements.forEach(el => { + el.textContent = config.subtitle; + }); + } + + // 更新导航栏 + function updateNavigation(config) { + const navbar = document.getElementById('navbar'); + if (!navbar) return; + + // 更新导航栏logo和标题 + const logoIcon = navbar.querySelector('.logo-icon i'); + const logoTitle = navbar.querySelector('.nav-logo h1'); + const logoSubtitle = navbar.querySelector('.nav-logo p'); + + if (logoIcon) { + logoIcon.className = `fas ${config.icon} text-white`; + } + + if (logoTitle) { + logoTitle.textContent = config.name + ' 2024'; + } + + if (logoSubtitle) { + logoSubtitle.textContent = config.subtitle; + } + + // 更新颜色主题 + updateColorTheme(config.color); + } + + // 更新颜色主题 + function updateColorTheme(color) { + // 移除所有可能的颜色类 + const colors = ['emerald', 'orange', 'blue', 'purple', 'gray', 'pink', 'indigo', 'yellow', 'red', 'green', 'teal', 'lime']; + colors.forEach(c => { + document.documentElement.classList.remove(`theme-${c}`); + }); + + // 添加新的颜色类 + document.documentElement.classList.add(`theme-${color}`); + + // 动态创建CSS变量 + const style = document.createElement('style'); + style.id = 'theme-colors'; + + // 移除旧的样式 + const oldStyle = document.getElementById('theme-colors'); + if (oldStyle) { + oldStyle.remove(); + } + + // 定义颜色映射 + const colorMap = { + emerald: { primary: '#10b981', secondary: '#34d399', accent: '#6ee7b7' }, + orange: { primary: '#f97316', secondary: '#fb923c', accent: '#fdba74' }, + blue: { primary: '#3b82f6', secondary: '#60a5fa', accent: '#93bbfc' }, + purple: { primary: '#8b5cf6', secondary: '#a78bfa', accent: '#c4b5fd' }, + gray: { primary: '#6b7280', secondary: '#9ca3af', accent: '#d1d5db' }, + pink: { primary: '#ec4899', secondary: '#f472b6', accent: '#f9a8d4' }, + indigo: { primary: '#6366f1', secondary: '#818cf8', accent: '#a5b4fc' }, + yellow: { primary: '#eab308', secondary: '#facc15', accent: '#fde047' }, + red: { primary: '#ef4444', secondary: '#f87171', accent: '#fca5a5' }, + green: { primary: '#22c55e', secondary: '#4ade80', accent: '#86efac' }, + teal: { primary: '#14b8a6', secondary: '#2dd4bf', accent: '#5eead4' }, + lime: { primary: '#84cc16', secondary: '#a3e635', accent: '#bef264' } + }; + + const colors = colorMap[color] || colorMap.emerald; + + style.textContent = ` + :root { + --color-primary: ${colors.primary}; + --color-secondary: ${colors.secondary}; + --color-accent: ${colors.accent}; + } + + .bg-gradient-primary { + background: linear-gradient(135deg, ${colors.primary} 0%, ${colors.secondary} 100%); + } + + .text-primary { color: ${colors.primary}; } + .bg-primary { background-color: ${colors.primary}; } + .border-primary { border-color: ${colors.primary}; } + + .nav-link.active { color: ${colors.primary}; } + .nav-link.active span { background-color: ${colors.primary}; } + .nav-link:hover { color: ${colors.primary}; } + .nav-link span { background-color: ${colors.primary}; } + + .logo-icon { + background: linear-gradient(to bottom right, ${colors.secondary}, ${colors.primary}); + } + `; + + document.head.appendChild(style); + } + + // 加载订单班特定内容 + function loadOrderClassContent(orderClass) { + // 这里可以根据订单班加载不同的内容 + // 例如,加载不同的图片、文本等 + console.log(`Loading content for order class: ${orderClass}`); + + // 触发自定义事件,让其他组件知道订单班已改变 + window.dispatchEvent(new CustomEvent('orderClassChanged', { + detail: { orderClass, config: orderClassConfig[orderClass] } + })); + } + + // 初始化 + function init() { + const orderClass = getCurrentOrderClass(); + const config = orderClassConfig[orderClass]; + + if (config) { + updatePageTitle(config); + updateNavigation(config); + loadOrderClassContent(orderClass); + } + } + + // DOM加载完成后初始化 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + + // 暴露全局函数供其他模块使用 + window.OrderClassHandler = { + getCurrentOrderClass, + getConfig: () => orderClassConfig[getCurrentOrderClass()], + getAllConfigs: () => orderClassConfig + }; +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/page-loader.js b/web_frontend/web_result/order-classes/wenlu/js/page-loader.js new file mode 100644 index 00000000..51c7805d --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/page-loader.js @@ -0,0 +1,387 @@ +// 页面加载动画组件 +(function() { + // 创建加载器HTML结构 + function createLoader() { + const loaderHTML = ` +
+ +
+ +
+ + +
+ ${Array.from({length: 12}).map((_, i) => ` +
+ `).join('')} +
+ + +
+
+
+
+
+
+ + +
+ +
+
+ +
+
+ + +
+
+ 新 +
+
+
+ + +

+ 长三角国际新能源汽车博览会 +

+

+ 智行未来,绿动长三角 +

+
+ + +
+
+
+
+
+ 加载中 + 0% +
+
+ + +
+ 正在准备展会信息 + + . + . + . + +
+
+
+ `; + + // 将加载器插入到 body 的最前面,确保body存在 + if (document.body) { + document.body.insertAdjacentHTML('afterbegin', loaderHTML); + } else { + // 如果body不存在,等待DOM加载完成 + document.addEventListener('DOMContentLoaded', function() { + if (document.body && !document.getElementById('page-loader')) { + document.body.insertAdjacentHTML('afterbegin', loaderHTML); + } + }); + } + } + + // 添加样式 + function addStyles() { + const style = document.createElement('style'); + style.textContent = ` + /* 基础动画定义 */ + @keyframes spin-reverse { + from { transform: rotate(0deg); } + to { transform: rotate(-360deg); } + } + + @keyframes fade-in { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes fade-in-delay { + from { opacity: 0; transform: translateY(15px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes float { + 0%, 100% { transform: translateY(0px) rotate(0deg); } + 25% { transform: translateY(-10px) rotate(90deg); } + 50% { transform: translateY(-5px) rotate(180deg); } + 75% { transform: translateY(-15px) rotate(270deg); } + } + + @keyframes dot-blink { + 0%, 20% { opacity: 0; } + 50% { opacity: 1; } + 100% { opacity: 0; } + } + + @keyframes shape-float { + 0%, 100% { transform: translateY(0px) translateX(0px) rotate(0deg); } + 33% { transform: translateY(-20px) translateX(10px) rotate(120deg); } + 66% { transform: translateY(10px) translateX(-10px) rotate(240deg); } + } + + @keyframes particle-drift { + 0% { transform: translateY(100vh) translateX(0px) rotate(0deg); opacity: 0; } + 10% { opacity: 1; } + 90% { opacity: 1; } + 100% { transform: translateY(-100px) translateX(50px) rotate(360deg); opacity: 0; } + } + + /* 应用动画类 */ + .animate-spin-reverse { + animation: spin-reverse 2s linear infinite; + } + + .animate-fade-in { + animation: fade-in 1s ease-out forwards; + } + + .animate-fade-in-delay { + animation: fade-in-delay 1s ease-out 0.3s forwards; + opacity: 0; + } + + /* 加载点动画 */ + .loading-dots { + display: inline-block; + } + + .loading-dots .dot { + animation: dot-blink 1.4s infinite; + } + + .loading-dots .dot:nth-child(2) { + animation-delay: 0.2s; + } + + .loading-dots .dot:nth-child(3) { + animation-delay: 0.4s; + } + + /* 浮动粒子 */ + .floating-particles { + position: absolute; + inset: 0; + overflow: hidden; + pointer-events: none; + } + + .particle { + position: absolute; + width: 6px; + height: 6px; + border-radius: 50%; + animation: particle-drift 8s infinite linear; + } + + .particle-1 { background: rgba(16, 185, 129, 0.6); left: 10%; animation-delay: 0s; animation-duration: 8s; } + .particle-2 { background: rgba(59, 130, 246, 0.6); left: 20%; animation-delay: 1s; animation-duration: 10s; } + .particle-3 { background: rgba(168, 85, 247, 0.6); left: 30%; animation-delay: 2s; animation-duration: 9s; } + .particle-4 { background: rgba(236, 72, 153, 0.6); left: 40%; animation-delay: 3s; animation-duration: 11s; } + .particle-5 { background: rgba(245, 158, 11, 0.6); left: 50%; animation-delay: 4s; animation-duration: 7s; } + .particle-6 { background: rgba(239, 68, 68, 0.6); left: 60%; animation-delay: 5s; animation-duration: 9s; } + .particle-7 { background: rgba(16, 185, 129, 0.6); left: 70%; animation-delay: 6s; animation-duration: 8s; } + .particle-8 { background: rgba(59, 130, 246, 0.6); left: 80%; animation-delay: 7s; animation-duration: 10s; } + .particle-9 { background: rgba(168, 85, 247, 0.6); left: 90%; animation-delay: 1.5s; animation-duration: 9s; } + .particle-10 { background: rgba(236, 72, 153, 0.6); left: 15%; animation-delay: 2.5s; animation-duration: 11s; } + .particle-11 { background: rgba(245, 158, 11, 0.6); left: 25%; animation-delay: 3.5s; animation-duration: 7s; } + .particle-12 { background: rgba(239, 68, 68, 0.6); left: 85%; animation-delay: 4.5s; animation-duration: 8s; } + + /* 几何图形 */ + .geometric-shapes { + position: absolute; + inset: 0; + overflow: hidden; + pointer-events: none; + } + + .shape { + position: absolute; + border-radius: 12px; + animation: shape-float 6s ease-in-out infinite; + } + + .shape-1 { + top: 20%; + left: 10%; + width: 40px; + height: 40px; + background: linear-gradient(45deg, rgba(16, 185, 129, 0.1), rgba(59, 130, 246, 0.1)); + animation-delay: 0s; + } + + .shape-2 { + top: 60%; + right: 15%; + width: 60px; + height: 60px; + background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(236, 72, 153, 0.1)); + border-radius: 50%; + animation-delay: 2s; + } + + .shape-3 { + bottom: 30%; + left: 20%; + width: 32px; + height: 32px; + background: linear-gradient(225deg, rgba(245, 158, 11, 0.1), rgba(239, 68, 68, 0.1)); + animation-delay: 4s; + } + + /* 加载器隐藏动画 */ + #page-loader.fade-out { + animation: fadeOutLoader 0.8s ease-in-out forwards; + } + + @keyframes fadeOutLoader { + 0% { + opacity: 1; + transform: scale(1); + } + 100% { + opacity: 0; + transform: scale(1.05); + } + } + + /* 防止页面滚动 */ + body.loading { + overflow: hidden; + } + `; + document.head.appendChild(style); + } + + // 更新加载进度 + function updateProgress(percentage, text) { + const progressBar = document.getElementById('loading-progress'); + const percentageText = document.getElementById('loading-percentage'); + const loadingText = document.getElementById('loading-text'); + + if (progressBar) { + progressBar.style.width = percentage + '%'; + } + if (percentageText) { + percentageText.textContent = percentage + '%'; + } + if (loadingText && text) { + loadingText.textContent = text; + } + } + + // 模拟加载过程 + function simulateLoading() { + const loadingSteps = [ + { progress: 0, text: '正在准备展会信息', delay: 100 }, + { progress: 15, text: '加载导航组件', delay: 200 }, + { progress: 30, text: '获取展会数据', delay: 300 }, + { progress: 45, text: '渲染页面布局', delay: 250 }, + { progress: 60, text: '加载图片资源', delay: 400 }, + { progress: 75, text: '初始化交互功能', delay: 200 }, + { progress: 85, text: '优化页面性能', delay: 150 }, + { progress: 95, text: '准备完成', delay: 200 }, + { progress: 100, text: '加载完成', delay: 300 } + ]; + + let currentStep = 0; + + function nextStep() { + if (currentStep < loadingSteps.length) { + const step = loadingSteps[currentStep]; + updateProgress(step.progress, step.text); + currentStep++; + setTimeout(nextStep, step.delay); + } else { + // 加载完成,延迟一点时间后隐藏加载器 + setTimeout(hideLoader, 500); + } + } + + // 开始加载过程 + setTimeout(nextStep, 300); + } + + // 隐藏加载器 + function hideLoader() { + const loader = document.getElementById('page-loader'); + if (loader) { + loader.classList.add('fade-out'); + + // 动画结束后移除元素并恢复页面滚动 + setTimeout(() => { + loader.remove(); + document.body.classList.remove('loading'); + + // 触发页面加载完成事件 + document.dispatchEvent(new CustomEvent('pageLoaded')); + }, 800); + } + } + + // 初始化加载器 + function init() { + // 确保DOM已加载 + if (!document.body) { + // 如果body还不存在,等待DOM加载完成 + document.addEventListener('DOMContentLoaded', init); + return; + } + + // 添加样式 + addStyles(); + + // 创建加载器 + createLoader(); + + // 禁用页面滚动 + if (document.body) { + document.body.classList.add('loading'); + } + + // 等待页面资源加载完成 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + setTimeout(simulateLoading, 200); + }); + } else { + setTimeout(simulateLoading, 200); + } + + // 监听 window load 事件(所有资源包括图片都加载完成) + window.addEventListener('load', () => { + // 如果加载器仍然存在,加速完成过程 + if (document.getElementById('page-loader')) { + updateProgress(100, '加载完成'); + setTimeout(hideLoader, 300); + } + }); + + // 监听页面可见性变化(用户切换标签页时暂停动画) + document.addEventListener('visibilitychange', function() { + const loader = document.getElementById('page-loader'); + if (loader) { + if (document.hidden) { + loader.style.animationPlayState = 'paused'; + } else { + loader.style.animationPlayState = 'running'; + } + } + }); + } + + // 提供外部接口 + window.PageLoader = { + updateProgress, + hideLoader + }; + + // 初始化 + init(); +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/performance-optimizer.js b/web_frontend/web_result/order-classes/wenlu/js/performance-optimizer.js new file mode 100644 index 00000000..e5d056d7 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/performance-optimizer.js @@ -0,0 +1,461 @@ +// 页面性能优化组件 +(function() { + let performanceData = {}; + let isOptimizing = false; + + // 性能监测 + function measurePerformance() { + if ('performance' in window) { + const navigation = performance.getEntriesByType('navigation')[0]; + const paint = performance.getEntriesByType('paint'); + + performanceData = { + // 页面加载时间 + domLoading: navigation.domContentLoadedEventEnd - navigation.navigationStart, + pageLoading: navigation.loadEventEnd - navigation.navigationStart, + + // 渲染时间 + firstPaint: paint.find(p => p.name === 'first-paint')?.startTime || 0, + firstContentfulPaint: paint.find(p => p.name === 'first-contentful-paint')?.startTime || 0, + + // 网络时间 + dnsTime: navigation.domainLookupEnd - navigation.domainLookupStart, + connectTime: navigation.connectEnd - navigation.connectStart, + requestTime: navigation.responseStart - navigation.requestStart, + downloadTime: navigation.responseEnd - navigation.responseStart, + + // 内存使用(如果支持) + memory: performance.memory ? { + used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024), + total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024), + limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024) + } : null + }; + } + + return performanceData; + } + + // 资源预加载优化 + function optimizeResourceLoading() { + // 根据当前页面位置调整路径 + const isInPagesFolder = window.location.pathname.includes('/pages/'); + const basePath = isInPagesFolder ? '../' : ''; + + // 预加载关键资源 + const criticalResources = [ + { href: basePath + 'css/styles.css', as: 'style' }, + { href: basePath + 'css/animations.css', as: 'style' }, + { href: basePath + 'js/nav-component.js', as: 'script' } + ]; + + criticalResources.forEach(resource => { + if (!document.querySelector(`link[href*="${resource.href.split('/').pop()}"]`)) { + const link = document.createElement('link'); + link.rel = 'preload'; + link.href = resource.href; + link.as = resource.as; + document.head.appendChild(link); + } + }); + } + + // 图片懒加载和优化 + function optimizeImages() { + // 创建 Intersection Observer 用于懒加载 + if ('IntersectionObserver' in window) { + const imageObserver = new IntersectionObserver((entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target; + + // 懒加载图片 + if (img.dataset.src) { + img.src = img.dataset.src; + img.removeAttribute('data-src'); + } + + // 添加加载状态 + img.classList.add('loading'); + + img.addEventListener('load', () => { + img.classList.remove('loading'); + img.classList.add('loaded'); + }); + + img.addEventListener('error', () => { + img.classList.add('error'); + img.style.display = 'none'; + }); + + observer.unobserve(img); + } + }); + }, { + rootMargin: '50px', + threshold: 0.01 + }); + + // 观察所有图片 + document.querySelectorAll('img[data-src], img:not([src])').forEach(img => { + imageObserver.observe(img); + }); + } + + // 为现有图片添加优化 + document.querySelectorAll('img').forEach(img => { + // 添加 loading="lazy" 属性 + if (!img.loading) { + img.loading = 'lazy'; + } + + // 添加 decode="async" 属性 + if (!img.decode) { + img.decode = 'async'; + } + + // 图片错误处理 + img.addEventListener('error', function() { + this.style.opacity = '0'; + this.style.transition = 'opacity 0.3s'; + }); + }); + } + + // CSS 优化 + function optimizeCSS() { + // 内联关键 CSS + const criticalCSS = ` + /* 关键渲染路径 CSS */ + body { font-family: 'Inter', sans-serif; margin: 0; padding: 0; } + #navbar { position: fixed; top: 0; width: 100%; z-index: 50; background: white; } + .container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; } + + /* 页面加载器样式 */ + #page-loader { position: fixed; inset: 0; z-index: 9999; background: white; display: flex; align-items: center; justify-content: center; } + `; + + // 添加关键 CSS 到 head + if (!document.getElementById('critical-css')) { + const style = document.createElement('style'); + style.id = 'critical-css'; + style.textContent = criticalCSS; + document.head.insertBefore(style, document.head.firstChild); + } + + // 异步加载非关键 CSS + const nonCriticalCSS = document.querySelectorAll('link[rel="stylesheet"]:not([data-critical])'); + nonCriticalCSS.forEach(link => { + if (link.href.includes('tailwind') || link.href.includes('font-awesome')) { + link.media = 'print'; + link.addEventListener('load', function() { + this.media = 'all'; + }); + } + }); + } + + // JavaScript 优化 + function optimizeJavaScript() { + // 延迟加载非关键脚本 + const deferScripts = [ + 'js/back-to-top.js', + 'js/mobile-optimize.js' + ]; + + deferScripts.forEach(src => { + const script = document.querySelector(`script[src*="${src}"]`); + if (script && !script.defer && !script.async) { + script.defer = true; + } + }); + + // 移除未使用的事件监听器(防止内存泄漏) + const cleanupEvents = () => { + // 移除已销毁元素的事件监听器 + document.querySelectorAll('[data-cleanup]').forEach(el => { + el.removeEventListener('click', null); + el.removeEventListener('scroll', null); + el.removeEventListener('resize', null); + }); + }; + + // 页面卸载时清理 + window.addEventListener('beforeunload', cleanupEvents); + } + + // 网络优化 + function optimizeNetwork() { + // DNS 预解析 + const domains = [ + 'fonts.googleapis.com', + 'cdnjs.cloudflare.com', + 'cdn.jsdelivr.net' + ]; + + domains.forEach(domain => { + if (!document.querySelector(`link[rel="dns-prefetch"][href*="${domain}"]`)) { + const link = document.createElement('link'); + link.rel = 'dns-prefetch'; + link.href = `https://${domain}`; + document.head.appendChild(link); + } + }); + + // 预连接到关键域名 + const preconnectDomains = ['fonts.gstatic.com']; + preconnectDomains.forEach(domain => { + if (!document.querySelector(`link[rel="preconnect"][href*="${domain}"]`)) { + const link = document.createElement('link'); + link.rel = 'preconnect'; + link.href = `https://${domain}`; + link.crossOrigin = ''; + document.head.appendChild(link); + } + }); + } + + // 缓存优化 + function optimizeCache() { + // Service Worker 注册(如果支持) + if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('./sw.js') + .then(registration => { + console.log('SW registered: ', registration); + }) + .catch(registrationError => { + console.log('SW registration failed: ', registrationError); + }); + }); + } + + // 本地存储优化 + try { + // 缓存静态数据 + if (localStorage) { + const cacheKey = 'page-cache-' + window.location.pathname; + const cacheTime = 30 * 60 * 1000; // 30分钟 + + const cachedData = localStorage.getItem(cacheKey); + if (cachedData) { + const { timestamp, data } = JSON.parse(cachedData); + if (Date.now() - timestamp < cacheTime) { + // 使用缓存数据 + console.log('Using cached data'); + } + } + } + } catch (e) { + console.warn('LocalStorage not available'); + } + } + + // 动画性能优化 + function optimizeAnimations() { + // 检测是否需要减少动画 + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + if (prefersReducedMotion) { + document.body.classList.add('reduce-motion'); + + // 添加减少动画的 CSS + const style = document.createElement('style'); + style.textContent = ` + .reduce-motion *, + .reduce-motion *::before, + .reduce-motion *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + `; + document.head.appendChild(style); + } + + // 使用 transform 和 opacity 进行动画(硬件加速) + document.querySelectorAll('[class*="animate-"]').forEach(el => { + el.style.willChange = 'transform, opacity'; + + // 动画完成后移除 will-change + el.addEventListener('animationend', function() { + this.style.willChange = 'auto'; + }, { once: true }); + }); + } + + // 内存优化 + function optimizeMemory() { + // 定期清理无用的 DOM 元素 + const cleanup = () => { + // 移除隐藏的元素(如果不再需要) + document.querySelectorAll('.hidden, [style*="display: none"]').forEach(el => { + if (el.dataset.keepHidden !== 'true') { + // 这里可以添加更智能的清理逻辑 + } + }); + }; + + // 每5分钟执行一次清理 + setInterval(cleanup, 5 * 60 * 1000); + + // 监听内存压力(如果支持) + if ('memory' in performance) { + const checkMemory = () => { + const used = performance.memory.usedJSHeapSize; + const limit = performance.memory.jsHeapSizeLimit; + const usage = used / limit; + + if (usage > 0.8) { + console.warn('High memory usage detected:', Math.round(usage * 100) + '%'); + cleanup(); + } + }; + + setInterval(checkMemory, 30000); // 每30秒检查一次 + } + } + + // 性能监控和报告 + function monitorPerformance() { + // 使用 Performance Observer 监控 + if ('PerformanceObserver' in window) { + // 监控 Long Tasks + const longTaskObserver = new PerformanceObserver(list => { + list.getEntries().forEach(entry => { + if (entry.duration > 50) { + console.warn('Long task detected:', entry.duration + 'ms'); + } + }); + }); + + try { + longTaskObserver.observe({ entryTypes: ['longtask'] }); + } catch (e) { + // longtask 不被支持 + } + + // 监控 Layout Shift + const clsObserver = new PerformanceObserver(list => { + list.getEntries().forEach(entry => { + if (entry.value > 0.1) { + console.warn('Layout shift detected:', entry.value); + } + }); + }); + + try { + clsObserver.observe({ entryTypes: ['layout-shift'] }); + } catch (e) { + // layout-shift 不被支持 + } + } + } + + // 添加性能优化样式 + function addPerformanceStyles() { + const style = document.createElement('style'); + style.textContent = ` + /* 硬件加速 */ + .gpu-accelerated { + transform: translateZ(0); + will-change: transform, opacity; + } + + /* 图片加载状态 */ + img.loading { + opacity: 0.3; + filter: blur(5px); + transition: opacity 0.3s, filter 0.3s; + } + + img.loaded { + opacity: 1; + filter: none; + } + + img.error { + opacity: 0; + display: none !important; + } + + /* 字体加载优化 */ + .font-loading { + font-display: swap; + } + + /* 减少重绘 */ + .contain-layout { + contain: layout; + } + + .contain-paint { + contain: paint; + } + + /* 滚动优化 */ + .scroll-smooth { + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; + } + `; + document.head.appendChild(style); + } + + // 初始化性能优化 + function init() { + if (isOptimizing) return; + isOptimizing = true; + + // 立即执行的优化 + optimizeResourceLoading(); + optimizeCSS(); + optimizeNetwork(); + addPerformanceStyles(); + + // DOM 加载完成后执行 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + optimizeImages(); + optimizeJavaScript(); + optimizeAnimations(); + monitorPerformance(); + }); + } else { + optimizeImages(); + optimizeJavaScript(); + optimizeAnimations(); + monitorPerformance(); + } + + // 页面完全加载后执行 + window.addEventListener('load', () => { + optimizeCache(); + optimizeMemory(); + + // 测量性能 + setTimeout(() => { + const perf = measurePerformance(); + console.log('Performance metrics:', perf); + + // 发送性能数据到分析系统(如果需要) + if (window.gtag) { + gtag('event', 'performance', { + 'page_load_time': Math.round(perf.pageLoading), + 'dom_load_time': Math.round(perf.domLoading) + }); + } + }, 1000); + }); + } + + // 提供外部接口 + window.PerformanceOptimizer = { + measurePerformance, + optimizeImages, + getPerformanceData: () => performanceData + }; + + // 初始化 + init(); +})(); \ No newline at end of file diff --git a/web_frontend/web_result/order-classes/wenlu/js/router.js b/web_frontend/web_result/order-classes/wenlu/js/router.js new file mode 100644 index 00000000..c27512b3 --- /dev/null +++ b/web_frontend/web_result/order-classes/wenlu/js/router.js @@ -0,0 +1,276 @@ +// 路由系统 - 支持多订单班结果展示 +class OrderClassRouter { + constructor() { + this.routes = { + 'wenlu': { + name: '文旅订单班', + path: '/result/wenlu', + template: 'wenlu-result.html', + title: '2024长三角国际新能源汽车与智能交通产业博览会', + agents: ['项目策划专家', '展会设计师', '市场营销专家', '运营管理专家', '财务分析师', '风险控制专家'] + }, + 'food': { + name: '食品订单班', + path: '/result/food', + template: 'food-result.html', + title: '中高端个性化轻食店铺经营方案', + agents: ['轻食店经营管理专家', '餐饮市场调研专家', '餐饮品牌设计专家', '菜品研发专家', '餐厅选址装修专家', '餐饮团队人员管理专家', '财务预算专家'] + } + }; + + this.currentRoute = null; + this.init(); + } + + init() { + // 监听URL变化 + window.addEventListener('popstate', () => this.handleRoute()); + + // 处理初始路由 + this.handleRoute(); + } + + handleRoute() { + const path = window.location.pathname; + const urlParams = new URLSearchParams(window.location.search); + const orderClass = urlParams.get('class') || this.getClassFromPath(path); + + if (orderClass && this.routes[orderClass]) { + this.loadRoute(orderClass); + } else { + // 默认加载文旅订单班 + this.loadRoute('wenlu'); + } + } + + getClassFromPath(path) { + // 从路径中提取订单班类型 + const match = path.match(/\/result\/(\w+)/); + return match ? match[1] : null; + } + + loadRoute(orderClass) { + const route = this.routes[orderClass]; + if (!route) return; + + this.currentRoute = orderClass; + + // 更新页面标题 + document.title = route.title; + + // 更新URL(不刷新页面) + const newUrl = `${window.location.origin}${route.path}`; + if (window.location.href !== newUrl) { + window.history.pushState({ orderClass }, '', newUrl); + } + + // 加载对应的内容 + this.loadContent(route); + } + + loadContent(route) { + // 检查是否有特定的内容容器 + const contentContainer = document.getElementById('order-class-content'); + if (!contentContainer) { + console.warn('未找到内容容器 #order-class-content'); + return; + } + + // 根据订单班类型加载不同的内容 + if (route.template) { + this.loadTemplate(route.template, contentContainer); + } else { + // 动态生成内容 + this.generateContent(route, contentContainer); + } + } + + async loadTemplate(templatePath, container) { + try { + const response = await fetch(`/pages/${templatePath}`); + if (response.ok) { + const html = await response.text(); + container.innerHTML = html; + this.initializePageScripts(); + } else { + this.generateContent(this.routes[this.currentRoute], container); + } + } catch (error) { + console.error('加载模板失败:', error); + this.generateContent(this.routes[this.currentRoute], container); + } + } + + generateContent(route, container) { + // 动态生成页面内容 + const html = ` +
+
+

${route.title}

+

基于AI Agent多角色协作生成的完整方案

+
+ +
+

参与的AI专家团队

+
+ ${route.agents.map(agent => ` +
+
+ ${agent} +
+

${agent}

+
+ `).join('')} +
+
+ +
+
+ +
+
+
+ `; + + container.innerHTML = html; + + // 加载具体内容 + this.loadOrderClassContent(route); + } + + async loadOrderClassContent(route) { + const contentDiv = document.getElementById('dynamic-content'); + if (!contentDiv) return; + + // 根据不同订单班加载不同内容 + if (this.currentRoute === 'food') { + await this.loadFoodContent(contentDiv); + } else if (this.currentRoute === 'wenlu') { + await this.loadWenluContent(contentDiv); + } + } + + async loadFoodContent(container) { + // 加载食品订单班的具体内容 + container.innerHTML = ` +
+ + +
+
+

市场调研分析

+
+
+ +
+

品牌设计方案

+
+
+ + + +
+

选址与装修设计

+
+
+ +
+

团队人员管理

+
+
+ +
+

财务预算规划

+
+
+ +
+

经营管理策略

+
+
+
+
+ `; + + // 加载各部分内容 + await this.loadFoodSectionContent(); + } + + async loadWenluContent(container) { + // 加载文旅订单班的内容(使用现有的页面) + container.innerHTML = ` +
+ + +
+ +
+
+ `; + } + + async loadFoodSectionContent() { + // 从markdown文件加载内容 + try { + const response = await fetch('/data/订单班文档资料/食品/notion文稿/中高端个性化轻食店铺经营方案 278d463fce51805081f7cfdc7280f4a4.md'); + if (response.ok) { + const markdown = await response.text(); + // 这里可以使用markdown解析器将内容转换为HTML + this.parseFoodMarkdown(markdown); + } + } catch (error) { + console.error('加载食品内容失败:', error); + } + } + + parseFoodMarkdown(markdown) { + // 简单的markdown解析(实际项目中建议使用专门的markdown解析库) + const sections = markdown.split(/^#{1,3}\s+/m); + // 分配内容到各个部分 + // 这里需要根据实际的markdown结构进行解析 + } + + initializePageScripts() { + // 初始化页面特定的脚本 + if (window.initOrderClassPage) { + window.initOrderClassPage(this.currentRoute); + } + } + + // 导航到指定订单班 + navigateTo(orderClass) { + if (this.routes[orderClass]) { + this.loadRoute(orderClass); + } + } +} + +// 初始化路由器 +document.addEventListener('DOMContentLoaded', () => { + window.orderClassRouter = new OrderClassRouter(); +}); + +// 导出给其他模块使用 +if (typeof module !== 'undefined' && module.exports) { + module.exports = OrderClassRouter; +} \ No newline at end of file