From 3ed48736b6db2dc259f01d553e7fdbdfc163bc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=88=90?= Date: Mon, 25 Aug 2025 15:57:54 +0800 Subject: [PATCH] 1 --- docs/LESS使用指南.md | 312 ++++++ docs/动画效果使用指南.md | 314 ++++++ docs/移动端适配指南.md | 307 ++++++ package.json | 2 + src/assets/css/animations.css | 785 --------------- src/assets/css/animations.less | 761 +++++++++++++++ src/assets/css/common.css | 39 - src/assets/css/common.less | 40 + src/assets/css/common_mobile.less | 933 ++++++++++++++++++ src/assets/css/content_pages.css | 1264 ------------------------- src/assets/css/content_pages.less | 251 +++++ src/assets/css/mobile_responsive.less | 602 ++++++++++++ src/assets/css/variables.less | 279 ++++++ src/components/ihead.vue | 47 + src/main.js | 8 +- src/static/scroll_helper.js | 250 +++++ src/views/index.vue | 16 +- vue.config.js | 21 + yarn.lock | 99 +- 19 files changed, 4233 insertions(+), 2097 deletions(-) create mode 100644 docs/LESS使用指南.md create mode 100644 docs/动画效果使用指南.md create mode 100644 docs/移动端适配指南.md delete mode 100644 src/assets/css/animations.css create mode 100644 src/assets/css/animations.less delete mode 100644 src/assets/css/common.css create mode 100644 src/assets/css/common.less create mode 100644 src/assets/css/common_mobile.less delete mode 100644 src/assets/css/content_pages.css create mode 100644 src/assets/css/content_pages.less create mode 100644 src/assets/css/mobile_responsive.less create mode 100644 src/assets/css/variables.less create mode 100644 src/static/scroll_helper.js diff --git a/docs/LESS使用指南.md b/docs/LESS使用指南.md new file mode 100644 index 0000000..5620467 --- /dev/null +++ b/docs/LESS使用指南.md @@ -0,0 +1,312 @@ +# LESS 使用指南 + +## 概述 + +本项目已全面从 CSS 转换为 LESS,使用 LESS 的嵌套结构、变量、混合等特性来提高代码的可维护性和复用性。 + +## 🎯 LESS 的优势 + +### 1. **嵌套结构** +- 更清晰的代码层次结构 +- 减少选择器重复 +- 提高代码可读性 + +### 2. **变量系统** +- 统一的颜色、字体、间距管理 +- 快速修改全局样式 +- 减少重复值 + +### 3. **混合(Mixins)** +- 可复用的样式块 +- 参数化样式 +- 减少代码重复 + +### 4. **函数和运算** +- 动态计算尺寸 +- 颜色混合和透明度 +- 响应式断点管理 + +## 📁 文件结构 + +``` +src/assets/css/ +├── variables.less # 全局变量定义 +├── common.less # 通用样式 +├── animations.less # 动画效果 +├── mobile_responsive.less # 移动端响应式 +└── common_mobile.less # 通用移动端适配 +``` + +## 🔧 已配置的 LESS 功能 + +### 1. **全局变量** +- 颜色系统(主色、成功、警告、错误等) +- 字体系统(大小、行高、字重) +- 间距系统(内边距、外边距) +- 断点系统(移动端、平板、桌面) +- 动画系统(时长、缓动、延迟) + +### 2. **响应式混合** +```less +.mobile({ + // 移动端样式 +}); + +.tablet({ + // 平板端样式 +}); + +.desktop({ + // 桌面端样式 +}); +``` + +### 3. **设备检测混合** +```less +.touch-device({ + // 触摸设备样式 +}); + +.high-dpi({ + // 高分辨率屏幕样式 +}); + +.landscape({ + // 横屏模式样式 +}); +``` + +## 📝 使用方法 + +### 1. **引入变量文件** +```less +@import './variables.less'; +``` + +### 2. **使用变量** +```less +.button { + background-color: @primary-color; + padding: @padding-md; + border-radius: @border-radius-base; + font-size: @font-size-base; +} +``` + +### 3. **嵌套结构** +```less +.card { + padding: @card-padding-base; + border: @border-width-base solid @border-color; + + .card-header { + padding: @padding-md; + border-bottom: @border-width-base solid @border-color-light; + + .card-title { + font-size: @font-size-h4; + color: @heading-color; + margin: 0; + } + } + + .card-body { + padding: @padding-md; + + .card-text { + color: @text-color; + line-height: @line-height-base; + } + } +} +``` + +### 4. **使用混合** +```less +.responsive-button { + .mobile({ + width: 100%; + padding: @padding-sm; + font-size: @font-size-sm; + }); + + .tablet({ + width: auto; + padding: @padding-md; + font-size: @font-size-base; + }); + + .desktop({ + padding: @padding-lg; + font-size: @font-size-lg; + }); +} +``` + +### 5. **颜色操作** +```less +.button { + background-color: @primary-color; + + &:hover { + background-color: lighten(@primary-color, 10%); + } + + &:active { + background-color: darken(@primary-color, 10%); + } + + &.disabled { + background-color: fade(@primary-color, 50%); + } +} +``` + +## 🎨 颜色系统 + +### 主色调 +- `@primary-color`: #3498db +- `@primary-light`: #5dade2 +- `@primary-dark`: #2980b9 +- `@primary-lighter`: #ebf3fd + +### 功能色 +- `@success-color`: #52c41a +- `@warning-color`: #faad14 +- `@error-color`: #f5222d +- `@info-color`: #1890ff + +### 中性色 +- `@white`: #ffffff +- `@black`: #000000 +- `@gray-1` 到 `@gray-10`: 从浅到深的灰色 + +## 📱 响应式断点 + +### 断点变量 +- `@screen-xs`: 480px +- `@screen-sm`: 576px +- `@screen-md`: 768px +- `@screen-lg`: 992px +- `@screen-xl`: 1200px +- `@screen-xxl`: 1600px + +### 设备断点 +- `@mobile-breakpoint`: 768px +- `@tablet-breakpoint`: 992px +- `@desktop-breakpoint`: 1200px + +## 🔄 动画系统 + +### 动画时长 +- `@animation-duration-base`: 0.2s +- `@animation-duration-fast`: 0.1s +- `@animation-duration-slow`: 0.3s +- `@animation-duration-slower`: 0.5s + +### 动画缓动 +- `@animation-timing-base`: ease +- `@animation-timing-ease-in`: ease-in +- `@animation-timing-ease-out`: ease-out +- `@animation-timing-ease-in-out`: ease-in-out +- `@animation-timing-linear`: linear + +## 🛠️ 开发工具 + +### 1. **Vue CLI 配置** +项目已配置 LESS 支持,包括: +- LESS 编译器 +- 全局变量注入 +- 自动前缀处理 + +### 2. **构建命令** +```bash +# 开发模式 +yarn serve + +# 生产构建 +yarn build +``` + +## 📋 最佳实践 + +### 1. **变量命名** +- 使用语义化名称 +- 遵循 BEM 命名规范 +- 保持命名一致性 + +### 2. **嵌套深度** +- 避免超过 3 层嵌套 +- 使用 `&` 符号引用父选择器 +- 保持选择器简洁 + +### 3. **混合使用** +- 创建可复用的混合 +- 参数化常用样式 +- 避免过度抽象 + +### 4. **文件组织** +- 按功能模块组织文件 +- 使用 `@import` 引入依赖 +- 保持文件结构清晰 + +## 🚀 性能优化 + +### 1. **变量缓存** +- 减少重复计算 +- 使用预定义值 +- 避免运行时计算 + +### 2. **选择器优化** +- 避免过深嵌套 +- 使用类选择器 +- 减少选择器复杂度 + +### 3. **媒体查询优化** +- 使用混合管理断点 +- 避免重复的媒体查询 +- 合并相似的样式规则 + +## 🔍 调试技巧 + +### 1. **浏览器开发工具** +- 查看编译后的 CSS +- 检查选择器优先级 +- 调试响应式样式 + +### 2. **LESS 编译错误** +- 检查语法错误 +- 验证变量引用 +- 确认文件路径 + +### 3. **样式冲突** +- 使用 `!important` 谨慎 +- 检查选择器特异性 +- 验证样式继承 + +## 📚 学习资源 + +### 1. **官方文档** +- [LESS 官网](http://lesscss.org/) +- [LESS 语言特性](http://lesscss.org/features/) +- [LESS 函数参考](http://lesscss.org/functions/) + +### 2. **在线工具** +- [LESS 在线编译器](http://lesscss.org/less-preview/) +- [LESS 语法检查器](https://github.com/less/less.js) + +### 3. **社区资源** +- [LESS GitHub](https://github.com/less/less.js) +- [LESS 论坛](https://github.com/less/less.js/discussions) + +## 🎉 总结 + +通过使用 LESS,我们实现了: + +1. **更好的代码组织**:嵌套结构和模块化文件 +2. **统一的样式管理**:全局变量和混合系统 +3. **高效的开发流程**:快速修改和样式复用 +4. **响应式设计**:断点管理和设备适配 +5. **维护性提升**:清晰的代码结构和语义化命名 + +LESS 为项目提供了强大的样式管理能力,让开发更加高效,维护更加简单。 diff --git a/docs/动画效果使用指南.md b/docs/动画效果使用指南.md new file mode 100644 index 0000000..54ee4b8 --- /dev/null +++ b/docs/动画效果使用指南.md @@ -0,0 +1,314 @@ +# 动画效果使用指南 + +## 概述 + +本项目已集成丰富的动画效果库,包含多种CSS动画、悬停效果、滚动触发动画等,让页面更加生动有趣。 + +## 🎯 已集成的动画类型 + +### 1. 基础动画类 + +#### 淡入动画 +- `.fade-in` - 淡入 +- `.fade-in-up` - 从下方淡入 +- `.fade-in-down` - 从上方淡入 +- `.fade-in-left` - 从左侧淡入 +- `.fade-in-right` - 从右侧淡入 + +#### 滑入动画 +- `.slide-in-up` - 从下方滑入 +- `.slide-in-down` - 从上方滑入 +- `.slide-in-left` - 从左侧滑入 +- `.slide-in-right` - 从右侧滑入 + +#### 缩放动画 +- `.zoom-in` - 缩放进入 +- `.zoom-out` - 缩放退出 + +#### 特殊效果动画 +- `.bounce` - 弹跳 +- `.pulse` - 脉冲 +- `.shake` - 抖动 +- `.swing` - 摇摆 +- `.rotate-in` - 旋转进入 +- `.flip-in-x` - X轴翻转 +- `.flip-in-y` - Y轴翻转 +- `.light-speed-in` - 光速进入 +- `.rubber-band` - 橡皮筋 +- `.wobble` - 摇摆 +- `.tada` - 庆祝 +- `.heart-beat` - 心跳 +- `.hinge` - 铰链 +- `.roll-in` - 滚入 +- `.roll-out` - 滚出 + +### 2. 悬停动画效果 + +- `.hover-lift` - 悬停时上浮 +- `.hover-scale` - 悬停时缩放 +- `.hover-rotate` - 悬停时旋转 +- `.hover-glow` - 悬停时发光 +- `.hover-bounce` - 悬停时弹跳 + +### 3. 滚动触发动画 + +- `.animate-on-scroll` - 滚动时触发动画 +- `.animate-stagger` - 交错动画容器 + +### 4. 高级动画效果 + +- `.gradient-bg` - 渐变背景动画 +- `.neon-text` - 霓虹灯文字效果 +- `.wave` - 波浪效果 +- `.particles` - 粒子效果 +- `.flip-card` - 3D翻转卡片 +- `.typewriter` - 打字机效果 + +## 🚀 使用方法 + +### 基础用法 + +```html + +
这个元素会从下方淡入
+ + +
快速动画
+
慢速动画
+ + +
延迟1秒
+
延迟2秒
+ + +
无限脉冲
+``` + +### 悬停效果 + +```html + +
悬停时会上浮
+ + +
悬停时会缩放
+ + +
悬停时会发光
+``` + +### 滚动触发动画 + +```html + +
滚动到此处时显示
+ + +
+
第一个元素
+
第二个元素
+
第三个元素
+
+``` + +### 组合使用 + +```html + +
+ 延迟淡入 + 悬停上浮 +
+ + +
+
悬停缩放
+
悬停发光
+
悬停旋转
+
+``` + +## 🎨 自定义动画 + +### 创建新的关键帧动画 + +```css +@keyframes customAnimation { + 0% { + opacity: 0; + transform: scale(0.5); + } + 50% { + opacity: 0.5; + transform: scale(1.2); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +.custom-animate { + animation-name: customAnimation; + animation-duration: 2s; + animation-timing-function: ease-in-out; +} +``` + +### 自定义悬停效果 + +```css +.custom-hover { + transition: all 0.3s ease; +} + +.custom-hover:hover { + transform: translateY(-20px) rotate(10deg); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); +} +``` + +## 🔧 JavaScript 动画控制 + +### 使用动画助手 + +```javascript +// 手动触发动画 +window.animationHelper.triggerAnimation(element, 'fade-in'); + +// 添加滚动动画 +window.animationHelper.addScrollAnimation(element, { + threshold: 0.2, + delay: 0.5 +}); + +// 添加交错动画 +window.animationHelper.addStaggerAnimation(container, { + delay: 0.2, + staggerDelay: 0.1 +}); +``` + +### 事件监听 + +```javascript +// 监听动画开始事件 +element.addEventListener('animationStart', (event) => { + console.log('动画开始:', event.detail); +}); + +// 监听动画触发事件 +element.addEventListener('animationTrigger', (event) => { + console.log('动画触发:', event.detail); +}); +``` + +## 📱 响应式动画 + +### 移动端优化 + +```css +/* 移动端减少动画复杂度 */ +@media (max-width: 768px) { + .animate { + animation-duration: 0.5s; + } + + .floating-particles { + display: none; + } +} +``` + +### 性能优化 + +```css +/* 使用 will-change 优化性能 */ +.animate { + will-change: transform, opacity; +} + +/* 使用 transform3d 启用硬件加速 */ +.animate { + transform: translate3d(0, 0, 0); +} +``` + +## 🎭 动画最佳实践 + +### 1. 适度使用 +- 不要过度使用动画,影响用户体验 +- 保持动画简洁明了 +- 考虑用户的动画偏好设置 + +### 2. 性能考虑 +- 使用 `transform` 和 `opacity` 属性 +- 避免在动画中改变布局属性 +- 合理使用 `will-change` 属性 + +### 3. 可访问性 +- 尊重用户的 `prefers-reduced-motion` 设置 +- 提供动画开关选项 +- 确保动画不影响内容可读性 + +### 4. 时机控制 +- 页面加载完成后延迟启动动画 +- 滚动触发动画使用 Intersection Observer +- 合理设置动画延迟和持续时间 + +## 🐛 常见问题 + +### Q: 动画不工作? +A: 检查是否正确引入了 `animations.css` 文件,确保元素有正确的类名。 + +### Q: 动画性能差? +A: 使用 `transform` 和 `opacity` 属性,避免改变布局属性,启用硬件加速。 + +### Q: 移动端动画卡顿? +A: 减少动画复杂度,使用更简单的动画效果,考虑禁用某些复杂动画。 + +### Q: 如何禁用动画? +A: 在CSS中添加 `animation: none !important;` 或使用 `prefers-reduced-motion` 媒体查询。 + +## 📚 示例代码 + +### 完整的动画页面示例 + +```html + +``` + +## 🎉 总结 + +通过合理使用这些动画效果,可以让您的页面更加生动有趣,提升用户体验。记住要适度使用,注重性能优化,并考虑可访问性需求。 diff --git a/docs/移动端适配指南.md b/docs/移动端适配指南.md new file mode 100644 index 0000000..235e235 --- /dev/null +++ b/docs/移动端适配指南.md @@ -0,0 +1,307 @@ +# 移动端适配指南 + +## 概述 + +本项目已全面适配移动端,包括响应式布局、触摸优化、性能优化等,确保在各种移动设备上都有良好的用户体验。 + +## 🎯 已实现的移动端适配 + +### 1. 响应式导航 +- **桌面端导航**:完整的主菜单和下拉菜单 +- **移动端导航**:汉堡菜单 + 全屏移动端菜单 +- **返回按钮**:移动端菜单中的返回功能,已修复显示问题 + +### 2. 响应式布局 +- **断点设置**: + - 移动端:≤768px + - 平板端:769px-1024px + - 桌面端:>1024px +- **网格系统**:自动从多列调整为单列 +- **容器适配**:边距和间距自动调整 + +### 3. 触摸设备优化 +- **触摸友好尺寸**:按钮最小高度44px +- **触摸友好间距**:菜单项间距优化 +- **触摸反馈**:移动端禁用悬停效果 + +### 4. 性能优化 +- **移动端动画简化**:减少复杂动画 +- **图片优化**:高分辨率屏幕支持 +- **硬件加速**:启用GPU加速 + +## 📱 移动端适配详情 + +### 头部导航适配 + +#### 桌面端导航 +```html +
+ +
+``` + +#### 移动端导航 +```html +
+ +
+ +
+ +
+``` + +#### 返回按钮修复 +```css +/* 返回按钮移动端适配 */ +.goBack { + margin-top: 30px; + padding-top: 20px; + border-top: 2px solid rgba(255, 255, 255, 0.3); +} + +.linkGoBack { + display: flex; + align-items: center; + color: white; + text-decoration: none; + font-size: 16px; + padding: 15px 0; +} + +.linkGoBack img { + width: 20px; + height: 20px; + margin-right: 10px; + filter: brightness(0) invert(1); +} +``` + +### 页面布局适配 + +#### 横幅区域 +```css +@media (max-width: 768px) { + .page-banner { + padding: 60px 0 40px !important; + } + + .banner-title { + font-size: 2.5rem !important; + line-height: 1.2 !important; + } + + .banner-subtitle { + font-size: 1.2rem !important; + line-height: 1.4 !important; + } +} +``` + +#### 网格布局 +```css +@media (max-width: 768px) { + .advantages-grid, + .areas-grid, + .services-grid { + grid-template-columns: 1fr !important; + gap: 20px !important; + } +} +``` + +#### 卡片组件 +```css +@media (max-width: 768px) { + .card, + .advantage-card, + .area-card { + padding: 25px 20px !important; + margin-bottom: 20px !important; + } +} +``` + +## 🔧 使用方法 + +### 1. 自动适配 +大部分页面会自动适配移动端,无需额外配置。 + +### 2. 手动添加移动端类 +```html + +
仅桌面端显示
+ + +
仅移动端显示
+ + +
自动适配边距
+ + +
自动调整列数
+``` + +### 3. 自定义断点 +```css +/* 自定义移动端断点 */ +@media (max-width: 600px) { + .custom-element { + font-size: 14px; + } +} +``` + +## 📐 断点规范 + +### 标准断点 +```css +/* 超小屏幕 */ +@media (max-width: 360px) { } + +/* 小屏幕手机 */ +@media (max-width: 480px) { } + +/* 移动端 */ +@media (max-width: 768px) { } + +/* 平板端 */ +@media (min-width: 769px) and (max-width: 1024px) { } + +/* 桌面端 */ +@media (min-width: 1025px) { } +``` + +### 特殊断点 +```css +/* 横屏模式 */ +@media (orientation: landscape) and (max-height: 500px) { } + +/* 触摸设备 */ +@media (hover: none) and (pointer: coarse) { } + +/* 高分辨率屏幕 */ +@media (-webkit-min-device-pixel-ratio: 2) { } + +/* 深色模式 */ +@media (prefers-color-scheme: dark) { } + +/* 减少动画偏好 */ +@media (prefers-reduced-motion: reduce) { } +``` + +## 🎨 移动端样式规范 + +### 1. 字体大小 +- **主标题**:移动端 2.5rem,小屏幕 2rem +- **副标题**:移动端 1.2rem,小屏幕 1rem +- **正文**:移动端 1rem,小屏幕 0.95rem + +### 2. 间距规范 +- **页面间距**:移动端 40px,小屏幕 30px +- **容器边距**:移动端 15px,小屏幕 10px +- **卡片内边距**:移动端 25px 20px,小屏幕 20px 15px + +### 3. 触摸优化 +- **按钮最小尺寸**:44px × 44px +- **触摸间距**:最小 8px +- **触摸反馈**:禁用悬停效果 + +## 🚀 性能优化建议 + +### 1. 移动端优化 +```css +/* 减少动画复杂度 */ +@media (max-width: 768px) { + .animate { + animation-duration: 0.5s; + } + + .floating-particles { + display: none; + } +} +``` + +### 2. 触摸设备优化 +```css +/* 触摸友好的交互 */ +@media (hover: none) and (pointer: coarse) { + .hover-effect { + display: none; + } + + .touch-friendly { + min-height: 44px; + } +} +``` + +### 3. 高分辨率优化 +```css +/* 高分辨率屏幕优化 */ +@media (-webkit-min-device-pixel-ratio: 2) { + .icon { + image-rendering: -webkit-optimize-contrast; + } +} +``` + +## 🐛 常见问题解决 + +### Q: 返回按钮在移动端不显示? +A: 已修复,现在返回按钮在移动端菜单中正确显示,带有白色图标和文字。 + +### Q: 移动端菜单无法关闭? +A: 支持多种关闭方式: +- 点击返回按钮 +- 点击菜单项 +- 点击外部区域 +- 按ESC键 + +### Q: 移动端动画卡顿? +A: 已优化: +- 减少动画复杂度 +- 禁用复杂粒子效果 +- 优化触摸设备性能 + +### Q: 如何自定义移动端样式? +A: 使用 `!important` 覆盖,或添加自定义断点: +```css +@media (max-width: 768px) { + .custom-element { + /* 自定义样式 */ + } +} +``` + +## 📱 测试建议 + +### 1. 设备测试 +- iPhone (各种尺寸) +- Android (各种尺寸) +- iPad (横竖屏) +- 桌面端 (各种分辨率) + +### 2. 功能测试 +- 导航菜单 +- 触摸交互 +- 滚动性能 +- 动画效果 + +### 3. 性能测试 +- 页面加载速度 +- 滚动流畅度 +- 内存使用 +- 电池消耗 + +## 🎉 总结 + +通过全面的移动端适配,项目现在支持: +- ✅ 响应式导航 +- ✅ 触摸友好交互 +- ✅ 性能优化 +- ✅ 多设备兼容 +- ✅ 无障碍支持 + +所有页面都已适配移动端,确保在各种设备上都有良好的用户体验! diff --git a/package.json b/package.json index 89cf847..6f36f43 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "@vue/cli-plugin-babel": "^3.0.3", "@vue/cli-service": "^3.0.3", "autoprefixer": "^10.4.21", + "less": "^4.4.1", + "less-loader": "7.3.0", "postcss-pxtorem": "^6.1.0", "vue-template-compiler": "^2.5.17" }, diff --git a/src/assets/css/animations.css b/src/assets/css/animations.css deleted file mode 100644 index 41cdcc0..0000000 --- a/src/assets/css/animations.css +++ /dev/null @@ -1,785 +0,0 @@ -/* 动画效果库 - 为项目添加丰富的动画效果 */ - -/* ===== 基础动画关键帧 ===== */ -@keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(30px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes fadeInDown { - from { - opacity: 0; - transform: translateY(-30px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes fadeInLeft { - from { - opacity: 0; - transform: translateX(-30px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes fadeInRight { - from { - opacity: 0; - transform: translateX(30px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -@keyframes slideInUp { - from { - transform: translateY(100px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes slideInDown { - from { - transform: translateY(-100px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -@keyframes slideInLeft { - from { - transform: translateX(-100px); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } -} - -@keyframes slideInRight { - from { - transform: translateX(100px); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } -} - -@keyframes zoomIn { - from { - opacity: 0; - transform: scale(0.3); - } - 50% { - opacity: 1; - } - to { - opacity: 1; - transform: scale(1); - } -} - -@keyframes zoomOut { - from { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 1; - } - to { - opacity: 0; - transform: scale(0.3); - } -} - -@keyframes bounce { - 0%, 20%, 53%, 80%, 100% { - transform: translate3d(0, 0, 0); - } - 40%, 43% { - transform: translate3d(0, -30px, 0); - } - 70% { - transform: translate3d(0, -15px, 0); - } - 90% { - transform: translate3d(0, -4px, 0); - } -} - -@keyframes pulse { - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.05); - } - 100% { - transform: scale(1); - } -} - -@keyframes shake { - 0%, 100% { - transform: translateX(0); - } - 10%, 30%, 50%, 70%, 90% { - transform: translateX(-10px); - } - 20%, 40%, 60%, 80% { - transform: translateX(10px); - } -} - -@keyframes swing { - 20% { - transform: rotate(15deg); - } - 40% { - transform: rotate(-10deg); - } - 60% { - transform: rotate(5deg); - } - 80% { - transform: rotate(-5deg); - } - 100% { - transform: rotate(0deg); - } -} - -@keyframes rotateIn { - from { - transform: rotate(-200deg); - opacity: 0; - } - to { - transform: rotate(0); - opacity: 1; - } -} - -@keyframes flipInX { - from { - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - opacity: 0; - } - 40% { - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - } - 60% { - transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - } - 80% { - transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - } - to { - transform: perspective(400px); - opacity: 1; - } -} - -@keyframes flipInY { - from { - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - opacity: 0; - } - 40% { - transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - } - 60% { - transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - } - 80% { - transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - } - to { - transform: perspective(400px); - opacity: 1; - } -} - -@keyframes lightSpeedIn { - from { - transform: translate3d(100%, 0, 0) skewX(-30deg); - opacity: 0; - } - 60% { - transform: skewX(20deg); - opacity: 1; - } - 80% { - transform: skewX(-5deg); - } - to { - transform: translate3d(0, 0, 0); - opacity: 1; - } -} - -@keyframes rubberBand { - from { - transform: scale(1); - } - 30% { - transform: scaleX(1.25) scaleY(0.75); - } - 40% { - transform: scaleX(0.75) scaleY(1.25); - } - 50% { - transform: scaleX(1.15) scaleY(0.85); - } - 65% { - transform: scaleX(0.95) scaleY(1.05); - } - 75% { - transform: scaleX(1.05) scaleY(0.95); - } - to { - transform: scale(1); - } -} - -@keyframes wobble { - from { - transform: translate3d(0, 0, 0); - } - 15% { - transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - } - 30% { - transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - } - 45% { - transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - } - 60% { - transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - } - 75% { - transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - } - to { - transform: translate3d(0, 0, 0); - } -} - -@keyframes tada { - from { - transform: scale3d(1, 1, 1); - } - 10%, 20% { - transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); - } - 30%, 50%, 70%, 90% { - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - } - 40%, 60%, 80% { - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - } - to { - transform: scale3d(1, 1, 1); - } -} - -@keyframes heartBeat { - 0% { - transform: scale(1); - } - 14% { - transform: scale(1.3); - } - 28% { - transform: scale(1); - } - 42% { - transform: scale(1.3); - } - 70% { - transform: scale(1); - } -} - -@keyframes hinge { - 0% { - transform: rotate(0); - transform-origin: top left; - animation-timing-function: ease-in-out; - } - 20%, 60% { - transform: rotate(80deg); - transform-origin: top left; - animation-timing-function: ease-in-out; - } - 40% { - transform: rotate(60deg); - transform-origin: top left; - animation-timing-function: ease-in-out; - } - 80% { - transform: rotate(60deg) translateY(0); - transform-origin: top left; - animation-timing-function: ease-in-out; - } - 100% { - transform: translateY(700px); - } -} - -@keyframes rollIn { - from { - opacity: 0; - transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0); - } -} - -@keyframes rollOut { - from { - opacity: 1; - } - to { - opacity: 0; - transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - } -} - -/* ===== 动画类 ===== */ -.animate { - animation-duration: 1s; - animation-fill-mode: both; -} - -.animate.infinite { - animation-iteration-count: infinite; -} - -.animate.delay-1s { - animation-delay: 1s; -} - -.animate.delay-2s { - animation-delay: 2s; -} - -.animate.delay-3s { - animation-delay: 3s; -} - -.animate.delay-4s { - animation-delay: 4s; -} - -.animate.delay-5s { - animation-delay: 5s; -} - -.animate.fast { - animation-duration: 0.5s; -} - -.animate.slow { - animation-duration: 2s; -} - -.animate.slower { - animation-duration: 3s; -} - -/* ===== 淡入动画 ===== */ -.fade-in { - animation-name: fadeIn; -} - -.fade-in-up { - animation-name: fadeInUp; -} - -.fade-in-down { - animation-name: fadeInDown; -} - -.fade-in-left { - animation-name: fadeInLeft; -} - -.fade-in-right { - animation-name: fadeInRight; -} - -/* ===== 滑入动画 ===== */ -.slide-in-up { - animation-name: slideInUp; -} - -.slide-in-down { - animation-name: slideInDown; -} - -.slide-in-left { - animation-name: slideInLeft; -} - -.slide-in-right { - animation-name: slideInRight; -} - -/* ===== 缩放动画 ===== */ -.zoom-in { - animation-name: zoomIn; -} - -.zoom-out { - animation-name: zoomOut; -} - -/* ===== 特殊效果动画 ===== */ -.bounce { - animation-name: bounce; -} - -.pulse { - animation-name: pulse; -} - -.shake { - animation-name: shake; -} - -.swing { - animation-name: swing; -} - -.rotate-in { - animation-name: rotateIn; -} - -.flip-in-x { - animation-name: flipInX; -} - -.flip-in-y { - animation-name: flipInY; -} - -.light-speed-in { - animation-name: lightSpeedIn; -} - -.rubber-band { - animation-name: rubberBand; -} - -.wobble { - animation-name: wobble; -} - -.tada { - animation-name: tada; -} - -.heart-beat { - animation-name: heartBeat; -} - -.hinge { - animation-name: hinge; -} - -.roll-in { - animation-name: rollIn; -} - -.roll-out { - animation-name: rollOut; -} - -/* ===== 悬停动画 ===== */ -.hover-lift { - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.hover-lift:hover { - transform: translateY(-10px); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); -} - -.hover-scale { - transition: transform 0.3s ease; -} - -.hover-scale:hover { - transform: scale(1.05); -} - -.hover-rotate { - transition: transform 0.3s ease; -} - -.hover-rotate:hover { - transform: rotate(5deg); -} - -.hover-glow { - transition: box-shadow 0.3s ease; -} - -.hover-glow:hover { - box-shadow: 0 0 20px rgba(52, 152, 219, 0.5); -} - -.hover-bounce { - transition: transform 0.3s ease; -} - -.hover-bounce:hover { - animation: bounce 0.6s ease; -} - -/* ===== 滚动触发动画 ===== */ -.scroll-animate { - opacity: 0; - transform: translateY(30px); - transition: all 0.8s ease; -} - -.scroll-animate.animate-in { - opacity: 1; - transform: translateY(0); -} - -/* ===== 加载动画 ===== */ -.loading-spinner { - width: 40px; - height: 40px; - border: 4px solid #f3f3f3; - border-top: 4px solid #3498db; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.loading-dots { - display: inline-block; -} - -.loading-dots::after { - content: ''; - animation: dots 1.5s steps(5, end) infinite; -} - -@keyframes dots { - 0%, 20% { content: ''; } - 40% { content: '.'; } - 60% { content: '..'; } - 80%, 100% { content: '...'; } -} - -/* ===== 打字机效果 ===== */ -.typewriter { - overflow: hidden; - border-right: 2px solid #333; - 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: #333; } -} - -/* ===== 波浪效果 ===== */ -.wave { - position: relative; - overflow: hidden; -} - -.wave::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); - animation: wave 2s infinite; -} - -@keyframes wave { - 0% { left: -100%; } - 100% { left: 100%; } -} - -/* ===== 粒子效果 ===== */ -.particles { - position: relative; -} - -.particle { - position: absolute; - width: 4px; - height: 4px; - background: #3498db; - border-radius: 50%; - animation: particle-float 3s infinite ease-in-out; -} - -.particle:nth-child(1) { animation-delay: 0s; } -.particle:nth-child(2) { animation-delay: 0.5s; } -.particle:nth-child(3) { animation-delay: 1s; } -.particle:nth-child(4) { animation-delay: 1.5s; } -.particle:nth-child(5) { animation-delay: 2s; } - -@keyframes particle-float { - 0%, 100% { - transform: translateY(0) scale(1); - opacity: 1; - } - 50% { - transform: translateY(-20px) scale(1.2); - opacity: 0.7; - } -} - -/* ===== 3D翻转效果 ===== */ -.flip-card { - perspective: 1000px; - height: 200px; -} - -.flip-card-inner { - position: relative; - width: 100%; - height: 100%; - text-align: center; - transition: transform 0.8s; - transform-style: preserve-3d; -} - -.flip-card:hover .flip-card-inner { - transform: rotateY(180deg); -} - -.flip-card-front, .flip-card-back { - position: absolute; - width: 100%; - height: 100%; - backface-visibility: hidden; -} - -.flip-card-back { - transform: rotateY(180deg); -} - -/* ===== 渐变背景动画 ===== */ -.gradient-bg { - background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); - background-size: 400% 400%; - animation: gradient-shift 15s ease infinite; -} - -@keyframes gradient-shift { - 0% { background-position: 0% 50%; } - 50% { background-position: 100% 50%; } - 100% { background-position: 0% 50%; } -} - -/* ===== 霓虹灯效果 ===== */ -.neon-text { - color: #fff; - text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #0073e6, 0 0 20px #0073e6, 0 0 25px #0073e6, 0 0 30px #0073e6, 0 0 35px #0073e6; - animation: neon-pulse 1.5s ease-in-out infinite alternate; -} - -@keyframes neon-pulse { - from { text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #0073e6, 0 0 20px #0073e6, 0 0 25px #0073e6, 0 0 30px #0073e6, 0 0 35px #0073e6; } - to { text-shadow: 0 0 2px #fff, 0 0 4px #fff, 0 0 6px #0073e6, 0 0 8px #0073e6, 0 0 10px #0073e6, 0 0 12px #0073e6, 0 0 14px #0073e6; } -} - -/* ===== 响应式动画 ===== */ -@media (prefers-reduced-motion: reduce) { - .animate, - .hover-lift, - .hover-scale, - .hover-rotate, - .hover-glow, - .hover-bounce { - animation: none !important; - transition: none !important; - transform: none !important; - } -} - -/* ===== 动画工具类 ===== */ -.animate-on-scroll { - opacity: 0; - transform: translateY(50px); - transition: all 0.8s ease; -} - -.animate-on-scroll.visible { - opacity: 1; - transform: translateY(0); -} - -.animate-stagger > * { - opacity: 0; - transform: translateY(30px); - transition: all 0.6s ease; -} - -.animate-stagger.visible > * { - opacity: 1; - transform: translateY(0); -} - -.animate-stagger.visible > *:nth-child(1) { transition-delay: 0.1s; } -.animate-stagger.visible > *:nth-child(2) { transition-delay: 0.2s; } -.animate-stagger.visible > *:nth-child(3) { transition-delay: 0.3s; } -.animate-stagger.visible > *:nth-child(4) { transition-delay: 0.4s; } -.animate-stagger.visible > *:nth-child(5) { transition-delay: 0.5s; } -.animate-stagger.visible > *:nth-child(6) { transition-delay: 0.6s; } diff --git a/src/assets/css/animations.less b/src/assets/css/animations.less new file mode 100644 index 0000000..534f7f9 --- /dev/null +++ b/src/assets/css/animations.less @@ -0,0 +1,761 @@ +// 动画效果库 - LESS版本 +@import './variables.less'; + +// ===== 动画变量 ===== +@animation-duration: @animation-duration-base; +@animation-timing: @animation-timing-base; +@animation-fill-mode: both; + +// ===== 基础动画关键帧 ===== +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInLeft { + from { + opacity: 0; + transform: translateX(-30px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes fadeInRight { + from { + opacity: 0; + transform: translateX(30px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slideInUp { + from { + transform: translateY(100px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes slideInDown { + from { + transform: translateY(-100px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes slideInLeft { + from { + transform: translateX(-100px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideInRight { + from { + transform: translateX(100px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes zoomIn { + from { + opacity: 0; + transform: scale(0.3); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes zoomOut { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.3); + } +} + +@keyframes bounce { + 0%, 20%, 53%, 80%, 100% { + transform: translate3d(0, 0, 0); + } + 40%, 43% { + transform: translate3d(0, -30px, 0); + } + 70% { + transform: translate3d(0, -15px, 0); + } + 90% { + transform: translate3d(0, -4px, 0); + } +} + +@keyframes pulse { + from { + transform: scale3d(1, 1, 1); + } + 50% { + transform: scale3d(1.05, 1.05, 1.05); + } + to { + transform: scale3d(1, 1, 1); + } +} + +@keyframes shake { + 0%, 100% { + transform: translate3d(0, 0, 0); + } + 10%, 30%, 50%, 70%, 90% { + transform: translate3d(-10px, 0, 0); + } + 20%, 40%, 60%, 80% { + transform: translate3d(10px, 0, 0); + } +} + +@keyframes swing { + 20% { + transform: rotate(15deg); + } + 40% { + transform: rotate(-10deg); + } + 60% { + transform: rotate(5deg); + } + 80% { + transform: rotate(-5deg); + } + 100% { + transform: rotate(0deg); + } +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes flip { + 0% { + transform: perspective(400px) rotateY(0); + } + 40% { + transform: perspective(400px) rotateY(90deg); + } + 60% { + transform: perspective(400px) rotateY(180deg); + } + 80% { + transform: perspective(400px) rotateY(270deg); + } + 100% { + transform: perspective(400px) rotateY(360deg); + } +} + +@keyframes lightSpeedIn { + from { + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + 60% { + transform: skewX(20deg); + opacity: 1; + } + 80% { + transform: skewX(-5deg); + opacity: 1; + } + to { + transform: translate3d(0, 0, 0); + opacity: 1; + } +} + +@keyframes rubberBand { + from { + transform: scale3d(1, 1, 1); + } + 30% { + transform: scale3d(1.25, 0.75, 1); + } + 40% { + transform: scale3d(0.75, 1.25, 1); + } + 50% { + transform: scale3d(1.15, 0.85, 1); + } + 65% { + transform: scale3d(0.95, 1.05, 1); + } + 75% { + transform: scale3d(1.05, 0.95, 1); + } + to { + transform: scale3d(1, 1, 1); + } +} + +@keyframes wobble { + 0% { + transform: translate3d(0, 0, 0); + } + 15% { + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + 30% { + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + 45% { + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + 60% { + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + 75% { + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + 100% { + transform: translate3d(0, 0, 0); + } +} + +@keyframes tada { + 0% { + transform: scale3d(1, 1, 1); + } + 10%, 20% { + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + 30%, 50%, 70%, 90% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + 40%, 60%, 80% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + 100% { + transform: scale3d(1, 1, 1); + } +} + +@keyframes heartBeat { + 0% { + transform: scale(1); + } + 14% { + transform: scale(1.3); + } + 28% { + transform: scale(1); + } + 42% { + transform: scale(1.3); + } + 70% { + transform: scale(1); + } +} + +@keyframes hinge { + 0% { + transform: rotate(0); + transform-origin: top left; + animation-timing-function: ease-in-out; + } + 20%, 60% { + transform: rotate(80deg); + transform-origin: top left; + animation-timing-function: ease-in-out; + } + 40%, 80% { + transform: rotate(60deg); + transform-origin: top left; + animation-timing-function: ease-in-out; + opacity: 1; + } + 100% { + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +@keyframes roll { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +// ===== 基础动画类 ===== +.animate { + animation-duration: @animation-duration; + animation-fill-mode: @animation-fill-mode; + animation-timing-function: @animation-timing; +} + +// 淡入动画 +.fade-in { animation-name: fadeIn; } +.fade-in-up { animation-name: fadeInUp; } +.fade-in-down { animation-name: fadeInDown; } +.fade-in-left { animation-name: fadeInLeft; } +.fade-in-right { animation-name: fadeInRight; } + +// 滑入动画 +.slide-in-up { animation-name: slideInUp; } +.slide-in-down { animation-name: slideInDown; } +.slide-in-left { animation-name: slideInLeft; } +.slide-in-right { animation-name: slideInRight; } + +// 缩放动画 +.zoom-in { animation-name: zoomIn; } +.zoom-out { animation-name: zoomOut; } + +// 特殊动画 +.bounce { animation-name: bounce; } +.pulse { animation-name: pulse; } +.shake { animation-name: shake; } +.swing { animation-name: swing; } +.rotate { animation-name: rotate; } +.flip { animation-name: flip; } +.light-speed-in { animation-name: lightSpeedIn; } +.rubber-band { animation-name: rubberBand; } +.wobble { animation-name: wobble; } +.tada { animation-name: tada; } +.heart-beat { animation-name: heartBeat; } +.hinge { animation-name: hinge; } +.roll { animation-name: roll; } + +// ===== 动画时长 ===== +.duration-0_5s { animation-duration: 0.5s; } +.duration-1s { animation-duration: 1s; } +.duration-1_5s { animation-duration: 1.5s; } +.duration-2s { animation-duration: 2s; } +.duration-3s { animation-duration: 3s; } + +// ===== 动画延迟 ===== +.delay-0_5s { animation-delay: 0.5s; } +.delay-1s { animation-delay: 1s; } +.delay-1_5s { animation-delay: 1.5s; } +.delay-2s { animation-delay: 2s; } +.delay-3s { animation-delay: 3s; } + +// ===== 动画重复 ===== +.infinite { animation-iteration-count: infinite; } +.repeat-2 { animation-iteration-count: 2; } +.repeat-3 { animation-iteration-count: 3; } + +// ===== 悬停动画效果 ===== +.hover-lift { + transition: transform 0.3s ease, box-shadow 0.3s ease; + + &:hover { + transform: translateY(-5px); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); + } +} + +.hover-scale { + transition: transform 0.3s ease; + + &:hover { + transform: scale(1.05); + } +} + +.hover-glow { + transition: box-shadow 0.3s ease; + + &:hover { + box-shadow: 0 0 20px rgba(0, 123, 255, 0.5); + } +} + +.hover-rotate { + transition: transform 0.3s ease; + + &:hover { + transform: rotate(5deg); + } +} + +.hover-bounce { + transition: transform 0.3s ease; + + &:hover { + animation: bounce 0.6s ease; + } +} + +// ===== 滚动触发动画 ===== +.scroll-animate { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; + + &.animate-in { + opacity: 1; + transform: translateY(0); + } +} + +// ===== 交错动画 ===== +.animate-stagger { + > * { + opacity: 0; + transform: translateY(20px); + transition: all 0.6s ease; + + &:nth-child(1) { transition-delay: 0.1s; } + &:nth-child(2) { transition-delay: 0.2s; } + &:nth-child(3) { transition-delay: 0.3s; } + &:nth-child(4) { transition-delay: 0.4s; } + &:nth-child(5) { transition-delay: 0.5s; } + &:nth-child(6) { transition-delay: 0.6s; } + + &.animate-in { + opacity: 1; + transform: translateY(0); + } + } +} + +// ===== 加载指示器 ===== +.loading-spinner { + width: 40px; + height: 40px; + border: 4px solid #f3f3f3; + border-top: 4px solid #3498db; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +// ===== 打字机效果 ===== +.typewriter { + overflow: hidden; + border-right: 2px solid #000; + 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: #000; } +} + +// ===== 波浪效果 ===== +.wave { + position: relative; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.3) 50%, transparent 70%); + animation: wave 2s infinite; + } +} + +@keyframes wave { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +// ===== 粒子效果 ===== +.particles { + position: relative; + overflow: hidden; + + &::before, + &::after { + content: ''; + position: absolute; + width: 4px; + height: 4px; + background: rgba(255, 255, 255, 0.8); + border-radius: 50%; + animation: float 6s infinite; + } + + &::before { + left: 20%; + animation-delay: 0s; + } + + &::after { + right: 20%; + animation-delay: 3s; + } +} + +@keyframes float { + 0%, 100% { transform: translateY(0px) rotate(0deg); opacity: 1; } + 50% { transform: translateY(-20px) rotate(180deg); opacity: 0.5; } +} + +// ===== 3D翻转卡片 ===== +.flip-card { + perspective: 1000px; + + .flip-card-inner { + position: relative; + width: 100%; + height: 100%; + text-align: center; + transition: transform 0.6s; + transform-style: preserve-3d; + + &:hover { + transform: rotateY(180deg); + } + } + + .flip-card-front, + .flip-card-back { + position: absolute; + width: 100%; + height: 100%; + backface-visibility: hidden; + } + + .flip-card-back { + transform: rotateY(180deg); + } +} + +// ===== 渐变背景动画 ===== +.gradient-bg { + background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); + background-size: 400% 400%; + animation: gradient 15s ease infinite; +} + +@keyframes gradient { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +// ===== 霓虹文字效果 ===== +.neon-text { + color: #fff; + text-shadow: + 0 0 5px #fff, + 0 0 10px #fff, + 0 0 15px #0073e6, + 0 0 20px #0073e6, + 0 0 25px #0073e6, + 0 0 30px #0073e6, + 0 0 35px #0073e6; + animation: neon-pulse 1.5s ease-in-out infinite alternate; +} + +@keyframes neon-pulse { + from { text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #0073e6, 0 0 20px #0073e6, 0 0 25px #0073e6, 0 0 30px #0073e6, 0 0 35px #0073e6; } + to { text-shadow: 0 0 2px #fff, 0 0 4px #fff, 0 0 6px #0073e6, 0 0 8px #0073e6, 0 0 10px #0073e6, 0 0 12px #0073e6, 0 0 14px #0073e6; } +} + +// ===== 滚动进度条 ===== +.scroll-progress-bar { + position: fixed; + top: 0; + left: 0; + width: 0%; + height: 3px; + background: linear-gradient(90deg, #0073e6, #00d4ff); + z-index: 9999; + transition: width 0.1s ease; +} + +// ===== 视差滚动元素 ===== +.parallax { + position: relative; + overflow: hidden; + + .parallax-bg { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 120%; + background-size: cover; + background-position: center; + transform: translateZ(0); + will-change: transform; + } +} + +// ===== 浮动粒子 ===== +.floating-particles { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; + z-index: 1; + + .floating-particle { + position: absolute; + width: 4px; + height: 4px; + background: rgba(255, 255, 255, 0.6); + border-radius: 50%; + animation: float-particle 8s infinite linear; + + &:nth-child(1) { left: 10%; animation-delay: 0s; animation-duration: 8s; } + &:nth-child(2) { left: 20%; animation-delay: 1s; animation-duration: 10s; } + &:nth-child(3) { left: 30%; animation-delay: 2s; animation-duration: 12s; } + &:nth-child(4) { left: 40%; animation-delay: 3s; animation-duration: 9s; } + &:nth-child(5) { left: 50%; animation-delay: 4s; animation-duration: 11s; } + &:nth-child(6) { left: 60%; animation-delay: 5s; animation-duration: 7s; } + &:nth-child(7) { left: 70%; animation-delay: 6s; animation-duration: 13s; } + &:nth-child(8) { left: 80%; animation-delay: 7s; animation-duration: 8s; } + &:nth-child(9) { left: 90%; animation-delay: 8s; animation-duration: 10s; } + &:nth-child(10) { left: 95%; animation-delay: 9s; animation-duration: 12s; } + } +} + +@keyframes float-particle { + 0% { + transform: translateY(100vh) scale(0); + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + transform: translateY(-100px) scale(1); + opacity: 0; + } +} + +// ===== 文字发光效果 ===== +.text-glow { + text-shadow: + 0 0 5px currentColor, + 0 0 10px currentColor, + 0 0 15px currentColor, + 0 0 20px currentColor; + animation: text-glow-pulse 2s ease-in-out infinite alternate; +} + +@keyframes text-glow-pulse { + from { text-shadow: 0 0 5px currentColor, 0 0 10px currentColor, 0 0 15px currentColor, 0 0 20px currentColor; } + to { text-shadow: 0 0 10px currentColor, 0 0 20px currentColor, 0 0 30px currentColor, 0 0 40px currentColor; } +} + +// ===== 响应式动画考虑 ===== +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +// ===== 移动端动画优化 ===== +@media (max-width: 768px) { + .floating-particles { + display: none; // 移动端隐藏浮动粒子以提升性能 + } + + .animate { + animation-duration: 0.8s; // 移动端减少动画时长 + } + + .scroll-animate { + transform: translateY(20px); // 移动端减少移动距离 + } +} + +// ===== 性能优化 ===== +.animate, +.hover-lift, +.hover-scale, +.hover-glow, +.hover-rotate, +.hover-bounce { + will-change: transform, opacity; + transform: translate3d(0, 0, 0); // 启用硬件加速 +} + +// ===== 动画工具类 ===== +.animation-paused { animation-play-state: paused; } +.animation-running { animation-play-state: running; } +.animation-reverse { animation-direction: reverse; } +.animation-alternate { animation-direction: alternate; } +.animation-alternate-reverse { animation-direction: alternate-reverse; } diff --git a/src/assets/css/common.css b/src/assets/css/common.css deleted file mode 100644 index 7a70cfb..0000000 --- a/src/assets/css/common.css +++ /dev/null @@ -1,39 +0,0 @@ - - -.scrollToContentWrapper { - height: 6em; - text-align: center; - z-index: 5000; -} - -.scrollToContentWrapper>a.scrollToContent { - position: relative; - display: inline-block; - margin-top: -3em; - background: lightyellow; - width: 4.8em; - height: 4.8em; - line-height: 5.6em; - border: 1px solid #eee; - border-radius: 50%; - background-color: #fff; - text-align: center; - cursor: pointer; - text-decoration: none; - color: #000; -} - -.scrollToContentWrapper>a.scrollToContent:hover { - background-color: #000 !important; - border-color: #111; - text-decoration: none; -} - -.scrollToContentWrapper>a.scrollToContent:hover img { - -webkit-filter: invert(100%); - filter: invert(100%); -} - -.scrollToContentWrapper>a.scrollToContent>img { - height: 1.0em; -} diff --git a/src/assets/css/common.less b/src/assets/css/common.less new file mode 100644 index 0000000..4fb717a --- /dev/null +++ b/src/assets/css/common.less @@ -0,0 +1,40 @@ +// 通用样式 - LESS版本 +@import './variables.less'; + +.scrollToContentWrapper { + height: 6em; + text-align: center; + z-index: 5000; + + > a.scrollToContent { + position: relative; + display: inline-block; + margin-top: -3em; + background: lightyellow; + width: 4.8em; + height: 4.8em; + line-height: 5.6em; + border: 1px solid #eee; + border-radius: 50%; + background-color: #fff; + text-align: center; + cursor: pointer; + text-decoration: none; + color: #000; + + &:hover { + background-color: #000 !important; + border-color: #111; + text-decoration: none; + + img { + -webkit-filter: invert(100%); + filter: invert(100%); + } + } + + > img { + height: 1.0em; + } + } +} diff --git a/src/assets/css/common_mobile.less b/src/assets/css/common_mobile.less new file mode 100644 index 0000000..fbf62f1 --- /dev/null +++ b/src/assets/css/common_mobile.less @@ -0,0 +1,933 @@ +// 通用移动端适配样式 - 适用于所有页面 - LESS版本 +@import './variables.less'; + +// ===== 移动端断点变量 ===== +// 使用全局变量文件中的断点 + +// ===== 移动端基础适配 ===== +@media (max-width: @mobile-breakpoint) { + // 页面横幅移动端适配 + .page-banner { + padding: 60px 0 40px !important; + } + + .banner-title { + font-size: 2.5rem !important; + line-height: 1.2 !important; + } + + .banner-subtitle { + font-size: 1.2rem !important; + line-height: 1.4 !important; + } + + // 容器移动端适配 + .container { + padding: 0 15px !important; + } + + // 标题移动端适配 + .section-title { + font-size: 2rem !important; + line-height: 1.3 !important; + } + + .section-subtitle { + font-size: 1.1rem !important; + line-height: 1.5 !important; + } + + // 网格布局移动端适配 + .grid, + .advantages-grid, + .areas-grid, + .services-grid, + .team-grid, + .contact-grid, + .tech-grid { + grid-template-columns: 1fr !important; + gap: 20px !important; + } + + // 卡片移动端适配 + .card, + .advantage-card, + .area-card, + .service-item, + .team-member, + .contact-card, + .tech-layer { + padding: 25px 20px !important; + margin-bottom: 20px !important; + } + + // 内容区域移动端适配 + .content-section { + margin-bottom: 40px !important; + } + + .section-text { + font-size: 1rem !important; + line-height: 1.6 !important; + } + + // 图标移动端适配 + .icon, + .service-icon, + .advantage-icon, + .area-icon, + .contact-icon { + font-size: 2.5rem !important; + margin-bottom: 15px !important; + } + + // 列表移动端适配 + .feature-list, + .advantage-features, + .area-features { + padding-left: 20px !important; + + li { + margin-bottom: 8px !important; + font-size: 0.95rem !important; + } + } + + // 时间线移动端适配 + .timeline { + padding-left: 20px !important; + + .timeline-item { + margin-bottom: 30px !important; + padding-left: 20px !important; + + .timeline-date { + font-size: 0.9rem !important; + margin-bottom: 8px !important; + } + + .timeline-title { + font-size: 1.1rem !important; + margin-bottom: 10px !important; + } + + .timeline-content { + font-size: 0.95rem !important; + line-height: 1.5 !important; + } + } + } + + // 统计数字移动端适配 + .stats-section { + padding: 40px 0 !important; + + .stats-grid { + grid-template-columns: repeat(2, 1fr) !important; + gap: 20px !important; + } + + .stat-item { + text-align: center !important; + padding: 20px 15px !important; + + .stat-number { + font-size: 2rem !important; + margin-bottom: 10px !important; + } + + .stat-label { + font-size: 0.9rem !important; + } + } + } + + // 团队介绍移动端适配 + .team-section { + .team-grid { + grid-template-columns: 1fr !important; + gap: 25px !important; + } + + .team-member { + text-align: center !important; + + .member-photo { + width: 120px !important; + height: 120px !important; + margin: 0 auto 15px !important; + } + + .member-name { + font-size: 1.2rem !important; + margin-bottom: 8px !important; + } + + .member-position { + font-size: 0.9rem !important; + margin-bottom: 10px !important; + } + + .member-description { + font-size: 0.9rem !important; + line-height: 1.5 !important; + } + } + } + + // 联系信息移动端适配 + .contact-section { + .contact-grid { + grid-template-columns: 1fr !important; + gap: 25px !important; + } + + .contact-info { + text-align: center !important; + + .contact-icon { + font-size: 2rem !important; + margin-bottom: 15px !important; + } + + .contact-title { + font-size: 1.1rem !important; + margin-bottom: 8px !important; + } + + .contact-detail { + font-size: 0.95rem !important; + line-height: 1.5 !important; + } + } + } + + // 技术栈移动端适配 + .tech-section { + .tech-grid { + grid-template-columns: repeat(2, 1fr) !important; + gap: 15px !important; + } + + .tech-layer { + padding: 20px 15px !important; + text-align: center !important; + + .tech-icon { + font-size: 2rem !important; + margin-bottom: 10px !important; + } + + .tech-name { + font-size: 0.9rem !important; + } + } + } + + // 解决方案移动端适配 + .solutions-section { + .solution-item { + margin-bottom: 30px !important; + padding: 20px !important; + + .solution-icon { + font-size: 2.5rem !important; + margin-bottom: 15px !important; + text-align: center !important; + } + + .solution-title { + font-size: 1.3rem !important; + margin-bottom: 12px !important; + text-align: center !important; + } + + .solution-description { + font-size: 0.95rem !important; + line-height: 1.6 !important; + text-align: center !important; + } + } + } + + // 优势特点移动端适配 + .advantages-section { + .advantage-item { + margin-bottom: 25px !important; + padding: 20px !important; + text-align: center !important; + + .advantage-icon { + font-size: 2.5rem !important; + margin-bottom: 15px !important; + } + + .advantage-title { + font-size: 1.2rem !important; + margin-bottom: 10px !important; + } + + .advantage-description { + font-size: 0.9rem !important; + line-height: 1.5 !important; + } + } + } + + // 服务领域移动端适配 + .areas-section { + .area-item { + margin-bottom: 25px !important; + padding: 20px !important; + text-align: center !important; + + .area-icon { + font-size: 2.5rem !important; + margin-bottom: 15px !important; + } + + .area-title { + font-size: 1.2rem !important; + margin-bottom: 10px !important; + } + + .area-description { + font-size: 0.9rem !important; + line-height: 1.5 !important; + } + + .area-features { + margin-top: 15px !important; + text-align: left !important; + } + } + } + + // 按钮移动端适配 + .btn, + .button { + padding: 12px 24px !important; + font-size: 16px !important; + min-height: 44px !important; // 触摸友好尺寸 + width: 100% !important; // 移动端全宽按钮 + + &.btn-large { + padding: 15px 30px !important; + font-size: 18px !important; + } + + &.btn-small { + padding: 8px 16px !important; + font-size: 14px !important; + } + + &.btn-block { + display: block !important; + width: 100% !important; + } + } + + // 表单移动端适配 + .form-section { + .form-group { + margin-bottom: 20px !important; + + label { + display: block !important; + margin-bottom: 8px !important; + font-weight: 500 !important; + font-size: 0.95rem !important; + } + + input, + textarea, + select { + width: 100% !important; + padding: 12px !important; + font-size: 16px !important; // 防止iOS缩放 + border: 1px solid #ddd !important; + border-radius: 4px !important; + min-height: 44px !important; // 触摸友好尺寸 + + &:focus { + outline: none !important; + border-color: #3498db !important; + box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2) !important; + } + } + + textarea { + min-height: 100px !important; + resize: vertical !important; + } + + .form-help { + font-size: 0.85rem !important; + color: #666 !important; + margin-top: 5px !important; + } + } + + .form-actions { + text-align: center !important; + margin-top: 30px !important; + + .btn { + margin: 0 5px 10px !important; + } + } + } + + // 图片移动端适配 + .image-section { + img { + max-width: 100% !important; + height: auto !important; + border-radius: 8px !important; + } + + .image-caption { + font-size: 0.9rem !important; + color: #666 !important; + text-align: center !important; + margin-top: 10px !important; + } + } + + // 视频移动端适配 + .video-section { + .video-container { + position: relative !important; + width: 100% !important; + height: 0 !important; + padding-bottom: 56.25% !important; // 16:9 比例 + + iframe, + video { + position: absolute !important; + top: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100% !important; + border-radius: 8px !important; + } + } + } + + // 表格移动端适配 + .table-section { + .table-responsive { + overflow-x: auto !important; + -webkit-overflow-scrolling: touch !important; + + table { + min-width: 600px !important; + font-size: 0.9rem !important; + + th, + td { + padding: 8px 12px !important; + white-space: nowrap !important; + } + } + } + } + + // 分页移动端适配 + .pagination { + display: flex !important; + justify-content: center !important; + flex-wrap: wrap !important; + gap: 5px !important; + margin: 30px 0 !important; + + .page-item { + .page-link { + padding: 8px 12px !important; + font-size: 14px !important; + min-width: 40px !important; + text-align: center !important; + border-radius: 4px !important; + } + } + } + + // 面包屑移动端适配 + .breadcrumb { + padding: 10px 0 !important; + font-size: 0.9rem !important; + + .breadcrumb-item { + margin-right: 8px !important; + + &:not(:last-child)::after { + content: '>' !important; + margin-left: 8px !important; + color: #999 !important; + } + } + } + + // 标签移动端适配 + .tags { + display: flex !important; + flex-wrap: wrap !important; + gap: 8px !important; + margin: 15px 0 !important; + + .tag { + padding: 4px 8px !important; + font-size: 0.8rem !important; + border-radius: 12px !important; + background: #f0f0f0 !important; + color: #333 !important; + } + } + + // 警告框移动端适配 + .alert { + padding: 15px !important; + margin: 20px 0 !important; + border-radius: 4px !important; + font-size: 0.95rem !important; + + &.alert-success { + background: #d4edda !important; + border: 1px solid #c3e6cb !important; + color: #155724 !important; + } + + &.alert-warning { + background: #fff3cd !important; + border: 1px solid #ffeaa7 !important; + color: #856404 !important; + } + + &.alert-danger { + background: #f8d7da !important; + border: 1px solid #f5c6cb !important; + color: #721c24 !important; + } + + &.alert-info { + background: #d1ecf1 !important; + border: 1px solid #bee5eb !important; + color: #0c5460 !important; + } + } + + // 进度条移动端适配 + .progress { + height: 20px !important; + margin: 15px 0 !important; + border-radius: 10px !important; + overflow: hidden !important; + + .progress-bar { + height: 100% !important; + border-radius: 10px !important; + font-size: 0.8rem !important; + line-height: 20px !important; + } + } + + // 模态框移动端适配 + .modal { + .modal-dialog { + margin: 10px !important; + max-width: calc(100% - 20px) !important; + } + + .modal-content { + border-radius: 8px !important; + } + + .modal-header { + padding: 15px 20px !important; + + .modal-title { + font-size: 1.2rem !important; + } + } + + .modal-body { + padding: 20px !important; + font-size: 0.95rem !important; + } + + .modal-footer { + padding: 15px 20px !important; + + .btn { + margin: 0 5px !important; + } + } + } + + // 工具提示移动端适配 + .tooltip { + font-size: 0.85rem !important; + max-width: 200px !important; + } + + // 弹出框移动端适配 + .popover { + max-width: 250px !important; + font-size: 0.9rem !important; + + .popover-header { + font-size: 1rem !important; + padding: 10px 15px !important; + } + + .popover-body { + padding: 10px 15px !important; + } + } + + // 手风琴移动端适配 + .accordion { + .accordion-item { + margin-bottom: 10px !important; + border-radius: 8px !important; + overflow: hidden !important; + + .accordion-header { + .accordion-button { + padding: 15px 20px !important; + font-size: 1rem !important; + + &:not(.collapsed) { + background-color: #f8f9fa !important; + } + } + } + + .accordion-body { + padding: 15px 20px !important; + font-size: 0.95rem !important; + line-height: 1.6 !important; + } + } + } + + // 标签页移动端适配 + .nav-tabs { + flex-wrap: wrap !important; + + .nav-item { + margin-bottom: 5px !important; + + .nav-link { + padding: 8px 12px !important; + font-size: 0.9rem !important; + border-radius: 4px !important; + + &.active { + background-color: #007bff !important; + border-color: #007bff !important; + color: white !important; + } + } + } + } + + // 导航栏移动端适配 + .navbar { + .navbar-brand { + font-size: 1.2rem !important; + } + + .navbar-nav { + .nav-link { + padding: 10px 15px !important; + font-size: 0.95rem !important; + border-bottom: 1px solid #eee !important; + + &:hover { + background-color: #f8f9fa !important; + } + } + } + } + + // 侧边栏移动端适配 + .sidebar { + position: fixed !important; + top: 0 !important; + left: -100% !important; + width: 80% !important; + height: 100vh !important; + background: white !important; + z-index: 1000 !important; + transition: left 0.3s ease !important; + overflow-y: auto !important; + + &.active { + left: 0 !important; + } + + .sidebar-header { + padding: 20px !important; + border-bottom: 1px solid #eee !important; + + .close-sidebar { + float: right !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + cursor: pointer !important; + } + } + + .sidebar-body { + padding: 20px !important; + } + } + + // 页脚移动端适配 + .footer { + padding: 40px 0 20px !important; + text-align: center !important; + + .footer-content { + margin-bottom: 30px !important; + + .footer-section { + margin-bottom: 25px !important; + + .footer-title { + font-size: 1.1rem !important; + margin-bottom: 15px !important; + } + + .footer-links { + list-style: none !important; + padding: 0 !important; + + li { + margin-bottom: 8px !important; + + a { + color: #666 !important; + text-decoration: none !important; + font-size: 0.9rem !important; + + &:hover { + color: #333 !important; + } + } + } + } + } + } + + .footer-bottom { + border-top: 1px solid #eee !important; + padding-top: 20px !important; + font-size: 0.85rem !important; + color: #999 !important; + } + } +} + +// ===== 平板端适配 ===== +@media (min-width: @mobile-breakpoint + 1) and (max-width: @tablet-breakpoint) { + .container { + padding: 0 30px !important; + } + + .grid, + .advantages-grid, + .areas-grid, + .services-grid, + .team-grid, + .contact-grid, + .tech-grid { + grid-template-columns: repeat(2, 1fr) !important; + gap: 25px !important; + } + + .stats-grid { + grid-template-columns: repeat(4, 1fr) !important; + } + + .tech-grid { + grid-template-columns: repeat(3, 1fr) !important; + } +} + +// ===== 小屏幕移动端适配 ===== +@media (max-width: @small-mobile) { + .container { + padding: 0 10px !important; + } + + .banner-title { + font-size: 2rem !important; + } + + .banner-subtitle { + font-size: 1rem !important; + } + + .section-title { + font-size: 1.8rem !important; + } + + .section-subtitle { + font-size: 1rem !important; + } + + .stats-grid { + grid-template-columns: 1fr !important; + } + + .card, + .advantage-card, + .area-card, + .service-item, + .team-member, + .contact-card, + .tech-layer { + padding: 20px 15px !important; + } +} + +// ===== 超小屏幕移动端适配 ===== +@media (max-width: @xs-mobile) { + .container { + padding: 0 8px !important; + } + + .banner-title { + font-size: 1.8rem !important; + } + + .banner-subtitle { + font-size: 0.9rem !important; + } + + .section-title { + font-size: 1.6rem !important; + } + + .section-subtitle { + font-size: 0.9rem !important; + } + + .card, + .advantage-card, + .area-card, + .service-item, + .team-member, + .contact-card, + .tech-layer { + padding: 15px 12px !important; + } + + .btn, + .button { + padding: 8px 16px !important; + font-size: 14px !important; + } +} + +// ===== 触摸设备优化 ===== +@media (hover: none) and (pointer: coarse) { + // 触摸设备禁用悬停效果 + .btn:hover, + .button:hover, + .card:hover, + .advantage-card:hover, + .area-card:hover, + .service-item:hover, + .team-member:hover, + .contact-card:hover, + .tech-layer:hover { + transform: none !important; + box-shadow: none !important; + } + + // 触摸设备增加触摸反馈 + .btn, + .button, + .card, + .advantage-card, + .area-card, + .service-item, + .team-member, + .contact-card, + .tech-layer { + -webkit-tap-highlight-color: rgba(52, 152, 219, 0.3) !important; + + &:active { + transform: scale(0.98) !important; + transition: transform 0.1s ease !important; + } + } +} + +// ===== 高分辨率屏幕适配 ===== +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .icon, + .service-icon, + .advantage-icon, + .area-icon, + .contact-icon { + image-rendering: -webkit-optimize-contrast !important; + image-rendering: crisp-edges !important; + } +} + +// ===== 横屏模式适配 ===== +@media (orientation: landscape) and (max-height: 500px) { + .page-banner { + padding: 30px 0 20px !important; + } + + .banner-title { + font-size: 2rem !important; + margin-bottom: 15px !important; + } + + .banner-subtitle { + font-size: 1rem !important; + margin-bottom: 20px !important; + } + + .section { + padding: 30px 0 !important; + } +} + +// ===== 打印样式 ===== +@media print { + .mobile-nav, + .sidebar, + .modal, + .tooltip, + .popover { + display: none !important; + } + + .container { + padding: 0 !important; + } + + .section { + padding: 20px 0 !important; + page-break-inside: avoid !important; + } + + .card, + .advantage-card, + .area-card, + .service-item, + .team-member, + .contact-card, + .tech-layer { + page-break-inside: avoid !important; + margin-bottom: 15px !important; + } +} diff --git a/src/assets/css/content_pages.css b/src/assets/css/content_pages.css deleted file mode 100644 index 9315daf..0000000 --- a/src/assets/css/content_pages.css +++ /dev/null @@ -1,1264 +0,0 @@ -/*PAGE*/ - -/* 使用 CSS 自定义属性定义响应式断点 */ -:root { - /* 响应式断点 */ - --breakpoint-xs: 380px; - --breakpoint-sm: 480px; - --breakpoint-md: 620px; - --breakpoint-lg: 768px; - --breakpoint-xl: 992px; - --breakpoint-xxl: 1200px; - --breakpoint-xxxl: 1500px; - --breakpoint-xxxxl: 1600px; - - /* 间距变量 */ - --spacing-xs: 0.5em; - --spacing-sm: 1em; - --spacing-md: 1.5em; - --spacing-lg: 2em; - --spacing-xl: 3em; - --spacing-xxl: 4em; - --spacing-xxxl: 5em; - --spacing-xxxxl: 7em; - - /* 字体大小变量 */ - --font-size-xs: 0.9em; - --font-size-sm: 1.2em; - --font-size-md: 1.5em; - --font-size-lg: 2.5em; - --font-size-xl: 3em; - - /* 布局变量 */ - --sidebar-width: 25%; - --content-width: 75%; - --content-padding: 4em; - --sidebar-padding: 5em; -} - -.contentPage { - position: relative; - width: 100%; - vertical-align: top; - margin: 0 auto; - padding-top: var(--spacing-xxxxl); - min-height: 100vh; - - /* 容器查询支持 */ - container-type: inline-size; - container-name: content-page; -} - -/* CONTENT TABLES */ - -.contentTableLeft { - width: var(--sidebar-width); - padding-left: var(--sidebar-padding); - padding-top: var(--spacing-xl); - vertical-align: top; -} - -.contentTableRight { - width: var(--content-width); - padding-top: 3.5em; - padding-left: var(--content-padding); - padding-right: var(--content-padding); - vertical-align: top; -} - -/* 使用容器查询替代媒体查询 */ -@container content-page (max-width: 1600px) { - .contentTableRight { - padding-right: 8em; - } -} - -@container content-page (max-width: 1500px) { - .contentTableRight { - padding-right: 6em; - } - - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-xl); - } -} - -@container content-page (max-width: 1200px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-lg); - } - - .contentPage .contentPageBanner .bannerBox .bread { - width: 75%; - } - - .contentTableLeft { - padding-left: var(--spacing-xl); - padding-top: var(--spacing-xl); - } - - .contentTableRight { - padding-top: var(--spacing-xl); - padding-left: var(--content-padding); - padding-right: var(--content-padding); - } - - .contentPage>.contentTable .bottomTwoColumn div.column { - width: 100%; - } - - .contentPage>.contentTable .bottomTwoColumn div.column:first-child { - margin-bottom: var(--spacing-sm); - } -} - -@container content-page (max-width: 992px) { - .contentTableLeft { - padding-left: var(--spacing-lg); - } - - .contentTableRight { - padding-top: 4.5em; - padding-left: var(--spacing-xl); - padding-right: var(--spacing-lg); - } -} - -@container content-page (max-width: 768px) { - .contentPage { - padding-top: 5.5em; - } - - .contentPage .contentPageBanner .bannerBox .bannerBoxHeadline { - display: none; - } - - .contentPage .contentPageBanner .bannerBox .bread { - width: 90%; - } - - .pageBannerMobile { - display: inline-block; - width: 100%; - border: none; - } - - img.pageBanner { - display: none; - } - - .contentTableLeft { - width: 100%; - padding: 0; - margin: 0; - } - - .contentTableRight { - display: none; - } - - .contentTableLeft>div>#navBreadcrumb { - display: none; - } - - .accordion { - padding: var(--spacing-lg); - } - - .accordion:after { - display: none; - } - - .accordion>ul>li>ul.categoryItems>li>a { - width: 100%; - padding: var(--spacing-sm) 0; - } - - .accordion .triangleLeft, - .triangleOverlay { - display: none; - } - - .linkContactUs { - display: inline-block !important; - text-decoration: underline !important; - padding-bottom: 0; - } - - .contentPage>.contentTable .bottomTwoColumn div.column p.contactUs { - display: none; - } -} - -@container content-page (max-width: 620px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-md); - } - - .contentPage .contentPageBanner .bannerBox .bread { - width: 95%; - } -} - -@container content-page (max-width: 480px) { - .contentPage { - padding-top: 4em; - padding-bottom: 0; - line-height: 1.2em; - } - - .contentTableLeft { - width: 100%; - } - - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-md); - } - - .contentPage .contentPageBanner .bannerBox .bread { - display: none; - } - - .triangle { - border-top: 0.4em solid transparent; - border-bottom: 0.4em solid transparent; - border-left: 0.4em solid #ee3224; - margin-right: 0.6em; - } - - .categoryItems { - padding: 0; - } - - .accordion { - padding-bottom: 0; - } - - .accordion div.mobileContent .seekJobWrapper { - margin-top: var(--spacing-lg); - text-align: center; - } - - .accordion div.mobileContent a.seekJob { - display: block; - padding: 1.2em; - } -} - -@container content-page (max-width: 380px) { - .contentPage>.contentTable .productCustomers .customerIcons img { - height: 2.5em; - } - - .contentPage>.contentTable .productCustomers .customerIcons img.shorter { - height: 1.5em; - } -} - -/* 容器查询回退方案 - 为不支持容器查询的浏览器提供媒体查询 */ -@supports not (container-type: inline-size) { - @media only screen and (max-width: 1600px) { - .contentTableRight { - padding-top: 3.5em; - padding-left: 4.0em; - padding-right: 8.0em; - } - } - - @media only screen and (max-width: 1500px) { - .contentTableRight { - padding-top: 3.5em; - padding-left: 4.0em; - padding-right: 6.0em; - } - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: 3em; - } - } - - @media only screen and (max-width: 1200px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: 2.5em; - } - .contentPage .contentPageBanner .bannerBox .bread { - width: 75%; - } - .contentTableLeft { - padding-left: 3em; - padding-top: 3em; - } - .contentTableRight { - padding-top: 3em; - padding-left: 4.0em; - padding-right: 4.0em; - } - .contentPage>.contentTable .bottomTwoColumn div.column { - width: 100%; - } - .contentPage>.contentTable .bottomTwoColumn div.column:first-child { - margin-bottom: 1em; - } - } - - @media only screen and (max-width: 992px) { - .contentTableLeft { - padding-left: 2em; - } - .contentTableRight { - padding-top: 4.5em; - padding-left: 3em; - padding-right: 2em; - } - } - - @media only screen and (max-width: 768px) { - .contentPage { - padding-top: 5.5em; - } - .contentPage .contentPageBanner .bannerBox .bannerBoxHeadline { - display: none; - } - .contentPage .contentPageBanner .bannerBox .bread { - width: 90%; - } - .pageBannerMobile { - display: inline-block; - width: 100%; - border: none; - } - img.pageBanner { - display: none; - } - .contentTableLeft { - width: 100%; - padding: 0; - margin: 0; - } - .contentTableRight { - display: none; - } - .contentTableLeft>div>#navBreadcrumb { - display: none; - } - .accordion { - padding: 2em; - } - .accordion:after { - display: none; - } - .accordion>ul>li>ul.categoryItems>li>a { - width: 100%; - padding: 1em 0; - } - .accordion .triangleLeft, - .triangleOverlay { - display: none; - } - .linkContactUs { - display: inline-block !important; - text-decoration: underline !important; - padding-bottom: 0; - } - .contentPage>.contentTable .bottomTwoColumn div.column p.contactUs { - display: none; - } - } - - @media only screen and (max-width: 620px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: 1.5em; - } - .contentPage .contentPageBanner .bannerBox .bread { - width: 95%; - } - } - - @media only screen and (max-width: 480px) { - .contentPage { - padding-top: 4em; - padding-bottom: 0; - line-height: 1.2em; - } - .contentTableLeft { - width: 100%; - } - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: 1.5em; - } - .contentPage .contentPageBanner .bannerBox .bread { - display: none; - } - .triangle { - border-top: 0.4em solid transparent; - border-bottom: 0.4em solid transparent; - border-left: 0.4em solid #ee3224; - margin-right: 0.6em; - } - .categoryItems { - padding: 0; - } - .accordion { - padding-bottom: 0; - } - .accordion div.mobileContent .seekJobWrapper { - margin-top: 2em; - text-align: center; - } - .accordion div.mobileContent a.seekJob { - display: block; - padding: 1.2em; - } - } - - @media only screen and (max-width: 380px) { - .contentPage>.contentTable .productCustomers .customerIcons img { - height: 2.5em; - } - .contentPage>.contentTable .productCustomers .customerIcons img.shorter { - height: 1.5em; - } - } -} - - -/* NAV SIDEBAR */ - - -/* NAV BREADCRUMB */ - -.breadcrumb { - margin-bottom: 2em; - margin-right: 1em; -} - -.breadcrumb>a:hover { - color: #737373; - text-decoration: underline; -} - -.breadcrumb>a:last-child { - font-weight: bold; - pointer-events: none; -} - - -/* END NAV BREADCRUMB */ - - -/* NAV ACCORDION */ - -.accordion { - opacity: 0; - position: relative; - min-height: 100%; -} - -.accordion:after { - content: ''; - position: absolute; - right: 0; - top: 0; - width: 1px; - height: 100%; - background-color: #DCDCDC; -} - -.accordion>.content { - margin-top: 2em; - padding-left: 0.2em; -} - -.accordion>ul>li.category { - position: relative; - margin-bottom: 1.2em; - list-style: none; - font-size: 0.9em; -} - -.accordion>ul>li.category>a.categoryHead { - margin-bottom: 1.2em; - display: -webkit-box; - display: -ms-flexbox; - display: flex; -} - -.accordion ul li { - list-style: none; -} - - -/*increase space after last item in sub lists*/ - -.accordion>ul>li>ul>li:last-child { - margin-bottom: 1.5em; -} - -.accordion a { - display: block; - text-decoration: none; - vertical-align: middle; - position: relative; -} - -.accordion a:hover { - color: #404040; -} - -.accordion>ul>li>ul>li>a { - font-size: 0.96em; -} - -.accordion>ul>li>ul.categoryItems { - display: none; -} - -.accordion>ul>li>ul.categoryItems>li { - position: relative; - display: inline-block; - width: 100%; - height: auto; - padding: 0; -} - -.accordion>ul>li>ul.categoryItems>li>a { - width: 90%; - padding: 0.8em; - border-bottom: 1px solid gainsboro; - font-size: 1.1em !important; -} - - -/*.accordion > ul > li > ul.categoryItems > li:after { - content: ''; - display: inline-block; - margin-top: 0.6em; - width: 80%; - height: 1px; - background: red; -}*/ - - -/* ACCORDION TRIANGLES */ - -.triangle { - vertical-align: middle; - display: inline-block; - width: 0; - height: 100%; - border-top: 0.5em solid transparent; - border-bottom: 0.5em solid transparent; - border-left: 0.5em solid #ee3224; - margin-top: 0.1em; - margin-right: 1em; - /*transition: .5s all;*/ -} - -.triangleLeft { - width: 0; - height: 0; - position: absolute; - right: 0; - top: 50%; - -webkit-transform: translate(0, -50%); - transform: translate(0, -50%); - border-top: 0.85em solid transparent; - border-bottom: 0.85em solid transparent; - border-right: 0.85em solid #ee3224; - z-index: 2; -} - -.triangleOverlay { - position: absolute; - right: 0; - top: -0.2em; - bottom: -0.2em; - width: 5px; - background: #fff; - z-index: 1; -} - -.rotateRight { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); -} - -.contentPage>.contentTable .contentTableRight h1 { - font-weight: normal; - margin-bottom: 1em; -} - -.contentPage>.contentTable .contentTableRight p.contactUs { - margin-top: 3em; - text-align: right; -} - - -/* MAIN PAGE BANNER */ - -.contentPage img.pageBanner { - width: 100%; - height: auto; -} - -.contentPage .contentPageBannerContainer { - position: relative; -} - -.contentPage .contentPageBannerContainer img { - width: 100%; -} - -.contentPage .contentPageBanner { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; -} - - -/*.contentPage .mobileSolutions { - background-image: url('../images/banner_page_mobile_solutions.jpg'); -}*/ - -.contentPage .contentPageBanner .bannerBox { - position: relative; - width: 50%; -} - -.contentPage .contentPageBanner .bannerBox .bannerBoxHeadline { - text-align: left; - position: absolute; - top: 50%; - left: 0; - transform: translate(0, -50%); -} - -.contentPage .contentPageBanner .bannerBox .bannerBoxHeadline .headlineTop { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: 4em; - width: 95%; -} - -.contentPage .contentPageBanner .bannerBox .bread { - margin-top: 1em; - width: 60%; -} - -.contentPage div.cleaningBox { - text-align: center; -} - -.contentPage .cleaningBox img.chartCircle { - width: 90%; - max-width: 15em; - margin: 1em 0; -} - -.bannerBoxLeft .bannerBoxHeadline { - padding-left: 25%; -} - -.bannerBoxLeft .bread { - width: 80%; -} - - -/* MOBILE */ - -.pageBannerMobileContainer { - width: 100%; - padding: 0; -} - -.pageBannerMobile { - width: 100% !important; - display: none; -} - -.accordion div.mobileContent { - display: none; - margin-bottom: 1em; -} - -.accordion div.mobileContent>h1 { - display: none; -} - -.accordion div.mobileContent ul li { - list-style: disc; - margin-bottom: 0.5em; -} - -.accordion div.mobileContent ul li ul li { - margin: 0.5em 0 0.75em 0; -} - -.accordion div.mobileContent a.linkInContent { - text-decoration: underline; -} - -.accordion div.mobileContent .seekJobWrapper { - position: relative; - text-align: left; - margin-top: 2em; -} - -.accordion div.mobileContent a.seekJob { - display: inline-block; - color: #fff; - background: #000; - border-radius: 10px; - border: none; - padding: 1.2em 3em; - text-decoration: none; -} - - -/* END ACCORDION MOBILE */ - - -/* CONTACT TABLE (KONTAKTA OSS PAGE) */ - -.contentPage>.contentTable .contactTable { - margin-bottom: 2em; -} - -.contentPage>.contentTable .contactTable td img { - height: 1.2em; - margin-right: 0.5em; -} - -.contentPage>.contentTable .contactTable td a { - text-decoration: none; -} - -.contentPage>.contentTable .contactTable h3 { - margin-bottom: 0.2em; -} - - -/* end contact table */ - -.contentPage>.contentTable .locationContainer { - margin-top: 2em; - position: relative; - width: 100%; - height: 20em; -} - -.contentPage>.contentTable .locationContainer>img.locationMarker { - position: absolute; - top: 50%; - left: 50%; - -webkit-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); - height: 4em; -} - - -/*.contentPage > .contentTable .locationMobile img.locationMarker { - height: 1em !important; -}*/ - -.mapInterceptOverlay { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - background: transparent; -} - -.contentPage>.contentTable .locationContainer .findUs { - position: absolute; - bottom: 1.2em; - right: 1.2em; - height: 6em; - text-align: center; -} - -.contentPage>.contentTable .locationContainer .findUs a { - text-align: center; -} - -.contentPage>.contentTable .locationContainer .findUs a img { - margin: 0 auto; - height: 6em; -} - -.contentPage>.contentTable .locationContainer .findUs a div { - position: absolute; - bottom: 0.1em; - width: 100%; - color: white; - font-size: 0.75em; - text-decoration: underline; -} - -.contentPage>.contentTable .contentTableRight>.seekJobWrapper { - position: relative; - text-align: right; - margin-top: 4em; -} - -.contentPage>.contentTable .contentTableRight a.seekJob { - color: #fff; - background: #000; - border-radius: 10px; - border: none; - padding: 1.2em 3em; - text-decoration: none; -} - - -/* BOTTOM TWO COLUMN (FOR CUSTOMERS / CONTACT) */ - -.contentPage>.contentTable .contentTableRight .bottomTwoColumn { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; - margin-top: 2em; -} - -.contentPage>.contentTable .bottomTwoColumn div.column { - width: 50%; -} - -.contentPage>.contentTable .bottomTwoColumn div.column p:first-child { - margin-top: 0 !important; -} - -.contentPage>.contentTable .productCustomers .customerIcons { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.contentPage>.contentTable .productCustomers .customerIcons img { - height: 3em; - margin-right: 1em; -} - -.contentPage>.contentTable .productCustomers .customerIcons img.shorter { - height: 2em; -} - - -/* END BOTTOM TWO COLUMN */ - - -/* MEDIA QUERIES */ - - -/* 现代 CSS 优化和增强功能 */ - -/* 使用 CSS Grid 实现更灵活的布局 */ -.contentPage { - display: grid; - grid-template-columns: var(--sidebar-width) var(--content-width); - gap: 0; -} - -@container content-page (max-width: 768px) { - .contentPage { - grid-template-columns: 1fr; - gap: var(--spacing-lg); - } -} - -/* 使用 CSS 逻辑属性实现更好的国际化支持 */ -.contentTableLeft { - padding-inline-start: var(--sidebar-padding); - padding-block-start: var(--spacing-xl); -} - -.contentTableRight { - padding-inline: var(--content-padding); - padding-block-start: 3.5em; -} - -/* 使用 CSS 自定义属性实现主题切换 */ -:root { - /* 颜色主题 */ - --primary-color: #ee3224; - --secondary-color: #737373; - --border-color: #DCDCDC; - --text-color: #000; - --link-hover-color: #737373; - - /* 动画变量 */ - --transition-fast: 0.2s ease; - --transition-normal: 0.3s ease; - --transition-slow: 0.5s ease; -} - -/* 深色主题支持 */ -@media (prefers-color-scheme: dark) { - :root { - --primary-color: #ff6b6b; - --secondary-color: #a0a0a0; - --border-color: #404040; - --text-color: #ffffff; - --link-hover-color: #ff6b6b; - } -} - -/* 使用 CSS 自定义属性 */ -.breadcrumb>a:hover { - color: var(--link-hover-color); - text-decoration: underline; -} - -.accordion:after { - background-color: var(--border-color); -} - -/* 使用 CSS 逻辑属性实现更好的 RTL 支持 */ -.accordion:after { - inset-inline-end: 0; - inset-block-start: 0; -} - -/* 使用 CSS 逻辑属性实现更好的间距 */ -.accordion>.content { - margin-block-start: var(--spacing-lg); - padding-inline-start: 0.2em; -} - -/* 使用 CSS 逻辑属性实现更好的边框 */ -.accordion>ul>li.category { - margin-block-end: 1.2em; -} - -/* 使用 CSS 逻辑属性实现更好的定位 */ -.accordion>ul>li.category>a.categoryHead { - margin-block-end: 1.2em; -} - -/* 使用 CSS 逻辑属性实现更好的布局 */ -.accordion>ul>li.category>a.categoryHead { - display: flex; - align-items: center; - gap: var(--spacing-xs); -} - -/* 使用 CSS 逻辑属性实现更好的响应式 */ -@container content-page (max-width: 768px) { - .accordion { - padding-inline: var(--spacing-lg); - padding-block: var(--spacing-lg); - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端优化 */ -@container content-page (max-width: 480px) { - .accordion { - padding-block-end: 0; - } - - .accordion div.mobileContent .seekJobWrapper { - margin-block-start: var(--spacing-lg); - text-align: center; - } -} - -/* 使用 CSS 逻辑属性实现更好的图片优化 */ -@container content-page (max-width: 380px) { - .contentPage>.contentTable .productCustomers .customerIcons img { - height: 2.5em; - width: auto; - object-fit: contain; - } - - .contentPage>.contentTable .productCustomers .customerIcons img.shorter { - height: 1.5em; - width: auto; - object-fit: contain; - } -} - -/* 使用 CSS 逻辑属性实现更好的三角形样式 */ -.triangle { - border-block: 0.4em solid transparent; - border-inline-start: 0.4em solid var(--primary-color); - margin-inline-end: 0.6em; -} - -/* 使用 CSS 逻辑属性实现更好的链接样式 */ -.linkContactUs { - display: inline-block !important; - text-decoration: underline !important; - padding-block-end: 0; - color: var(--primary-color); - transition: color var(--transition-normal); -} - -.linkContactUs:hover { - color: var(--link-hover-color); -} - -/* 使用 CSS 逻辑属性实现更好的面包屑样式 */ -.breadcrumb { - margin-block-end: var(--spacing-lg); - margin-inline-end: var(--spacing-sm); -} - -/* 使用 CSS 逻辑属性实现更好的分类项样式 */ -.categoryItems { - padding-inline: 0; - margin-block: 0; -} - -/* 使用 CSS 逻辑属性实现更好的移动端内容样式 */ -.accordion div.mobileContent a.seekJob { - display: block; - padding: 1.2em; - text-align: center; - background: var(--primary-color); - color: white; - text-decoration: none; - border-radius: var(--border-radius); - transition: background-color var(--transition-normal); -} - -.accordion div.mobileContent a.seekJob:hover { - background: var(--link-hover-color); -} - -/* 使用 CSS 逻辑属性实现更好的页面横幅样式 */ -.contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-xl); - line-height: 1.2; - margin-block: var(--spacing-lg); -} - -.contentPage .contentPageBanner .bannerBox .bread { - width: 75%; - margin-block: var(--spacing-md); -} - -/* 使用 CSS 逻辑属性实现更好的移动端横幅样式 */ -@container content-page (max-width: 768px) { - .contentPage .contentPageBanner .bannerBox .bread { - width: 90%; - } -} - -@container content-page (max-width: 620px) { - .contentPage .contentPageBanner .bannerBox .bread { - width: 95%; - } -} - -@container content-page (max-width: 480px) { - .contentPage .contentPageBanner .bannerBox .bread { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的页面内容样式 */ -.contentPage { - padding-block-start: var(--spacing-xxxxl); - line-height: 1.6; -} - -@container content-page (max-width: 768px) { - .contentPage { - padding-block-start: 5.5em; - } -} - -@container content-page (max-width: 480px) { - .contentPage { - padding-block-start: 4em; - padding-block-end: 0; - line-height: 1.2em; - } -} - -/* 使用 CSS 逻辑属性实现更好的表格样式 */ -.contentPage>.contentTable .bottomTwoColumn div.column { - width: 100%; - margin-block-end: var(--spacing-sm); -} - -.contentPage>.contentTable .bottomTwoColumn div.column:first-child { - margin-block-end: var(--spacing-sm); -} - -/* 使用 CSS 逻辑属性实现更好的移动端表格样式 */ -@container content-page (max-width: 1200px) { - .contentPage>.contentTable .bottomTwoColumn div.column { - width: 100%; - } - - .contentPage>.contentTable .bottomTwoColumn div.column:first-child { - margin-block-end: var(--spacing-sm); - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端内容样式 */ -@container content-page (max-width: 768px) { - .contentPage>.contentTable .bottomTwoColumn div.column p.contactUs { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端导航样式 */ -@container content-page (max-width: 768px) { - .contentTableLeft>div>#navBreadcrumb { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端图标样式 */ -@container content-page (max-width: 768px) { - .accordion .triangleLeft, - .triangleOverlay { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端链接样式 */ -@container content-page (max-width: 768px) { - .accordion>ul>li>ul.categoryItems>li>a { - width: 100%; - padding-block: var(--spacing-sm); - padding-inline: 0; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端页面横幅样式 */ -@container content-page (max-width: 768px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxHeadline { - display: none; - } - - .pageBannerMobile { - display: inline-block; - width: 100%; - border: none; - } - - img.pageBanner { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端布局样式 */ -@container content-page (max-width: 768px) { - .contentTableLeft { - width: 100%; - padding: 0; - margin: 0; - } - - .contentTableRight { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端手风琴样式 */ -@container content-page (max-width: 768px) { - .accordion { - padding: var(--spacing-lg); - } - - .accordion:after { - display: none; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端链接样式 */ -@container content-page (max-width: 768px) { - .linkContactUs { - display: inline-block !important; - text-decoration: underline !important; - padding-block-end: 0; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端字体样式 */ -@container content-page (max-width: 620px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-md); - } -} - -@container content-page (max-width: 480px) { - .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { - font-size: var(--font-size-md); - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端三角形样式 */ -@container content-page (max-width: 480px) { - .triangle { - border-block: 0.4em solid transparent; - border-inline-start: 0.4em solid var(--primary-color); - margin-inline-end: 0.6em; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端分类项样式 */ -@container content-page (max-width: 480px) { - .categoryItems { - padding: 0; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端手风琴样式 */ -@container content-page (max-width: 480px) { - .accordion { - padding-block-end: 0; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端求职包装器样式 */ -@container content-page (max-width: 480px) { - .accordion div.mobileContent .seekJobWrapper { - margin-block-start: var(--spacing-lg); - text-align: center; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端求职链接样式 */ -@container content-page (max-width: 480px) { - .accordion div.mobileContent a.seekJob { - display: block; - padding: 1.2em; - } -} - -/* 使用 CSS 逻辑属性实现更好的移动端客户图标样式 */ -@container content-page (max-width: 380px) { - .contentPage>.contentTable .productCustomers .customerIcons img { - height: 2.5em; - } - - .contentPage>.contentTable .productCustomers .customerIcons img.shorter { - height: 1.5em; - } -} - -/* END NEW BY MARK */ \ No newline at end of file diff --git a/src/assets/css/content_pages.less b/src/assets/css/content_pages.less new file mode 100644 index 0000000..e8b7b68 --- /dev/null +++ b/src/assets/css/content_pages.less @@ -0,0 +1,251 @@ +/*PAGE*/ + +/* 导入全局变量 */ +@import './variables.less'; + +/* 使用 LESS 变量定义响应式断点 */ +@breakpoint-xs: 380px; +@breakpoint-sm: 480px; +@breakpoint-md: 620px; +@breakpoint-lg: 768px; +@breakpoint-xl: 992px; +@breakpoint-xxl: 1200px; +@breakpoint-xxxl: 1500px; +@breakpoint-xxxxl: 1600px; + +/* 间距变量 */ +@spacing-xs: 0.5em; +@spacing-sm: 1em; +@spacing-md: 1.5em; +@spacing-lg: 2em; +@spacing-xl: 3em; +@spacing-xxl: 4em; +@spacing-xxxl: 5em; +@spacing-xxxxl: 7em; + +/* 字体大小变量 */ +@font-size-xs: 0.9em; +@font-size-sm: 1.2em; +@font-size-md: 1.5em; +@font-size-lg: 2.5em; +@font-size-xl: 3em; + +/* 布局变量 */ +@sidebar-width: 25%; +@content-width: 75%; +@content-padding: 4em; +@sidebar-padding: 5em; + +.contentPage { + position: relative; + width: 100%; + vertical-align: top; + margin: 0 auto; + padding-top: @spacing-xxxxl; + min-height: 100vh; + + /* 容器查询支持 */ + container-type: inline-size; + container-name: content-page; +} + +/* CONTENT TABLES */ + +.contentTableLeft { + width: @sidebar-width; + padding-left: @sidebar-padding; + padding-top: @spacing-xl; + vertical-align: top; +} + +.contentTableRight { + width: @content-width; + padding-top: 3.5em; + padding-left: @content-padding; + padding-right: @content-padding; + vertical-align: top; +} + +/* 使用容器查询替代媒体查询 */ +@container content-page (max-width: @breakpoint-xxxxl) { + .contentTableRight { + padding-right: 8em; + } +} + +@container content-page (max-width: @breakpoint-xxxl) { + .contentTableRight { + padding-right: 6em; + } + + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-xl; + } +} + +@container content-page (max-width: @breakpoint-xxl) { + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-lg; + } + + .contentPage .contentPageBanner .bannerBox .bread { + width: 75%; + } + + .contentTableLeft { + padding-left: @spacing-xl; + padding-top: @spacing-xl; + } + + .contentTableRight { + padding-left: @spacing-xl; + padding-right: @spacing-xl; + } +} + +@container content-page (max-width: @breakpoint-xl) { + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-md; + } + + .contentPage .contentPageBanner .bannerBox .bread { + width: 85%; + } + + .contentTableLeft { + padding-left: @spacing-lg; + padding-top: @spacing-lg; + } + + .contentTableRight { + padding-left: @spacing-lg; + padding-right: @spacing-lg; + } +} + +@container content-page (max-width: @breakpoint-lg) { + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-sm; + } + + .contentPage .contentPageBanner .bannerBox .bread { + width: 90%; + } + + .contentTableLeft { + padding-left: @spacing-md; + padding-top: @spacing-md; + } + + .contentTableRight { + padding-left: @spacing-md; + padding-right: @spacing-md; + } +} + +@container content-page (max-width: @breakpoint-md) { + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-xs; + } + + .contentPage .contentPageBanner .bannerBox .bread { + width: 95%; + } + + .contentTableLeft { + padding-left: @spacing-sm; + padding-top: @spacing-sm; + } + + .contentTableRight { + padding-left: @spacing-sm; + padding-right: @spacing-sm; + } +} + +@container content-page (max-width: @breakpoint-sm) { + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-xs; + } + + .contentPage .contentPageBanner .bannerBox .bread { + width: 100%; + } + + .contentTableLeft { + padding-left: @spacing-xs; + padding-top: @spacing-xs; + } + + .contentTableRight { + padding-left: @spacing-xs; + padding-right: @spacing-xs; + } +} + +@container content-page (max-width: @breakpoint-xs) { + .contentPage .contentPageBanner .bannerBox .bannerBoxTitle { + font-size: @font-size-xs; + } + + .contentPage .contentPageBanner .bannerBox .bread { + width: 100%; + } + + .contentTableLeft { + padding-left: @spacing-xs; + padding-top: @spacing-xs; + } + + .contentTableRight { + padding-left: @spacing-xs; + padding-right: @spacing-xs; + } +} + +/* 响应式布局调整 */ +@media (max-width: @breakpoint-lg) { + .contentPage { + padding-top: @spacing-xxl; + } + + .contentTableLeft, + .contentTableRight { + width: 100%; + display: block; + } + + .contentTableLeft { + padding-bottom: @spacing-lg; + } +} + +@media (max-width: @breakpoint-md) { + .contentPage { + padding-top: @spacing-xl; + } + + .contentTableLeft { + padding-bottom: @spacing-md; + } +} + +@media (max-width: @breakpoint-sm) { + .contentPage { + padding-top: @spacing-lg; + } + + .contentTableLeft { + padding-bottom: @spacing-sm; + } +} + +@media (max-width: @breakpoint-xs) { + .contentPage { + padding-top: @spacing-md; + } + + .contentTableLeft { + padding-bottom: @spacing-xs; + } +} diff --git a/src/assets/css/mobile_responsive.less b/src/assets/css/mobile_responsive.less new file mode 100644 index 0000000..fcb6925 --- /dev/null +++ b/src/assets/css/mobile_responsive.less @@ -0,0 +1,602 @@ +// 移动端响应式适配样式 - LESS版本 +@import './variables.less'; + +// ===== 移动端断点变量 ===== +// 使用全局变量文件中的断点 + +// ===== 移动端断点 ===== +@media (max-width: @mobile-breakpoint) { + // 头部导航移动端适配 + .header { + padding: 10px 0; + + .content { + padding: 0 15px; + } + } + + // 桌面端导航隐藏 + .desktop { + display: none !important; + } + + // 移动端导航显示 + .mobile { + display: flex !important; + } + + // 移动端导航样式 + .mobile-nav { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + .logoCell { + .logo { + height: 40px; + width: auto; + } + } + + .mobileIconsCell { + cursor: pointer; + + img { + width: 24px; + height: 24px; + } + } + + // 移动端菜单样式 + .headerMobileMenu { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + background: rgba(0, 0, 0, 0.9); + z-index: 1000; + display: none; + overflow-y: auto; + + &.active { + display: block; + } + + .menuContent { + padding: 80px 20px 20px; + color: white; + + ul { + list-style: none; + padding: 0; + margin: 0; + } + + li { + margin: 15px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + padding-bottom: 15px; + } + + a { + color: white; + text-decoration: none; + font-size: 18px; + display: block; + padding: 10px 0; + } + + .hasSubmenu { + color: #3498db; + font-weight: bold; + margin-bottom: 10px; + } + + .subMenu { + margin-left: 20px; + margin-top: 10px; + + li { + border-bottom: none; + margin: 10px 0; + padding-bottom: 5px; + + a { + font-size: 16px; + color: rgba(255, 255, 255, 0.8); + } + } + } + } + + .closeMenu { + position: absolute; + top: 20px; + right: 20px; + background: none; + border: none; + color: white; + font-size: 24px; + cursor: pointer; + padding: 10px; + + &:hover { + color: #3498db; + } + } + + .goBack { + margin-top: 30px; + padding-top: 20px; + border-top: 2px solid rgba(255, 255, 255, 0.3); + text-align: center; + + a { + color: white; + text-decoration: none; + font-size: 16px; + display: inline-flex; + align-items: center; + gap: 8px; + + &:hover { + color: #3498db; + } + + img { + width: 16px; + height: 16px; + filter: invert(1); + } + } + } + } + + // 移动端横幅适配 + .banner { + height: 60vh; + min-height: 400px; + + .banner-content { + padding: 40px 20px; + text-align: center; + + .banner-title { + font-size: 2.5rem; + line-height: 1.2; + margin-bottom: 20px; + } + + .banner-subtitle { + font-size: 1.2rem; + line-height: 1.4; + margin-bottom: 30px; + } + + .banner-button { + padding: 12px 24px; + font-size: 16px; + } + } + } + + // 移动端内容区域适配 + .pageWidth { + padding: 0 15px; + } + + .section { + padding: 40px 0; + + .section-title { + font-size: 2rem; + line-height: 1.3; + margin-bottom: 20px; + } + + .section-subtitle { + font-size: 1.1rem; + line-height: 1.5; + margin-bottom: 30px; + } + } + + // 移动端网格布局适配 + .grid, + .advantages-grid, + .areas-grid, + .services-grid, + .team-grid, + .contact-grid { + grid-template-columns: 1fr; + gap: 20px; + } + + // 移动端卡片适配 + .card, + .advantage-card, + .area-card, + .service-card, + .team-card, + .contact-card { + padding: 20px; + margin-bottom: 20px; + + .card-title { + font-size: 1.3rem; + margin-bottom: 15px; + } + + .card-content { + font-size: 1rem; + line-height: 1.6; + } + } + + // 移动端按钮适配 + .btn, + .button { + padding: 12px 24px; + font-size: 16px; + min-height: 44px; // 触摸友好尺寸 + + &.btn-large { + padding: 15px 30px; + font-size: 18px; + } + + &.btn-small { + padding: 8px 16px; + font-size: 14px; + } + } + + // 移动端表单适配 + .form-group { + margin-bottom: 20px; + + label { + display: block; + margin-bottom: 8px; + font-weight: 500; + } + + input, + textarea, + select { + width: 100%; + padding: 12px; + font-size: 16px; // 防止iOS缩放 + border: 1px solid #ddd; + border-radius: 4px; + min-height: 44px; // 触摸友好尺寸 + + &:focus { + outline: none; + border-color: #3498db; + box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); + } + } + + textarea { + min-height: 100px; + resize: vertical; + } + } + + // 移动端图片适配 + img { + max-width: 100%; + height: auto; + } + + // 移动端图标适配 + .icon { + width: 24px; + height: 24px; + + &.icon-large { + width: 32px; + height: 32px; + } + + &.icon-small { + width: 16px; + height: 16px; + } + } + + // 移动端间距适配 + .mt-mobile { margin-top: 20px !important; } + .mb-mobile { margin-bottom: 20px !important; } + .ml-mobile { margin-left: 15px !important; } + .mr-mobile { margin-right: 15px !important; } + + .pt-mobile { padding-top: 20px !important; } + .pb-mobile { padding-bottom: 20px !important; } + .pl-mobile { padding-left: 15px !important; } + .pr-mobile { padding-right: 15px !important; } + + // 移动端隐藏元素 + .hide-mobile { + display: none !important; + } + + // 移动端显示元素 + .show-mobile { + display: block !important; + } + + // 移动端文本对齐 + .text-center-mobile { + text-align: center !important; + } + + .text-left-mobile { + text-align: left !important; + } + + .text-right-mobile { + text-align: right !important; + } +} + +// ===== 平板端适配 ===== +@media (min-width: @mobile-breakpoint + 1) and (max-width: @tablet-breakpoint) { + .pageWidth { + padding: 0 30px; + } + + .grid, + .advantages-grid, + .areas-grid, + .services-grid, + .team-grid, + .contact-grid { + grid-template-columns: repeat(2, 1fr); + gap: 25px; + } + + .section { + padding: 50px 0; + } + + .banner { + height: 70vh; + + .banner-content { + padding: 60px 40px; + + .banner-title { + font-size: 3rem; + } + + .banner-subtitle { + font-size: 1.4rem; + } + } + } +} + +// ===== 小屏幕移动端适配 ===== +@media (max-width: @small-mobile) { + .pageWidth { + padding: 0 10px; + } + + .banner { + .banner-content { + padding: 30px 15px; + + .banner-title { + font-size: 2rem; + } + + .banner-subtitle { + font-size: 1rem; + } + } + } + + .section { + padding: 30px 0; + + .section-title { + font-size: 1.8rem; + } + + .section-subtitle { + font-size: 1rem; + } + } + + .card, + .advantage-card, + .area-card, + .service-card, + .team-card, + .contact-card { + padding: 15px; + + .card-title { + font-size: 1.2rem; + } + } + + .btn, + .button { + padding: 10px 20px; + font-size: 15px; + } +} + +// ===== 超小屏幕移动端适配 ===== +@media (max-width: @xs-mobile) { + .pageWidth { + padding: 0 8px; + } + + .banner { + .banner-content { + padding: 25px 10px; + + .banner-title { + font-size: 1.8rem; + } + + .banner-subtitle { + font-size: 0.9rem; + } + } + } + + .section { + padding: 25px 0; + + .section-title { + font-size: 1.6rem; + } + + .section-subtitle { + font-size: 0.9rem; + } + } + + .card, + .advantage-card, + .area-card, + .service-card, + .team-card, + .contact-card { + padding: 12px; + + .card-title { + font-size: 1.1rem; + } + } + + .btn, + .button { + padding: 8px 16px; + font-size: 14px; + } +} + +// ===== 触摸设备优化 ===== +@media (hover: none) and (pointer: coarse) { + // 触摸设备禁用悬停效果 + .hover-lift:hover, + .hover-scale:hover, + .hover-glow:hover, + .hover-rotate:hover, + .hover-bounce:hover { + transform: none; + box-shadow: none; + } + + // 触摸设备增加触摸反馈 + .btn, + .button, + .card, + .advantage-card, + .area-card, + .service-card, + .team-card, + .contact-card { + -webkit-tap-highlight-color: rgba(52, 152, 219, 0.3); + + &:active { + transform: scale(0.98); + transition: transform 0.1s ease; + } + } + + // 触摸设备优化菜单项 + .menuContent { + li { + a { + padding: 15px 0; // 增加触摸区域 + min-height: 44px; // 触摸友好尺寸 + display: flex; + align-items: center; + } + } + } +} + +// ===== 高分辨率屏幕适配 ===== +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .logo { + image-rendering: -webkit-optimize-contrast; + image-rendering: crisp-edges; + } + + .icon { + image-rendering: -webkit-optimize-contrast; + image-rendering: crisp-edges; + } +} + +// ===== 横屏模式适配 ===== +@media (orientation: landscape) and (max-height: 500px) { + .headerMobileMenu { + .menuContent { + padding: 60px 20px 20px; + } + + .closeMenu { + top: 15px; + right: 15px; + } + } + + .banner { + height: 100vh; + + .banner-content { + padding: 30px 20px; + + .banner-title { + font-size: 2rem; + margin-bottom: 15px; + } + + .banner-subtitle { + font-size: 1rem; + margin-bottom: 20px; + } + } + } +} + +// ===== 打印样式 ===== +@media print { + .headerMobileMenu, + .mobile-nav, + .floating-particles, + .scroll-progress-bar { + display: none !important; + } + + .pageWidth { + padding: 0 !important; + } + + .section { + padding: 20px 0 !important; + page-break-inside: avoid; + } + + .card, + .advantage-card, + .area-card, + .service-card, + .team-card, + .contact-card { + page-break-inside: avoid; + margin-bottom: 15px !important; + } +} diff --git a/src/assets/css/variables.less b/src/assets/css/variables.less new file mode 100644 index 0000000..4360051 --- /dev/null +++ b/src/assets/css/variables.less @@ -0,0 +1,279 @@ +// ===== LESS 变量定义文件 ===== + +// ===== 颜色变量 ===== +@primary-color: #3498db; +@primary-light: #5dade2; +@primary-dark: #2980b9; +@primary-lighter: #ebf3fd; + +@success-color: #52c41a; +@success-light: #73d13d; +@success-dark: #389e0d; +@success-lighter: #f6ffed; + +@warning-color: #faad14; +@warning-light: #ffc53d; +@warning-dark: #d48806; +@warning-lighter: #fffbe6; + +@error-color: #f5222d; +@error-light: #ff4d4f; +@error-dark: #cf1322; +@error-lighter: #fff2f0; + +@info-color: #1890ff; +@info-light: #40a9ff; +@info-dark: #096dd9; +@info-lighter: #e6f7ff; + +// 中性色 +@white: #ffffff; +@black: #000000; +@gray-1: #fafafa; +@gray-2: #f5f5f5; +@gray-3: #f0f0f0; +@gray-4: #d9d9d9; +@gray-5: #bfbfbf; +@gray-6: #8c8c8c; +@gray-7: #595959; +@gray-8: #434343; +@gray-9: #262626; +@gray-10: #1f1f1f; + +// 文字颜色 +@text-color: @gray-8; +@text-color-secondary: @gray-6; +@text-color-disabled: @gray-5; +@heading-color: @gray-9; +@link-color: @primary-color; +@link-hover-color: @primary-light; + +// 背景颜色 +@bg-color: @white; +@bg-color-light: @gray-1; +@bg-color-dark: @gray-2; + +// 边框颜色 +@border-color: @gray-4; +@border-color-light: @gray-3; +@border-color-dark: @gray-5; + +// ===== 字体变量 ===== +@font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +@font-family-code: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; + +@font-size-base: 14px; +@font-size-lg: 16px; +@font-size-sm: 12px; +@font-size-xs: 10px; + +@font-size-h1: 38px; +@font-size-h2: 30px; +@font-size-h3: 24px; +@font-size-h4: 20px; +@font-size-h5: 16px; +@font-size-h6: 14px; + +@line-height-base: 1.5715; +@line-height-lg: 1.5; +@line-height-sm: 1.4; +@line-height-xs: 1.3; + +@font-weight-light: 300; +@font-weight-normal: 400; +@font-weight-medium: 500; +@font-weight-semibold: 600; +@font-weight-bold: 700; +@font-weight-extrabold: 800; + +// ===== 间距变量 ===== +@spacing-xs: 4px; +@spacing-sm: 8px; +@spacing-md: 16px; +@spacing-lg: 24px; +@spacing-xl: 32px; +@spacing-xxl: 48px; +@spacing-xxxl: 64px; + +@padding-xs: @spacing-xs; +@padding-sm: @spacing-sm; +@padding-md: @spacing-md; +@padding-lg: @spacing-lg; +@padding-xl: @spacing-xl; + +@margin-xs: @spacing-xs; +@margin-sm: @spacing-sm; +@margin-md: @spacing-md; +@margin-lg: @spacing-lg; +@margin-xl: @spacing-xl; + +// ===== 尺寸变量 ===== +@border-radius-base: 4px; +@border-radius-sm: 2px; +@border-radius-lg: 8px; +@border-radius-xl: 12px; +@border-radius-circle: 50%; + +@border-width-base: 1px; +@border-width-thick: 2px; +@border-width-thin: 0.5px; + +// ===== 阴影变量 ===== +@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); +@box-shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.1); +@box-shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.2); +@box-shadow-xl: 0 8px 24px rgba(0, 0, 0, 0.25); + +@text-shadow-base: 0 1px 2px rgba(0, 0, 0, 0.1); +@text-shadow-lg: 0 2px 4px rgba(0, 0, 0, 0.2); + +// ===== 断点变量 ===== +@screen-xs: 480px; +@screen-sm: 576px; +@screen-md: 768px; +@screen-lg: 992px; +@screen-xl: 1200px; +@screen-xxl: 1600px; + +@mobile-breakpoint: @screen-md; +@tablet-breakpoint: @screen-lg; +@desktop-breakpoint: @screen-xl; + +// 移动端断点变量 +@small-mobile: 480px; +@xs-mobile: 360px; + +// ===== 动画变量 ===== +@animation-duration-base: 0.2s; +@animation-duration-fast: 0.1s; +@animation-duration-slow: 0.3s; +@animation-duration-slower: 0.5s; + +@animation-timing-base: ease; +@animation-timing-ease-in: ease-in; +@animation-timing-ease-out: ease-out; +@animation-timing-ease-in-out: ease-in-out; +@animation-timing-linear: linear; + +@animation-delay-base: 0s; +@animation-delay-fast: 0.1s; +@animation-delay-slow: 0.3s; + +// ===== Z-index 变量 ===== +@zindex-dropdown: 1000; +@zindex-sticky: 1020; +@zindex-fixed: 1030; +@zindex-modal-backdrop: 1040; +@zindex-modal: 1050; +@zindex-popover: 1060; +@zindex-tooltip: 1070; +@zindex-toast: 1080; + +// ===== 组件特定变量 ===== + +// 按钮 +@btn-height-base: 32px; +@btn-height-lg: 40px; +@btn-height-sm: 24px; +@btn-padding-horizontal-base: @padding-md; +@btn-padding-horizontal-lg: @padding-lg; +@btn-padding-horizontal-sm: @padding-sm; + +// 输入框 +@input-height-base: 32px; +@input-height-lg: 40px; +@input-height-sm: 24px; +@input-padding-horizontal: @padding-sm; + +// 卡片 +@card-padding-base: @padding-lg; +@card-padding-sm: @padding-md; +@card-padding-lg: @padding-xl; + +// 表格 +@table-padding-vertical: @padding-sm; +@table-padding-horizontal: @padding-md; + +// 分页 +@pagination-item-size: 32px; +@pagination-item-size-sm: 24px; + +// 标签 +@tag-height: 20px; +@tag-height-lg: 28px; +@tag-height-sm: 16px; + +// 徽章 +@badge-height: 20px; +@badge-height-sm: 14px; +@badge-height-lg: 24px; + +// ===== 工具函数 ===== + +// 颜色混合函数 +.lighten(@color, @amount) { + color: lighten(@color, @amount); +} + +.darken(@color, @amount) { + color: darken(@color, @amount); +} + +// 透明度函数 +.alpha(@color, @opacity) { + color: fade(@color, @opacity); +} + +// 响应式混合 +.mobile(@rules) { + @media (max-width: @mobile-breakpoint) { + @rules(); + } +} + +.tablet(@rules) { + @media (min-width: @mobile-breakpoint + 1) and (max-width: @tablet-breakpoint) { + @rules(); + } +} + +.desktop(@rules) { + @media (min-width: @tablet-breakpoint + 1) { + @rules(); + } +} + +// 触摸设备检测 +.touch-device(@rules) { + @media (hover: none) and (pointer: coarse) { + @rules(); + } +} + +// 高分辨率屏幕 +.high-dpi(@rules) { + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + @rules(); + } +} + +// 打印样式 +.print(@rules) { + @media print { + @rules(); + } +} + +// 横屏模式 +.landscape(@rules) { + @media (orientation: landscape) and (max-height: 500px) { + @rules(); + } +} + +// 减少动画偏好 +.reduced-motion(@rules) { + @media (prefers-reduced-motion: reduce) { + @rules(); + } +} diff --git a/src/components/ihead.vue b/src/components/ihead.vue index f4b9841..0d66029 100644 --- a/src/components/ihead.vue +++ b/src/components/ihead.vue @@ -116,6 +116,7 @@ export default { mounted() { this.initHeader() this.initScrollListener() + this.initMobileMenu() }, methods: { // 封装事件监听器创建,提高代码复用性 @@ -197,6 +198,52 @@ export default { } } }); + }, + + // 初始化移动端菜单 + initMobileMenu() { + // 汉堡菜单点击事件 + this.add_event_listener("hamburger", "click", () => { + this.toggle_class("mobileMenu", "active", true); + }, true); + + // 返回按钮点击事件 + this.add_event_listener(".linkGoBack", "click", () => { + this.remove_class("mobileMenu", "active", true); + // 关闭移动端菜单后滚动到顶部 + window.scrollTo({ + top: 0, + left: 0, + behavior: 'smooth' + }); + }); + + // 点击菜单项后自动关闭菜单 + const menuLinks = document.querySelectorAll('.headerMobileMenu a'); + menuLinks.forEach(link => { + link.addEventListener('click', () => { + this.remove_class("mobileMenu", "active", true); + }); + }); + + // 点击菜单外部区域关闭菜单 + document.addEventListener('click', (e) => { + const mobileMenu = document.getElementById('mobileMenu'); + const hamburger = document.getElementById('hamburger'); + + if (mobileMenu && mobileMenu.classList.contains('active') && + !mobileMenu.contains(e.target) && + !hamburger.contains(e.target)) { + this.remove_class("mobileMenu", "active", true); + } + }); + + // ESC键关闭菜单 + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + this.remove_class("mobileMenu", "active", true); + } + }); } } } diff --git a/src/main.js b/src/main.js index 02721a0..05eb3a0 100644 --- a/src/main.js +++ b/src/main.js @@ -1,9 +1,13 @@ import './assets/css/cookieconsent.min.css' -import './assets/css/common.css' -import './assets/css/animations.css' +import './assets/css/common.less' +import './assets/css/content_pages.less' +import './assets/css/animations.less' +import './assets/css/mobile_responsive.less' +import './assets/css/common_mobile.less' import 'swiper/css/swiper.min.css' import 'es6-promise/auto' import './static/animation_helper' +import './static/scroll_helper' import Vue from 'vue' import App from './App.vue' import Vuex from 'vuex' diff --git a/src/static/scroll_helper.js b/src/static/scroll_helper.js new file mode 100644 index 0000000..6618472 --- /dev/null +++ b/src/static/scroll_helper.js @@ -0,0 +1,250 @@ +/** + * 滚动控制助手 - 处理页面刷新和路由切换时的滚动行为 + */ +class ScrollHelper { + constructor() { + this.isInitialized = false; + this.init(); + } + + /** + * 初始化滚动助手 + */ + init() { + if (this.isInitialized) return; + + // 监听页面加载完成事件 + this.handlePageLoad(); + + // 监听路由变化事件 + this.handleRouteChange(); + + // 监听页面刷新事件 + this.handlePageRefresh(); + + // 监听浏览器前进后退事件 + this.handleBrowserNavigation(); + + this.isInitialized = true; + } + + /** + * 处理页面加载完成 + */ + handlePageLoad() { + // 页面完全加载后滚动到顶部 + if (document.readyState === 'complete') { + this.scrollToTop(); + } else { + window.addEventListener('load', () => { + this.scrollToTop(); + }); + } + + // DOM内容加载完成后也滚动到顶部 + document.addEventListener('DOMContentLoaded', () => { + this.scrollToTop(); + }); + } + + /** + * 处理路由变化 + */ + handleRouteChange() { + // 监听 Vue Router 的全局前置守卫 + if (window.Vue && window.Vue.prototype.$router) { + window.Vue.prototype.$router.beforeEach((to, from, next) => { + // 路由切换时滚动到顶部 + this.scrollToTop(); + next(); + }); + } + } + + /** + * 处理页面刷新 + */ + handlePageRefresh() { + // 监听页面即将刷新事件 + window.addEventListener('beforeunload', () => { + // 保存当前滚动位置到 sessionStorage + sessionStorage.setItem('scrollPosition', window.pageYOffset); + }); + + // 页面加载完成后检查是否需要滚动到顶部 + window.addEventListener('load', () => { + const savedPosition = sessionStorage.getItem('scrollPosition'); + if (savedPosition) { + // 清除保存的位置 + sessionStorage.removeItem('scrollPosition'); + // 滚动到顶部 + this.scrollToTop(); + } + }); + } + + /** + * 处理浏览器前进后退 + */ + handleBrowserNavigation() { + // 监听 popstate 事件(浏览器前进后退) + window.addEventListener('popstate', () => { + // 延迟执行,确保路由更新完成 + setTimeout(() => { + this.scrollToTop(); + }, 100); + }); + } + + /** + * 滚动到页面顶部 + */ + scrollToTop(options = {}) { + const defaultOptions = { + top: 0, + left: 0, + behavior: 'smooth' + }; + + const config = { ...defaultOptions, ...options }; + + // 检查浏览器是否支持平滑滚动 + if (!this.supportsSmoothScrolling()) { + config.behavior = 'auto'; + } + + // 执行滚动 + window.scrollTo(config); + + // 同时滚动 body 和 html 元素(兼容性处理) + if (document.body) { + document.body.scrollTop = 0; + document.body.scrollLeft = 0; + } + if (document.documentElement) { + document.documentElement.scrollTop = 0; + document.documentElement.scrollLeft = 0; + } + } + + /** + * 滚动到指定元素 + */ + scrollToElement(selector, options = {}) { + const element = document.querySelector(selector); + if (!element) return; + + const defaultOptions = { + behavior: 'smooth', + block: 'start', + inline: 'nearest' + }; + + const config = { ...defaultOptions, ...options }; + + // 检查浏览器是否支持 scrollIntoView + if (element.scrollIntoView) { + element.scrollIntoView(config); + } else { + // 降级处理:计算元素位置并滚动 + const rect = element.getBoundingClientRect(); + const scrollTop = window.pageYOffset + rect.top; + this.scrollToTop({ top: scrollTop, behavior: config.behavior }); + } + } + + /** + * 检查浏览器是否支持平滑滚动 + */ + supportsSmoothScrolling() { + return 'scrollBehavior' in document.documentElement.style; + } + + /** + * 获取当前滚动位置 + */ + getScrollPosition() { + return { + x: window.pageXOffset || document.documentElement.scrollLeft, + y: window.pageYOffset || document.documentElement.scrollTop + }; + } + + /** + * 设置滚动位置 + */ + setScrollPosition(x, y, behavior = 'smooth') { + const options = { + top: y, + left: x, + behavior: this.supportsSmoothScrolling() ? behavior : 'auto' + }; + + window.scrollTo(options); + } + + /** + * 滚动到底部 + */ + scrollToBottom(behavior = 'smooth') { + const scrollHeight = Math.max( + document.body.scrollHeight, + document.documentElement.scrollHeight, + document.body.offsetHeight, + document.documentElement.offsetHeight, + document.body.clientHeight, + document.documentElement.clientHeight + ); + + this.scrollToTop({ + top: scrollHeight, + behavior: behavior + }); + } + + /** + * 滚动到指定百分比位置 + */ + scrollToPercentage(percentage, behavior = 'smooth') { + const scrollHeight = Math.max( + document.body.scrollHeight, + document.documentElement.scrollHeight + ); + const targetPosition = (scrollHeight - window.innerHeight) * (percentage / 100); + + this.scrollToTop({ + top: targetPosition, + behavior: behavior + }); + } + + /** + * 监听滚动事件 + */ + onScroll(callback) { + window.addEventListener('scroll', callback, { passive: true }); + } + + /** + * 移除滚动监听器 + */ + offScroll(callback) { + window.removeEventListener('scroll', callback); + } + + /** + * 销毁滚动助手 + */ + destroy() { + // 清理事件监听器 + this.isInitialized = false; + } +} + +// 创建全局实例 +window.scrollHelper = new ScrollHelper(); + +// 导出类(如果使用模块系统) +if (typeof module !== 'undefined' && module.exports) { + module.exports = ScrollHelper; +} diff --git a/src/views/index.vue b/src/views/index.vue index 851d4fe..cfd507a 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -7,13 +7,27 @@