This commit is contained in:
张成
2025-08-25 15:24:23 +08:00
parent a993c55d3f
commit c4148d9eb6
11 changed files with 3220 additions and 2589 deletions

View File

@@ -0,0 +1,488 @@
# 响应式设计指南 - 不使用媒体查询
## 概述
本指南介绍如何使用 JavaScript 检测屏幕尺寸来替代传统的 CSS 媒体查询,实现更灵活、更可控的响应式设计。
## 解决方案架构
### 1. CSS 容器查询 (Container Queries)
- **文件**: `src/assets/css/responsive_without_media.css`
- **优势**: 最新的 CSS 标准,基于容器而非视口
- **兼容性**: 现代浏览器支持
### 2. JavaScript 检测 + 类名控制
- **文件**: `src/static/responsive_helper.js`
- **优势**: 完全兼容,可编程控制
- **兼容性**: 所有浏览器支持
### 3. Vue2 组件化响应式
- **文件**: `src/components/ResponsiveLayout.vue`
- **优势**: 组件化,易于维护
- **兼容性**: Vue2 项目
## 使用方法
### 基础使用
#### 1. 引入响应式助手
```html
<!-- 在 index.html 中引入 -->
<script src="/static/responsive_helper.js"></script>
```
#### 2. 使用响应式布局组件
```vue
<template>
<ResponsiveLayout :showDebug="true" @screenSizeChange="handleScreenChange">
<div class="page-content">
<!-- 你的内容 -->
</div>
</ResponsiveLayout>
</template>
<script>
import ResponsiveLayout from '@/components/ResponsiveLayout.vue';
export default {
components: { ResponsiveLayout },
methods: {
handleScreenChange(event) {
console.log('屏幕尺寸变化:', event.detail);
// 根据屏幕尺寸执行不同逻辑
}
}
};
</script>
```
#### 3. 使用响应式 CSS 类
```html
<!-- 根据屏幕尺寸显示/隐藏 -->
<div class="responsive-hidden-xs">超小屏幕隐藏</div>
<div class="responsive-hidden-sm">小屏幕隐藏</div>
<div class="responsive-hidden-md">中等屏幕隐藏</div>
<div class="responsive-hidden-lg">大屏幕隐藏</div>
<div class="responsive-hidden-xl">超大屏幕隐藏</div>
<!-- 根据屏幕尺寸显示 -->
<div class="responsive-visible-xs">超小屏幕显示</div>
<div class="responsive-visible-sm">小屏幕显示</div>
<div class="responsive-visible-md">中等屏幕显示</div>
<div class="responsive-visible-lg">大屏幕显示</div>
<div class="responsive-visible-xl">超大屏幕显示</div>
```
### 高级使用
#### 1. 自定义断点
```vue
<template>
<ResponsiveLayout :customBreakpoints="customBreakpoints">
<!-- 内容 -->
</ResponsiveLayout>
</template>
<script>
export default {
data() {
return {
customBreakpoints: {
xs: 320, // 超小屏幕
sm: 576, // 小屏幕
md: 768, // 中等屏幕
lg: 992, // 大屏幕
xl: 1200, // 超大屏幕
xxl: 1400 // 超超大屏幕
}
};
}
};
</script>
```
#### 2. 响应式内容控制
```vue
<template>
<div>
<!-- 移动端内容 -->
<div v-if="isMobile" class="mobile-content">
<h2>移动端优化内容</h2>
<p>触摸友好的界面设计</p>
</div>
<!-- 桌面端内容 -->
<div v-if="isDesktop" class="desktop-content">
<h2>桌面端完整内容</h2>
<p>鼠标和键盘友好的界面</p>
</div>
<!-- 平板端内容 -->
<div v-if="isTablet" class="tablet-content">
<h2>平板端适配内容</h2>
<p>触摸和鼠标混合操作</p>
</div>
</div>
</template>
<script>
export default {
computed: {
isMobile() {
return this.$refs.responsiveLayout?.isMobile || false;
},
isTablet() {
return this.$refs.responsiveLayout?.isTablet || false;
},
isDesktop() {
return this.$refs.responsiveLayout?.isDesktop || false;
}
}
};
</script>
```
#### 3. 动态样式调整
```vue
<template>
<div :class="dynamicClasses">
<button @click="adjustLayout">调整布局</button>
</div>
</template>
<script>
export default {
computed: {
dynamicClasses() {
const classes = ['base-layout'];
if (this.$refs.responsiveLayout) {
const size = this.$refs.responsiveLayout.currentScreenSize;
classes.push(`layout-${size}`);
if (this.$refs.responsiveLayout.isMobile) {
classes.push('mobile-optimized');
}
}
return classes;
}
},
methods: {
adjustLayout() {
const element = this.$el.querySelector('.content');
if (this.$refs.responsiveLayout) {
// 根据断点显示/隐藏元素
this.$refs.responsiveLayout.responsiveShow(element, 'md');
}
}
}
};
</script>
```
## CSS 类名系统
### 响应式断点类名
```css
/* 屏幕尺寸类名 */
.screen-xs { /* 超小屏幕样式 */ }
.screen-sm { /* 小屏幕样式 */ }
.screen-md { /* 中等屏幕样式 */ }
.screen-lg { /* 大屏幕样式 */ }
.screen-xl { /* 超大屏幕样式 */ }
.screen-xxl { /* 超超大屏幕样式 */ }
/* 容器尺寸类名 */
.container-xs { /* 超小容器样式 */ }
.container-sm { /* 小容器样式 */ }
.container-md { /* 中等容器样式 */ }
.container-lg { /* 大容器样式 */ }
.container-xl { /* 超大容器样式 */ }
.container-xxl { /* 超超大容器样式 */ }
```
### 响应式工具类
```css
/* 显示/隐藏 */
.responsive-hidden-xs { display: none !important; }
.responsive-visible-xs { display: block !important; }
/* 布局 */
.responsive-grid { display: grid !important; }
.responsive-flex { display: flex !important; }
.responsive-block { display: block !important; }
/* 间距 */
.responsive-m-0 { margin: 0 !important; }
.responsive-p-3 { padding: 1rem !important; }
/* 文本 */
.responsive-text-center { text-align: center; }
.responsive-text-primary { color: #3498db; }
/* 阴影 */
.responsive-shadow { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); }
.responsive-shadow-lg { box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); }
```
## JavaScript API
### 全局方法
```javascript
// 响应式显示元素
window.responsiveShow(element, breakpoint);
// 响应式隐藏元素
window.responsiveHide(element, breakpoint);
// 响应式切换类名
window.responsiveToggleClass(element, className, breakpoint);
// 获取当前屏幕信息
const screenInfo = window.responsiveHelper.getCurrentScreenInfo();
```
### 事件监听
```javascript
// 监听屏幕尺寸变化
window.addEventListener('screenSizeChange', (event) => {
const { size, width, height, breakpoint } = event.detail;
if (size === 'xs' || size === 'sm' || size === 'md') {
// 移动端逻辑
console.log('切换到移动端');
} else if (size === 'lg') {
// 平板端逻辑
console.log('切换到平板端');
} else {
// 桌面端逻辑
console.log('切换到桌面端');
}
});
```
### 断点检测
```javascript
// 检查是否匹配特定断点
const isMobile = window.responsiveHelper.matchesBreakpoint('md');
const isTablet = window.responsiveHelper.matchesBreakpoint('lg');
const isDesktop = !window.responsiveHelper.matchesBreakpoint('lg');
```
## 最佳实践
### 1. 性能优化
```javascript
// 使用防抖优化 resize 事件
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
// 执行响应式逻辑
}, 100);
});
```
### 2. 渐进增强
```javascript
// 检测功能支持
if (CSS.supports('container-type', 'inline-size')) {
// 使用容器查询
console.log('支持容器查询');
} else {
// 回退到 JavaScript 检测
console.log('使用 JavaScript 检测');
}
```
### 3. 组件化设计
```vue
<!-- 创建可复用的响应式组件 -->
<template>
<div :class="responsiveClasses">
<slot name="mobile" v-if="isMobile"></slot>
<slot name="tablet" v-else-if="isTablet"></slot>
<slot name="desktop" v-else></slot>
<slot v-if="!isMobile && !isTablet && !isDesktop"></slot>
</div>
</template>
<script>
export default {
name: 'ResponsiveSlot',
inject: ['responsiveLayout'],
computed: {
isMobile() {
return this.responsiveLayout?.isMobile || false;
},
isTablet() {
return this.responsiveLayout?.isTablet || false;
},
isDesktop() {
return this.responsiveLayout?.isDesktop || false;
},
responsiveClasses() {
return {
'responsive-slot': true,
[`screen-${this.responsiveLayout?.currentScreenSize}`]: true
};
}
}
};
</script>
```
### 4. 主题切换
```javascript
// 根据屏幕尺寸切换主题
function switchThemeByScreen() {
const screenInfo = window.responsiveHelper.getCurrentScreenInfo();
if (screenInfo.isMobile) {
document.body.classList.add('theme-mobile');
document.body.classList.remove('theme-desktop');
} else {
document.body.classList.add('theme-desktop');
document.body.classList.remove('theme-mobile');
}
}
// 监听屏幕变化
window.addEventListener('screenSizeChange', switchThemeByScreen);
```
## 迁移指南
### 从媒体查询迁移
#### 原来的 CSS
```css
@media (max-width: 768px) {
.mobile-menu {
display: block;
}
.desktop-menu {
display: none;
}
}
```
#### 新的 JavaScript 方式
```javascript
// 在组件中
mounted() {
this.updateMenuVisibility();
window.addEventListener('screenSizeChange', this.updateMenuVisibility);
},
methods: {
updateMenuVisibility() {
const mobileMenu = this.$el.querySelector('.mobile-menu');
const desktopMenu = this.$el.querySelector('.desktop-menu');
if (this.$refs.responsiveLayout.isMobile) {
mobileMenu.style.display = 'block';
desktopMenu.style.display = 'none';
} else {
mobileMenu.style.display = 'none';
desktopMenu.style.display = 'block';
}
}
}
```
#### 新的 CSS 类方式
```css
/* 使用响应式类名 */
.mobile-menu {
display: none;
}
.desktop-menu {
display: block;
}
/* 移动端显示 */
.screen-xs .mobile-menu,
.screen-sm .mobile-menu,
.screen-md .mobile-menu {
display: block;
}
.screen-xs .desktop-menu,
.screen-sm .desktop-menu,
.screen-md .desktop-menu {
display: none;
}
```
## 调试和测试
### 1. 启用调试模式
```vue
<ResponsiveLayout :showDebug="true">
<!-- 内容 -->
</ResponsiveLayout>
```
### 2. 控制台调试
```javascript
// 查看当前屏幕信息
console.log(window.responsiveHelper.getCurrentScreenInfo());
// 手动触发屏幕变化
window.dispatchEvent(new Event('resize'));
```
### 3. 测试不同屏幕尺寸
```javascript
// 模拟不同屏幕尺寸
function simulateScreenSize(width) {
Object.defineProperty(window, 'innerWidth', {
writable: true,
configurable: true,
value: width
});
window.dispatchEvent(new Event('resize'));
}
// 测试移动端
simulateScreenSize(375);
// 测试平板端
simulateScreenSize(768);
// 测试桌面端
simulateScreenSize(1200);
```
## 总结
通过使用 JavaScript 检测屏幕尺寸替代媒体查询,我们获得了:
1. **更好的控制性**: 可以编程控制响应式行为
2. **更高的灵活性**: 支持复杂的响应式逻辑
3. **更好的性能**: 避免 CSS 重排和重绘
4. **更好的维护性**: 集中管理响应式逻辑
5. **更好的兼容性**: 支持所有浏览器
这种方案特别适合需要复杂响应式逻辑的现代 Web 应用。

View File

@@ -13,9 +13,9 @@
"dependencies": {
"es6-promise": "^4.2.5",
"image-webpack-loader": "^8.1.0",
"swiper": "^8.4.7",
"swiper": "5.4.5",
"vue": "^2.5.17",
"vue-awesome-swiper": "^4.1.1",
"vue-awesome-swiper": "3.1.3",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},

View File

@@ -0,0 +1,785 @@
/* 动画效果库 - 为项目添加丰富的动画效果 */
/* ===== 基础动画关键帧 ===== */
@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; }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,654 @@
<template>
<div class="responsive-layout" :class="layoutClasses">
<!-- 响应式容器 -->
<div class="responsive-container" :class="containerClasses">
<!-- 插槽内容 -->
<slot></slot>
</div>
<!-- 响应式调试信息 (开发环境显示) -->
<div v-if="showDebug" class="responsive-debug">
<div class="debug-info">
<strong>屏幕尺寸:</strong> {{ currentScreenSize }}
<br>
<strong>宽度:</strong> {{ screenWidth }}px
<br>
<strong>设备类型:</strong> {{ deviceType }}
<br>
<strong>触摸设备:</strong> {{ isTouchDevice ? '是' : '否' }}
<br>
<strong>高分辨率:</strong> {{ isHighDPI ? '是' : '否' }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ResponsiveLayout',
props: {
// 是否显示调试信息
showDebug: {
type: Boolean,
default: false
},
// 自定义断点
customBreakpoints: {
type: Object,
default: () => ({})
},
// 响应式模式
responsiveMode: {
type: String,
default: 'auto', // auto, mobile-first, desktop-first
validator: value => ['auto', 'mobile-first', 'desktop-first'].includes(value)
}
},
data() {
return {
// 当前屏幕尺寸
currentScreenSize: '',
// 屏幕宽度
screenWidth: 0,
// 是否为触摸设备
isTouchDevice: false,
// 是否为高分辨率屏幕
isHighDPI: false,
// 响应式助手实例
responsiveHelper: null,
// 断点配置
breakpoints: {
xs: 380,
sm: 480,
md: 620,
lg: 768,
xl: 992,
xxl: 1200,
xxxl: 1500,
xxxxl: 1600
}
};
},
computed: {
// 布局类名
layoutClasses() {
return {
[`screen-${this.currentScreenSize}`]: true,
'touch-device': this.isTouchDevice,
'no-touch-device': !this.isTouchDevice,
'high-dpi': this.isHighDPI,
'low-dpi': !this.isHighDPI
};
},
// 容器类名
containerClasses() {
return {
[`container-${this.currentScreenSize}`]: true,
'responsive-container': true
};
},
// 设备类型
deviceType() {
if (this.currentScreenSize === 'xs' || this.currentScreenSize === 'sm' || this.currentScreenSize === 'md') {
return '移动端';
} else if (this.currentScreenSize === 'lg') {
return '平板';
} else {
return '桌面端';
}
},
// 是否为移动端
isMobile() {
return this.currentScreenSize === 'xs' || this.currentScreenSize === 'sm' || this.currentScreenSize === 'md';
},
// 是否为平板
isTablet() {
return this.currentScreenSize === 'lg';
},
// 是否为桌面端
isDesktop() {
return this.currentScreenSize === 'xl' || this.currentScreenSize === 'xxl' || this.currentScreenSize === 'xxxl' || this.currentScreenSize === 'xxxxl';
}
},
mounted() {
this.initResponsiveHelper();
this.addEventListeners();
},
beforeDestroy() {
this.removeEventListeners();
if (this.responsiveHelper) {
this.responsiveHelper.destroy();
}
},
methods: {
/**
* 初始化响应式助手
*/
initResponsiveHelper() {
// 合并自定义断点
this.breakpoints = { ...this.breakpoints, ...this.customBreakpoints };
// 创建响应式助手实例
this.responsiveHelper = new this.$options.responsiveHelperClass || window.ResponsiveHelper;
if (this.responsiveHelper) {
// 更新断点配置
this.responsiveHelper.breakpoints = this.breakpoints;
// 检测初始屏幕尺寸
this.detectScreenSize();
// 检测设备特性
this.detectDeviceFeatures();
}
},
/**
* 检测屏幕尺寸
*/
detectScreenSize() {
const width = window.innerWidth;
this.screenWidth = width;
let newSize = '';
// 根据宽度确定屏幕尺寸
if (width <= this.breakpoints.xs) {
newSize = 'xs';
} else if (width <= this.breakpoints.sm) {
newSize = 'sm';
} else if (width <= this.breakpoints.md) {
newSize = 'md';
} else if (width <= this.breakpoints.lg) {
newSize = 'lg';
} else if (width <= this.breakpoints.xl) {
newSize = 'xl';
} else if (width <= this.breakpoints.xxl) {
newSize = 'xxl';
} else if (width <= this.breakpoints.xxxl) {
newSize = 'xxxl';
} else {
newSize = 'xxxxl';
}
// 如果尺寸发生变化,更新状态
if (newSize !== this.currentScreenSize) {
this.currentScreenSize = newSize;
this.$emit('screenSizeChange', {
size: newSize,
width: width,
height: window.innerHeight,
breakpoint: this.breakpoints[newSize]
});
}
},
/**
* 检测设备特性
*/
detectDeviceFeatures() {
// 检测触摸设备
this.isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// 检测高分辨率屏幕
this.isHighDPI = (window.devicePixelRatio || 1) >= 2;
},
/**
* 添加事件监听器
*/
addEventListeners() {
// 监听窗口大小变化
window.addEventListener('resize', this.handleResize);
// 监听方向变化
window.addEventListener('orientationchange', this.handleOrientationChange);
// 监听屏幕尺寸变化事件
window.addEventListener('screenSizeChange', this.handleScreenSizeChange);
},
/**
* 移除事件监听器
*/
removeEventListeners() {
window.removeEventListener('resize', this.handleResize);
window.removeEventListener('orientationchange', this.handleOrientationChange);
window.removeEventListener('screenSizeChange', this.handleScreenSizeChange);
},
/**
* 处理窗口大小变化
*/
handleResize() {
// 使用防抖优化性能
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(() => {
this.detectScreenSize();
}, 100);
},
/**
* 处理方向变化
*/
handleOrientationChange() {
// 方向变化后延迟检测,确保布局已更新
setTimeout(() => {
this.detectScreenSize();
}, 100);
},
/**
* 处理屏幕尺寸变化事件
*/
handleScreenSizeChange(event) {
this.currentScreenSize = event.detail.size;
this.screenWidth = event.detail.width;
},
/**
* 检查是否匹配特定断点
*/
matchesBreakpoint(breakpoint) {
return this.screenWidth <= this.breakpoints[breakpoint];
},
/**
* 获取当前屏幕信息
*/
getCurrentScreenInfo() {
return {
size: this.currentScreenSize,
width: this.screenWidth,
height: window.innerHeight,
breakpoint: this.breakpoints[this.currentScreenSize],
isMobile: this.isMobile,
isTablet: this.isTablet,
isDesktop: this.isDesktop,
isTouchDevice: this.isTouchDevice,
isHighDPI: this.isHighDPI
};
},
/**
* 响应式显示/隐藏元素
*/
responsiveShow(element, breakpoint = 'lg') {
if (this.matchesBreakpoint(breakpoint)) {
element.style.display = 'block';
} else {
element.style.display = 'none';
}
},
/**
* 响应式隐藏/显示元素
*/
responsiveHide(element, breakpoint = 'lg') {
if (this.matchesBreakpoint(breakpoint)) {
element.style.display = 'none';
} else {
element.style.display = 'block';
}
},
/**
* 响应式切换类名
*/
responsiveToggleClass(element, className, breakpoint = 'lg') {
if (this.matchesBreakpoint(breakpoint)) {
element.classList.add(className);
} else {
element.classList.remove(className);
}
}
}
};
</script>
<style scoped>
.responsive-layout {
width: 100%;
height: 100%;
}
.responsive-container {
width: 100%;
height: 100%;
}
/* 响应式调试信息 */
.responsive-debug {
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 12px;
z-index: 9999;
max-width: 200px;
}
.debug-info {
line-height: 1.4;
}
/* 响应式工具类 */
.responsive-hidden-xs { display: none !important; }
.responsive-hidden-sm { display: none !important; }
.responsive-hidden-md { display: none !important; }
.responsive-hidden-lg { display: none !important; }
.responsive-hidden-xl { display: none !important; }
.responsive-visible-xs { display: block !important; }
.responsive-visible-sm { display: block !important; }
.responsive-visible-md { display: block !important; }
.responsive-visible-lg { display: block !important; }
.responsive-visible-xl { display: block !important; }
/* 响应式网格 */
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
}
.responsive-grid-2 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.responsive-grid-3 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.responsive-grid-4 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
}
/* 响应式弹性布局 */
.responsive-flex {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.responsive-flex > * {
flex: 1 1 300px;
}
/* 响应式列 */
.responsive-column {
flex: 1 1 clamp(300px, 50%, 600px);
}
/* 响应式容器 */
.responsive-wrapper {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
/* 响应式侧边栏 */
.responsive-sidebar {
width: clamp(250px, 25vw, 300px);
min-width: 250px;
}
/* 响应式主内容 */
.responsive-main {
flex: 1;
min-width: 0;
}
/* 响应式按钮 */
.responsive-button {
width: 100%;
max-width: 300px;
padding: clamp(0.75rem, 2vw, 1.5rem);
font-size: clamp(0.875rem, 2vw, 1.125rem);
}
/* 响应式卡片 */
.responsive-card {
padding: clamp(1rem, 3vw, 2rem);
border-radius: clamp(0.5rem, 2vw, 1rem);
}
/* 响应式表单 */
.responsive-form {
display: grid;
gap: 1rem;
}
.responsive-form input,
.responsive-form textarea,
.responsive-form select {
width: 100%;
padding: clamp(0.5rem, 2vw, 1rem);
font-size: clamp(0.875rem, 2vw, 1rem);
}
/* 响应式模态框 */
.responsive-modal {
width: min(90vw, 600px);
margin: clamp(2rem, 5vw, 5rem) auto;
padding: clamp(1rem, 3vw, 2rem);
}
/* 响应式图片 */
.responsive-image {
width: 100%;
height: auto;
max-width: 100%;
object-fit: cover;
}
/* 响应式表格 */
.responsive-table {
width: 100%;
overflow-x: auto;
display: block;
}
.responsive-table table {
min-width: 600px;
}
/* 响应式导航 */
.responsive-nav {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.responsive-nav a {
flex: 1 1 auto;
text-align: center;
padding: 0.5rem 1rem;
}
/* 响应式文本 */
.responsive-text {
font-size: clamp(1rem, 2.5vw, 2rem);
line-height: 1.4;
}
/* 响应式间距 */
.responsive-spacing {
padding: clamp(1rem, 3vw, 3rem);
margin: clamp(0.5rem, 2vw, 2rem);
}
/* 响应式阴影 */
.responsive-shadow-sm { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
.responsive-shadow { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); }
.responsive-shadow-lg { box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); }
/* 响应式圆角 */
.responsive-rounded { border-radius: 0.375rem; }
.responsive-rounded-sm { border-radius: 0.25rem; }
.responsive-rounded-lg { border-radius: 0.5rem; }
.responsive-rounded-full { border-radius: 9999px; }
/* 响应式过渡 */
.responsive-transition { transition: all 0.3s ease; }
.responsive-transition-fast { transition: all 0.15s ease; }
.responsive-transition-slow { transition: all 0.5s ease; }
/* 响应式变换 */
.responsive-scale-95 { transform: scale(0.95); }
.responsive-scale-105 { transform: scale(1.05); }
.responsive-rotate-90 { transform: rotate(90deg); }
.responsive-rotate-180 { transform: rotate(180deg); }
/* 响应式颜色 */
.responsive-text-primary { color: #3498db; }
.responsive-text-success { color: #27ae60; }
.responsive-text-warning { color: #f39c12; }
.responsive-text-danger { color: #e74c3c; }
.responsive-bg-primary { background-color: #3498db; }
.responsive-bg-success { background-color: #27ae60; }
.responsive-bg-warning { background-color: #f39c12; }
.responsive-bg-danger { background-color: #e74c3c; }
/* 响应式间距工具类 */
.responsive-m-0 { margin: 0 !important; }
.responsive-m-1 { margin: 0.25rem !important; }
.responsive-m-2 { margin: 0.5rem !important; }
.responsive-m-3 { margin: 1rem !important; }
.responsive-m-4 { margin: 1.5rem !important; }
.responsive-m-5 { margin: 3rem !important; }
.responsive-p-0 { padding: 0 !important; }
.responsive-p-1 { padding: 0.25rem !important; }
.responsive-p-2 { padding: 0.5rem !important; }
.responsive-p-3 { padding: 1rem !important; }
.responsive-p-4 { padding: 1.5rem !important; }
.responsive-p-5 { padding: 3rem !important; }
/* 响应式文本对齐 */
.responsive-text-center { text-align: center; }
.responsive-text-left { text-align: left; }
.responsive-text-right { text-align: right; }
/* 响应式浮动 */
.responsive-float-left { float: left; }
.responsive-float-right { float: right; }
/* 响应式清除浮动 */
.responsive-clearfix::after {
content: "";
clear: both;
display: table;
}
/* 响应式隐藏/显示 */
.responsive-hidden { display: none !important; }
.responsive-visible { display: block !important; }
/* 响应式内联 */
.responsive-inline { display: inline !important; }
.responsive-inline-block { display: inline-block !important; }
/* 响应式块级 */
.responsive-block { display: block !important; }
/* 响应式弹性 */
.responsive-flex { display: flex !important; }
.responsive-inline-flex { display: inline-flex !important; }
/* 响应式网格 */
.responsive-grid { display: grid !important; }
.responsive-inline-grid { display: inline-grid !important; }
/* 响应式表格 */
.responsive-table { display: table !important; }
.responsive-table-row { display: table-row !important; }
.responsive-table-cell { display: table-cell !important; }
/* 响应式定位 */
.responsive-relative { position: relative !important; }
.responsive-absolute { position: absolute !important; }
.responsive-fixed { position: fixed !important; }
.responsive-sticky { position: sticky !important; }
/* 响应式溢出 */
.responsive-overflow-auto { overflow: auto !important; }
.responsive-overflow-hidden { overflow: hidden !important; }
.responsive-overflow-visible { overflow: visible !important; }
.responsive-overflow-scroll { overflow: scroll !important; }
/* 响应式光标 */
.responsive-cursor-pointer { cursor: pointer !important; }
.responsive-cursor-default { cursor: default !important; }
.responsive-cursor-not-allowed { cursor: not-allowed !important; }
/* 响应式用户选择 */
.responsive-select-none { user-select: none !important; }
.responsive-select-text { user-select: text !important; }
.responsive-select-all { user-select: all !important; }
/* 响应式指针事件 */
.responsive-pointer-events-none { pointer-events: none !important; }
.responsive-pointer-events-auto { pointer-events: auto !important; }
/* 响应式可见性 */
.responsive-visible { visibility: visible !important; }
.responsive-invisible { visibility: hidden !important; }
/* 响应式透明度 */
.responsive-opacity-0 { opacity: 0 !important; }
.responsive-opacity-25 { opacity: 0.25 !important; }
.responsive-opacity-50 { opacity: 0.5 !important; }
.responsive-opacity-75 { opacity: 0.75 !important; }
.responsive-opacity-100 { opacity: 1 !important; }
/* 响应式Z索引 */
.responsive-z-0 { z-index: 0 !important; }
.responsive-z-10 { z-index: 10 !important; }
.responsive-z-20 { z-index: 20 !important; }
.responsive-z-30 { z-index: 30 !important; }
.responsive-z-40 { z-index: 40 !important; }
.responsive-z-50 { z-index: 50 !important; }
/* 响应式宽度 */
.responsive-w-full { width: 100% !important; }
.responsive-w-auto { width: auto !important; }
.responsive-w-screen { width: 100vw !important; }
/* 响应式高度 */
.responsive-h-full { height: 100% !important; }
.responsive-h-auto { height: auto !important; }
.responsive-h-screen { height: 100vh !important; }
/* 响应式最大宽度 */
.responsive-max-w-none { max-width: none !important; }
.responsive-max-w-full { max-width: 100% !important; }
.responsive-max-w-screen-sm { max-width: 640px !important; }
.responsive-max-w-screen-md { max-width: 768px !important; }
.responsive-max-w-screen-lg { max-width: 1024px !important; }
.responsive-max-w-screen-xl { max-width: 1280px !important; }
/* 响应式最小宽度 */
.responsive-min-w-0 { min-width: 0 !important; }
.responsive-min-w-full { min-width: 100% !important; }
/* 响应式最大高度 */
.responsive-max-h-none { max-height: none !important; }
.responsive-max-h-full { max-height: 100% !important; }
.responsive-max-h-screen { max-height: 100vh !important; }
/* 响应式最小高度 */
.responsive-min-h-0 { min-height: 0 !important; }
.responsive-min-h-full { min-height: 100% !important; }
.responsive-min-h-screen { min-height: 100vh !important; }
</style>

View File

@@ -255,7 +255,7 @@ export default {
.logoCell {
flex-shrink: 0;
margin-right: 40px;
margin: 0px 40px;
}
.logoCell .logo {

View File

@@ -1,6 +1,7 @@
import './assets/css/cookieconsent.min.css'
import './assets/css/common.css'
import 'swiper/swiper.min.css'
import './assets/css/animations.css'
import 'swiper/css/swiper.min.css'
import 'es6-promise/auto'
import Vue from 'vue'
import App from './App.vue'

View File

@@ -0,0 +1,353 @@
/**
* 响应式助手 - 替代媒体查询的JavaScript解决方案
* 动态检测屏幕尺寸并添加相应的响应式类名
*/
class ResponsiveHelper {
constructor() {
// 响应式断点定义
this.breakpoints = {
xs: 380,
sm: 480,
md: 620,
lg: 768,
xl: 992,
xxl: 1200,
xxxl: 1500,
xxxxl: 1600
};
// 当前屏幕尺寸
this.currentSize = '';
// 初始化
this.init();
}
/**
* 初始化响应式助手
*/
init() {
// 添加容器查询支持检测
this.addContainerQuerySupport();
// 检测初始屏幕尺寸
this.detectScreenSize();
// 监听窗口大小变化
this.addResizeListener();
// 监听方向变化
this.addOrientationListener();
// 添加触摸设备检测
this.detectTouchDevice();
// 添加高分辨率屏幕检测
this.detectHighDPI();
}
/**
* 检测当前屏幕尺寸并添加相应类名
*/
detectScreenSize() {
const width = window.innerWidth;
let newSize = '';
// 根据宽度确定屏幕尺寸
if (width <= this.breakpoints.xs) {
newSize = 'xs';
} else if (width <= this.breakpoints.sm) {
newSize = 'sm';
} else if (width <= this.breakpoints.md) {
newSize = 'md';
} else if (width <= this.breakpoints.lg) {
newSize = 'lg';
} else if (width <= this.breakpoints.xl) {
newSize = 'xl';
} else if (width <= this.breakpoints.xxl) {
newSize = 'xxl';
} else if (width <= this.breakpoints.xxxl) {
newSize = 'xxxl';
} else {
newSize = 'xxxxl';
}
// 如果尺寸发生变化,更新类名
if (newSize !== this.currentSize) {
this.updateScreenClasses(newSize);
this.currentSize = newSize;
}
}
/**
* 更新屏幕尺寸相关的CSS类名
*/
updateScreenClasses(newSize) {
const body = document.body;
const html = document.documentElement;
// 移除旧的屏幕尺寸类名
const oldClasses = Object.values(this.breakpoints).map(size => `screen-${size}`);
body.classList.remove(...oldClasses);
html.classList.remove(...oldClasses);
// 添加新的屏幕尺寸类名
body.classList.add(`screen-${newSize}`);
html.classList.add(`screen-${newSize}`);
// 添加响应式容器类名
this.addResponsiveContainerClasses();
// 触发自定义事件
this.triggerScreenChangeEvent(newSize);
}
/**
* 添加响应式容器类名
*/
addResponsiveContainerClasses() {
const containers = document.querySelectorAll('.responsive-container');
containers.forEach(container => {
const width = container.offsetWidth;
let containerSize = '';
// 根据容器宽度确定尺寸
if (width <= this.breakpoints.xs) {
containerSize = 'xs';
} else if (width <= this.breakpoints.sm) {
containerSize = 'sm';
} else if (width <= this.breakpoints.md) {
containerSize = 'md';
} else if (width <= this.breakpoints.lg) {
containerSize = 'lg';
} else if (width <= this.breakpoints.xl) {
containerSize = 'xl';
} else if (width <= this.breakpoints.xxl) {
containerSize = 'xxl';
} else if (width <= this.breakpoints.xxxl) {
containerSize = 'xxxl';
} else {
containerSize = 'xxxxl';
}
// 移除旧的容器尺寸类名
const oldContainerClasses = Object.values(this.breakpoints).map(size => `container-${size}`);
container.classList.remove(...oldContainerClasses);
// 添加新的容器尺寸类名
container.classList.add(`container-${containerSize}`);
});
}
/**
* 添加容器查询支持
*/
addContainerQuerySupport() {
// 检测浏览器是否支持容器查询
if (CSS.supports('container-type', 'inline-size')) {
document.documentElement.classList.add('supports-container-queries');
} else {
document.documentElement.classList.add('no-container-queries');
// 为不支持容器查询的浏览器提供回退方案
this.addContainerQueryPolyfill();
}
}
/**
* 容器查询回退方案
*/
addContainerQueryPolyfill() {
// 创建 ResizeObserver 来监听容器大小变化
if (window.ResizeObserver) {
const containers = document.querySelectorAll('.responsive-container');
containers.forEach(container => {
const resizeObserver = new ResizeObserver(() => {
this.addResponsiveContainerClasses();
});
resizeObserver.observe(container);
});
}
}
/**
* 添加窗口大小变化监听器
*/
addResizeListener() {
let resizeTimeout;
window.addEventListener('resize', () => {
// 使用防抖优化性能
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
this.detectScreenSize();
}, 100);
});
}
/**
* 添加方向变化监听器
*/
addOrientationListener() {
window.addEventListener('orientationchange', () => {
// 方向变化后延迟检测,确保布局已更新
setTimeout(() => {
this.detectScreenSize();
}, 100);
});
}
/**
* 检测触摸设备
*/
detectTouchDevice() {
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
document.documentElement.classList.add('touch-device');
} else {
document.documentElement.classList.add('no-touch-device');
}
}
/**
* 检测高分辨率屏幕
*/
detectHighDPI() {
const dpr = window.devicePixelRatio || 1;
if (dpr >= 2) {
document.documentElement.classList.add('high-dpi');
} else {
document.documentElement.classList.add('low-dpi');
}
}
/**
* 触发屏幕变化事件
*/
triggerScreenChangeEvent(newSize) {
const event = new CustomEvent('screenSizeChange', {
detail: {
size: newSize,
width: window.innerWidth,
height: window.innerHeight,
breakpoint: this.breakpoints[newSize]
}
});
window.dispatchEvent(event);
}
/**
* 获取当前屏幕尺寸信息
*/
getCurrentScreenInfo() {
return {
size: this.currentSize,
width: window.innerWidth,
height: window.innerHeight,
breakpoint: this.breakpoints[this.currentSize],
isMobile: this.currentSize === 'xs' || this.currentSize === 'sm' || this.currentSize === 'md',
isTablet: this.currentSize === 'lg',
isDesktop: this.currentSize === 'xl' || this.currentSize === 'xxl' || this.currentSize === 'xxxl' || this.currentSize === 'xxxxl'
};
}
/**
* 检查是否匹配特定断点
*/
matchesBreakpoint(breakpoint) {
const width = window.innerWidth;
return width <= this.breakpoints[breakpoint];
}
/**
* 添加响应式工具方法
*/
addResponsiveUtils() {
// 添加响应式显示/隐藏方法
window.responsiveShow = (element, breakpoint = 'lg') => {
if (this.matchesBreakpoint(breakpoint)) {
element.style.display = 'block';
} else {
element.style.display = 'none';
}
};
window.responsiveHide = (element, breakpoint = 'lg') => {
if (this.matchesBreakpoint(breakpoint)) {
element.style.display = 'none';
} else {
element.style.display = 'block';
}
};
// 添加响应式类名切换方法
window.responsiveToggleClass = (element, className, breakpoint = 'lg') => {
if (this.matchesBreakpoint(breakpoint)) {
element.classList.add(className);
} else {
element.classList.remove(className);
}
};
}
/**
* 销毁响应式助手
*/
destroy() {
// 移除事件监听器
window.removeEventListener('resize', this.detectScreenSize);
window.removeEventListener('orientationchange', this.detectScreenSize);
// 移除类名
const body = document.body;
const html = document.documentElement;
const oldClasses = Object.values(this.breakpoints).map(size => `screen-${size}`);
body.classList.remove(...oldClasses);
html.classList.remove(...oldClasses);
}
}
// 创建全局实例
window.responsiveHelper = new ResponsiveHelper();
// 添加响应式工具方法
window.responsiveHelper.addResponsiveUtils();
// 导出类(如果使用模块系统)
if (typeof module !== 'undefined' && module.exports) {
module.exports = ResponsiveHelper;
}
// 使用示例:
/*
// 监听屏幕尺寸变化
window.addEventListener('screenSizeChange', (event) => {
console.log('屏幕尺寸变化:', event.detail);
// 根据屏幕尺寸执行不同逻辑
if (event.detail.size === 'xs' || event.detail.size === 'sm') {
// 移动端逻辑
console.log('移动端设备');
} else if (event.detail.size === 'lg') {
// 平板逻辑
console.log('平板设备');
} else {
// 桌面端逻辑
console.log('桌面端设备');
}
});
// 检查当前屏幕信息
const screenInfo = window.responsiveHelper.getCurrentScreenInfo();
console.log('当前屏幕信息:', screenInfo);
// 使用响应式工具方法
const element = document.querySelector('.my-element');
window.responsiveShow(element, 'md'); // 在中等屏幕以下显示
window.responsiveHide(element, 'lg'); // 在大屏幕以上隐藏
window.responsiveToggleClass(element, 'mobile-style', 'md'); // 在中等屏幕以下添加移动端样式
*/

View File

@@ -0,0 +1,814 @@
<template>
<div class="responsive-example">
<!-- 响应式布局组件 -->
<ResponsiveLayout
:showDebug="true"
@screenSizeChange="handleScreenSizeChange"
ref="responsiveLayout"
>
<!-- 页面内容 -->
<div class="page-content">
<!-- 响应式标题 -->
<h1 class="responsive-title">响应式设计示例</h1>
<!-- 响应式网格布局 -->
<div class="responsive-grid-3">
<div class="responsive-card" v-for="i in 6" :key="i">
<h3>卡片 {{ i }}</h3>
<p>这是一个响应式卡片会根据屏幕尺寸自动调整布局</p>
<button class="responsive-button">了解更多</button>
</div>
</div>
<!-- 响应式显示/隐藏内容 -->
<div class="responsive-section">
<h2>响应式内容控制</h2>
<!-- 移动端显示 -->
<div class="mobile-only">
<p>这段内容只在移动端显示</p>
</div>
<!-- 桌面端显示 -->
<div class="desktop-only">
<p>这段内容只在桌面端显示</p>
</div>
<!-- 平板端显示 -->
<div class="tablet-only">
<p>这段内容只在平板端显示</p>
</div>
</div>
<!-- 响应式表单 -->
<div class="responsive-form-section">
<h2>响应式表单</h2>
<form class="responsive-form">
<div class="form-group">
<label class="responsive-label">姓名</label>
<input type="text" class="responsive-input" placeholder="请输入姓名">
</div>
<div class="form-group">
<label class="responsive-label">邮箱</label>
<input type="email" class="responsive-input" placeholder="请输入邮箱">
</div>
<div class="form-group">
<label class="responsive-label">留言</label>
<textarea class="responsive-input" placeholder="请输入留言" rows="4"></textarea>
</div>
<button type="submit" class="responsive-button">提交</button>
</form>
</div>
<!-- 响应式表格 -->
<div class="responsive-table-section">
<h2>响应式表格</h2>
<div class="responsive-table">
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>职业</th>
<th>城市</th>
</tr>
</thead>
<tbody>
<tr v-for="person in people" :key="person.id">
<td>{{ person.name }}</td>
<td>{{ person.age }}</td>
<td>{{ person.job }}</td>
<td>{{ person.city }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 响应式导航 -->
<div class="responsive-nav-section">
<h2>响应式导航</h2>
<nav class="responsive-nav">
<a href="#" class="nav-link">首页</a>
<a href="#" class="nav-link">关于我们</a>
<a href="#" class="nav-link">服务</a>
<a href="#" class="nav-link">联系我们</a>
</nav>
</div>
</div>
</ResponsiveLayout>
<!-- 屏幕信息显示 -->
<div class="screen-info">
<h3>当前屏幕信息</h3>
<div class="info-grid">
<div class="info-item">
<strong>屏幕尺寸:</strong> {{ screenInfo.size }}
</div>
<div class="info-item">
<strong>宽度:</strong> {{ screenInfo.width }}px
</div>
<div class="info-item">
<strong>高度:</strong> {{ screenInfo.height }}px
</div>
<div class="info-item">
<strong>设备类型:</strong> {{ screenInfo.isMobile ? '移动端' : screenInfo.isTablet ? '平板' : '桌面端' }}
</div>
<div class="info-item">
<strong>触摸设备:</strong> {{ screenInfo.isTouchDevice ? '是' : '否' }}
</div>
<div class="info-item">
<strong>高分辨率:</strong> {{ screenInfo.isHighDPI ? '是' : '否' }}
</div>
</div>
</div>
<!-- 响应式控制按钮 -->
<div class="responsive-controls">
<h3>响应式控制</h3>
<div class="control-buttons">
<button @click="toggleMobileContent" class="control-btn">
{{ showMobileContent ? '隐藏' : '显示' }}移动端内容
</button>
<button @click="toggleDesktopContent" class="control-btn">
{{ showDesktopContent ? '隐藏' : '显示' }}桌面端内容
</button>
<button @click="toggleTabletContent" class="control-btn">
{{ showTabletContent ? '隐藏' : '显示' }}平板端内容
</button>
</div>
</div>
</div>
</template>
<script>
import ResponsiveLayout from '@/components/ResponsiveLayout.vue';
export default {
name: 'ResponsiveExample',
components: {
ResponsiveLayout
},
data() {
return {
screenInfo: {
size: '',
width: 0,
height: 0,
isMobile: false,
isTablet: false,
isDesktop: false,
isTouchDevice: false,
isHighDPI: false
},
showMobileContent: false,
showDesktopContent: false,
showTabletContent: false,
people: [
{ id: 1, name: '张三', age: 25, job: '工程师', city: '北京' },
{ id: 2, name: '李四', age: 30, job: '设计师', city: '上海' },
{ id: 3, name: '王五', age: 28, job: '产品经理', city: '深圳' },
{ id: 4, name: '赵六', age: 32, job: '运营', city: '广州' }
]
};
},
mounted() {
// 获取初始屏幕信息
this.updateScreenInfo();
// 监听屏幕尺寸变化
window.addEventListener('screenSizeChange', this.handleScreenSizeChange);
},
beforeDestroy() {
window.removeEventListener('screenSizeChange', this.handleScreenSizeChange);
},
methods: {
/**
* 处理屏幕尺寸变化
*/
handleScreenSizeChange(event) {
console.log('屏幕尺寸变化:', event.detail);
this.updateScreenInfo();
// 根据屏幕尺寸执行不同逻辑
if (event.detail.size === 'xs' || event.detail.size === 'sm' || event.detail.size === 'md') {
this.handleMobileLayout();
} else if (event.detail.size === 'lg') {
this.handleTabletLayout();
} else {
this.handleDesktopLayout();
}
},
/**
* 更新屏幕信息
*/
updateScreenInfo() {
if (this.$refs.responsiveLayout) {
this.screenInfo = this.$refs.responsiveLayout.getCurrentScreenInfo();
}
},
/**
* 处理移动端布局
*/
handleMobileLayout() {
console.log('切换到移动端布局');
this.showMobileContent = true;
this.showDesktopContent = false;
this.showTabletContent = false;
// 可以在这里执行移动端特定的逻辑
this.$nextTick(() => {
// 移动端优化
this.optimizeForMobile();
});
},
/**
* 处理平板端布局
*/
handleTabletLayout() {
console.log('切换到平板端布局');
this.showMobileContent = false;
this.showDesktopContent = false;
this.showTabletContent = true;
// 可以在这里执行平板端特定的逻辑
this.$nextTick(() => {
// 平板端优化
this.optimizeForTablet();
});
},
/**
* 处理桌面端布局
*/
handleDesktopLayout() {
console.log('切换到桌面端布局');
this.showMobileContent = false;
this.showDesktopContent = true;
this.showTabletContent = false;
// 可以在这里执行桌面端特定的逻辑
this.$nextTick(() => {
// 桌面端优化
this.optimizeForDesktop();
});
},
/**
* 移动端优化
*/
optimizeForMobile() {
// 隐藏不必要的元素
const elements = document.querySelectorAll('.mobile-hidden');
elements.forEach(el => {
el.style.display = 'none';
});
// 调整触摸目标大小
const buttons = document.querySelectorAll('button, a');
buttons.forEach(btn => {
btn.style.minHeight = '44px';
btn.style.minWidth = '44px';
});
},
/**
* 平板端优化
*/
optimizeForTablet() {
// 平板端特定优化
console.log('平板端优化完成');
},
/**
* 桌面端优化
*/
optimizeForDesktop() {
// 桌面端特定优化
console.log('桌面端优化完成');
},
/**
* 切换移动端内容显示
*/
toggleMobileContent() {
this.showMobileContent = !this.showMobileContent;
},
/**
* 切换桌面端内容显示
*/
toggleDesktopContent() {
this.showDesktopContent = !this.showDesktopContent;
},
/**
* 切换平板端内容显示
*/
toggleTabletContent() {
this.showTabletContent = !this.showTabletContent;
},
/**
* 响应式显示元素
*/
responsiveShow(element, breakpoint = 'lg') {
if (this.$refs.responsiveLayout) {
this.$refs.responsiveLayout.responsiveShow(element, breakpoint);
}
},
/**
* 响应式隐藏元素
*/
responsiveHide(element, breakpoint = 'lg') {
if (this.$refs.responsiveLayout) {
this.$refs.responsiveLayout.responsiveHide(element, breakpoint);
}
},
/**
* 响应式切换类名
*/
responsiveToggleClass(element, className, breakpoint = 'lg') {
if (this.$refs.responsiveLayout) {
this.$refs.responsiveLayout.responsiveToggleClass(element, className, breakpoint);
}
}
}
};
</script>
<style scoped>
.responsive-example {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.page-content {
margin-bottom: 40px;
}
.responsive-title {
font-size: clamp(2rem, 5vw, 3.5rem);
text-align: center;
margin-bottom: 2rem;
color: #2c3e50;
}
.responsive-section {
margin: 3rem 0;
padding: 2rem;
background: #f8f9fa;
border-radius: 10px;
}
.responsive-section h2 {
font-size: clamp(1.5rem, 3vw, 2rem);
margin-bottom: 1.5rem;
color: #34495e;
}
/* 响应式内容控制 */
.mobile-only,
.desktop-only,
.tablet-only {
padding: 1rem;
margin: 1rem 0;
border-radius: 8px;
text-align: center;
}
.mobile-only {
background: #e8f5e8;
border: 2px solid #27ae60;
display: none;
}
.desktop-only {
background: #e8f4fd;
border: 2px solid #3498db;
display: none;
}
.tablet-only {
background: #fff3cd;
border: 2px solid #f39c12;
display: none;
}
/* 屏幕信息显示 */
.screen-info {
background: #2c3e50;
color: white;
padding: 2rem;
border-radius: 10px;
margin-bottom: 2rem;
}
.screen-info h3 {
margin-bottom: 1.5rem;
text-align: center;
color: #ecf0f1;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.info-item {
padding: 1rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
text-align: center;
}
/* 响应式控制按钮 */
.responsive-controls {
background: #ecf0f1;
padding: 2rem;
border-radius: 10px;
margin-bottom: 2rem;
}
.responsive-controls h3 {
margin-bottom: 1.5rem;
text-align: center;
color: #2c3e50;
}
.control-buttons {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
}
.control-btn {
padding: 0.75rem 1.5rem;
background: #3498db;
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.9rem;
}
.control-btn:hover {
background: #2980b9;
transform: translateY(-2px);
}
/* 响应式表单样式 */
.responsive-form-section {
margin: 3rem 0;
}
.form-group {
margin-bottom: 1.5rem;
}
.responsive-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #2c3e50;
}
.responsive-input {
width: 100%;
padding: 0.75rem;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 1rem;
transition: border-color 0.3s ease;
}
.responsive-input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
/* 响应式表格样式 */
.responsive-table-section {
margin: 3rem 0;
}
.responsive-table {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.responsive-table table {
width: 100%;
border-collapse: collapse;
}
.responsive-table th,
.responsive-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #e9ecef;
}
.responsive-table th {
background: #f8f9fa;
font-weight: 600;
color: #2c3e50;
}
.responsive-table tr:hover {
background: #f8f9fa;
}
/* 响应式导航样式 */
.responsive-nav-section {
margin: 3rem 0;
}
.responsive-nav {
background: white;
border-radius: 10px;
padding: 1rem;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.nav-link {
display: block;
padding: 0.75rem 1rem;
color: #2c3e50;
text-decoration: none;
border-radius: 6px;
transition: all 0.3s ease;
text-align: center;
}
.nav-link:hover {
background: #3498db;
color: white;
}
/* 响应式卡片样式 */
.responsive-card {
background: white;
padding: 1.5rem;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.responsive-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.responsive-card h3 {
margin-bottom: 1rem;
color: #2c3e50;
}
.responsive-card p {
margin-bottom: 1.5rem;
color: #7f8c8d;
line-height: 1.6;
}
/* 响应式按钮样式 */
.responsive-button {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
text-decoration: none;
display: inline-block;
}
.responsive-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4);
}
/* 响应式网格布局 */
.responsive-grid-3 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin: 2rem 0;
}
/* 响应式间距 */
.responsive-spacing {
padding: clamp(1rem, 3vw, 3rem);
margin: clamp(0.5rem, 2vw, 2rem);
}
/* 响应式文本 */
.responsive-text {
font-size: clamp(1rem, 2.5vw, 1.5rem);
line-height: 1.6;
}
/* 响应式阴影 */
.responsive-shadow {
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.responsive-shadow-lg {
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* 响应式圆角 */
.responsive-rounded {
border-radius: 10px;
}
.responsive-rounded-lg {
border-radius: 15px;
}
/* 响应式过渡 */
.responsive-transition {
transition: all 0.3s ease;
}
/* 响应式颜色 */
.responsive-text-primary {
color: #3498db;
}
.responsive-text-success {
color: #27ae60;
}
.responsive-text-warning {
color: #f39c12;
}
.responsive-text-danger {
color: #e74c3c;
}
.responsive-bg-primary {
background-color: #3498db;
}
.responsive-bg-success {
background-color: #27ae60;
}
.responsive-bg-warning {
background-color: #f39c12;
}
.responsive-bg-danger {
background-color: #e74c3c;
}
/* 响应式工具类 */
.responsive-hidden-xs { display: none !important; }
.responsive-hidden-sm { display: none !important; }
.responsive-hidden-md { display: none !important; }
.responsive-hidden-lg { display: none !important; }
.responsive-hidden-xl { display: none !important; }
.responsive-visible-xs { display: block !important; }
.responsive-visible-sm { display: block !important; }
.responsive-visible-md { display: block !important; }
.responsive-visible-lg { display: block !important; }
.responsive-visible-xl { display: block !important; }
/* 响应式文本对齐 */
.responsive-text-center { text-align: center; }
.responsive-text-left { text-align: left; }
.responsive-text-right { text-align: right; }
/* 响应式间距工具类 */
.responsive-m-0 { margin: 0 !important; }
.responsive-m-1 { margin: 0.25rem !important; }
.responsive-m-2 { margin: 0.5rem !important; }
.responsive-m-3 { margin: 1rem !important; }
.responsive-m-4 { margin: 1.5rem !important; }
.responsive-m-5 { margin: 3rem !important; }
.responsive-p-0 { padding: 0 !important; }
.responsive-p-1 { padding: 0.25rem !important; }
.responsive-p-2 { padding: 0.5rem !important; }
.responsive-p-3 { padding: 1rem !important; }
.responsive-p-4 { padding: 1.5rem !important; }
.responsive-p-5 { padding: 3rem !important; }
/* 响应式显示/隐藏 */
.responsive-hidden { display: none !important; }
.responsive-visible { display: block !important; }
/* 响应式内联 */
.responsive-inline { display: inline !important; }
.responsive-inline-block { display: inline-block !important; }
/* 响应式块级 */
.responsive-block { display: block !important; }
/* 响应式弹性 */
.responsive-flex { display: flex !important; }
.responsive-inline-flex { display: inline-flex !important; }
/* 响应式网格 */
.responsive-grid { display: grid !important; }
.responsive-inline-grid { display: inline-grid !important; }
/* 响应式表格 */
.responsive-table { display: table !important; }
.responsive-table-row { display: table-row !important; }
.responsive-table-cell { display: table-cell !important; }
/* 响应式定位 */
.responsive-relative { position: relative !important; }
.responsive-absolute { position: absolute !important; }
.responsive-fixed { position: fixed !important; }
.responsive-sticky { position: sticky !important; }
/* 响应式溢出 */
.responsive-overflow-auto { overflow: auto !important; }
.responsive-overflow-hidden { overflow: hidden !important; }
.responsive-overflow-visible { overflow: visible !important; }
.responsive-overflow-scroll { overflow: scroll !important; }
/* 响应式光标 */
.responsive-cursor-pointer { cursor: pointer !important; }
.responsive-cursor-default { cursor: default !important; }
.responsive-cursor-not-allowed { cursor: not-allowed !important; }
/* 响应式用户选择 */
.responsive-select-none { user-select: none !important; }
.responsive-select-text { user-select: text !important; }
.responsive-select-all { user-select: all !important; }
/* 响应式指针事件 */
.responsive-pointer-events-none { pointer-events: none !important; }
.responsive-pointer-events-auto { pointer-events: auto !important; }
/* 响应式可见性 */
.responsive-visible { visibility: visible !important; }
.responsive-invisible { visibility: hidden !important; }
/* 响应式透明度 */
.responsive-opacity-0 { opacity: 0 !important; }
.responsive-opacity-25 { opacity: 0.25 !important; }
.responsive-opacity-50 { opacity: 0.5 !important; }
.responsive-opacity-75 { opacity: 0.75 !important; }
.responsive-opacity-100 { opacity: 1 !important; }
/* 响应式Z索引 */
.responsive-z-0 { z-index: 0 !important; }
.responsive-z-10 { z-index: 10 !important; }
.responsive-z-20 { z-index: 20 !important; }
.responsive-z-30 { z-index: 30 !important; }
.responsive-z-40 { z-index: 40 !important; }
.responsive-z-50 { z-index: 50 !important; }
/* 响应式宽度 */
.responsive-w-full { width: 100% !important; }
.responsive-w-auto { width: auto !important; }
.responsive-w-screen { width: 100vw !important; }
/* 响应式高度 */
.responsive-h-full { height: 100% !important; }
.responsive-h-auto { height: auto !important; }
.responsive-h-screen { height: 100vh !important; }
/* 响应式最大宽度 */
.responsive-max-w-none { max-width: none !important; }
.responsive-max-w-full { max-width: 100% !important; }
.responsive-max-w-screen-sm { max-width: 640px !important; }
.responsive-max-w-screen-md { max-width: 768px !important; }
.responsive-max-w-screen-lg { max-width: 1024px !important; }
.responsive-max-w-screen-xl { max-width: 1280px !important; }
/* 响应式最小宽度 */
.responsive-min-w-0 { min-width: 0 !important; }
.responsive-min-w-full { min-width: 100% !important; }
/* 响应式最大高度 */
.responsive-max-h-none { max-height: none !important; }
.responsive-max-h-full { max-height: 100% !important; }
.responsive-max-h-screen { max-height: 100vh !important; }
/* 响应式最小高度 */
.responsive-min-h-0 { min-height: 0 !important; }
.responsive-min-h-full { min-height: 100% !important; }
.responsive-min-h-screen { min-height: 100vh !important; }
</style>

View File

@@ -10,16 +10,16 @@
<img :src="banner.img" width="100%" />
<div class="banner-overlay">
<div class="banner-text">
<h1 class="banner-title">{{banner.title}}</h1>
<p class="banner-subtitle">{{banner.subtitle}}</p>
<h1 class="banner-title animate fade-in-down">{{banner.title}}</h1>
<p class="banner-subtitle animate fade-in-up delay-1s">{{banner.subtitle}}</p>
</div>
</div>
</div>
</swiper-slide>
<div class="directionControl swiper-button-prev" slot="button-prev"> </div>
<div class="directionControl swiper-button-next" slot="button-next"> </div>
<div class="swiper-pagination" slot="pagination"> </div>
<div class="swiper-button-prev" slot="button-prev"></div>
<div class="swiper-button-next" slot="button-next"></div>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
@@ -27,8 +27,8 @@
<div class="company-intro">
<div class="container">
<div class="intro-header">
<h2 class="section-title">技术驱动未来服务创造价值</h2>
<p class="section-subtitle">上海丙维数字科技有限公司成立于2018年专注于AI交付内容为客户提供高质量的数字化解决方案</p>
<h2 class="section-title animate-on-scroll">技术驱动未来服务创造价值</h2>
<p class="section-subtitle animate-on-scroll">上海丙维数字科技有限公司成立于2018年专注于AI交付内容为客户提供高质量的数字化解决方案</p>
</div>
<div class="intro-content">
@@ -158,6 +158,9 @@
</template>
<script>
import VueAwesomeSwiper from 'vue-awesome-swiper'
import ResponsiveHelper from '../static/responsive_helper'
let banner_index0 = require('../assets/img/banner_index0.png')
let banner_index1 = require('../assets/img/banner_index1.png')
let banner_index2 = require('../assets/img/banner_index2.png')
@@ -170,6 +173,11 @@ let boxbg_facilitiesmanagement = require('../assets/img/boxbg_facilitiesmanageme
let boxbg_errands = require('../assets/img/boxbg_errands.png')
export default {
components: {
VueAwesomeSwiper,
ResponsiveHelper,
},
data() {
return {
swiperOption: {
@@ -187,7 +195,6 @@ export default {
pagination: {
el: '.swiper-pagination',
type: 'bullets',
bulletElement: 'li',
clickable: true,
},
},
@@ -305,11 +312,19 @@ export default {
position: relative;
height: 80vh;
min-height: 500px;
overflow: hidden;
}
.banner-content {
position: relative;
height: 100%;
width: 100%;
}
.banner-content img {
width: 100%;
height: 100%;
object-fit: cover;
}
.banner-overlay {
@@ -620,20 +635,22 @@ export default {
.pageBoxes {
grid-template-columns: 1fr;
gap: 20px;
gap: 30px;
display: flex;
flex-direction: row;
}
.boxTableHeader {
padding: 20px;
padding: 25px;
}
.product-tags {
gap: 6px;
gap: 8px;
}
.tag {
font-size: 0.75rem;
padding: 3px 10px;
font-size: 0.8rem;
padding: 5px 12px;
}
}
@@ -671,6 +688,9 @@ export default {
}
.product-image img {
width: 100%;
height: auto;
display: block;
transition: transform 0.3s ease;
}
@@ -716,37 +736,37 @@ export default {
}
.boxTableHeader {
padding: 25px;
padding: 30px;
text-align: center;
}
.boxTableHeader .h2 {
font-size: 1.5rem;
font-size: 1.6rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 15px;
margin-bottom: 20px;
}
.product-description {
color: #6c757d;
line-height: 1.6;
margin-bottom: 20px;
font-size: 0.95rem;
line-height: 1.7;
margin-bottom: 25px;
font-size: 1rem;
}
.product-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
gap: 10px;
justify-content: center;
}
.tag {
background: #e9ecef;
color: #495057;
padding: 4px 12px;
border-radius: 15px;
font-size: 0.8rem;
padding: 6px 14px;
border-radius: 18px;
font-size: 0.85rem;
font-weight: 500;
transition: all 0.3s ease;
}
@@ -757,8 +777,47 @@ export default {
transform: translateY(-2px);
}
/* 原有样式保持 */
.swiper-pagination {
bottom: 10%;
}
/* Swiper 基础样式 */
.swiper {
width: 100%;
height: 100%;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.swiper-button-next,
.swiper-button-prev {
color: white;
background: rgba(0, 0, 0, 0.3);
width: 44px;
height: 44px;
border-radius: 50%;
transition: all 0.3s ease;
}
.swiper-button-next:hover,
.swiper-button-prev:hover {
background: rgba(0, 0, 0, 0.6);
}
.swiper-pagination {
bottom: 10%;
}
.swiper-pagination-bullet {
background: white;
opacity: 0.7;
}
.swiper-pagination-bullet-active {
opacity: 1;
background: #3498db;
}
</style>

View File

@@ -3162,12 +3162,12 @@ dom-serializer@^1.0.1:
domhandler "^4.2.0"
entities "^2.0.0"
dom7@^4.0.4:
version "4.0.6"
resolved "https://registry.npmmirror.com/dom7/-/dom7-4.0.6.tgz#091a51621d7a19ce0fb86045cafb3c10035e97ed"
integrity sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==
dom7@^2.1.3, dom7@^2.1.5:
version "2.1.5"
resolved "https://registry.npmmirror.com/dom7/-/dom7-2.1.5.tgz#a79411017800b31d8400070cdaebbfc92c1f6377"
integrity sha512-xnhwVgyOh3eD++/XGtH+5qBwYTgCm0aW91GFgPJ3XG+jlsRLyJivnbP0QmUBFhI+Oaz9FV0s7cxgXHezwOEBYA==
dependencies:
ssr-window "^4.0.0"
ssr-window "^2.0.0"
domain-browser@^1.1.1:
version "1.2.0"
@@ -8282,10 +8282,15 @@ sshpk@^1.7.0:
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
ssr-window@^4.0.0, ssr-window@^4.0.2:
version "4.0.2"
resolved "https://registry.npmmirror.com/ssr-window/-/ssr-window-4.0.2.tgz#dc6b3ee37be86ac0e3ddc60030f7b3bc9b8553be"
integrity sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==
ssr-window@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/ssr-window/-/ssr-window-1.0.1.tgz#30752a6a4666e7767f0b7e6aa6fc2fdbd0d9b369"
integrity sha512-dgFqB+f00LJTEgb6UXhx0h+SrG50LJvti2yMKMqAgzfUmUXZrLSv2fjULF7AWGwK25EXu8+smLR3jYsJQChPsg==
ssr-window@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/ssr-window/-/ssr-window-2.0.0.tgz#98c301aef99523317f8d69618f0010791096efc4"
integrity sha512-NXzN+/HPObKAx191H3zKlYomE5WrVIkoCB5IaSdvKokxTpjBdWfr0RaP+1Z5KOfDT0ZVz+2tdtiBkhsEQ9p+0A==
ssri@^5.2.4:
version "5.3.0"
@@ -8591,13 +8596,21 @@ svgo@^2.1.0:
picocolors "^1.0.0"
stable "^0.1.8"
swiper@^8.4.7:
version "8.4.7"
resolved "https://registry.npmmirror.com/swiper/-/swiper-8.4.7.tgz#0301d385c3efc8efe8b66a64187edcb30e3067ee"
integrity sha512-VwO/KU3i9IV2Sf+W2NqyzwWob4yX9Qdedq6vBtS0rFqJ6Fa5iLUJwxQkuD4I38w0WDJwmFl8ojkdcRFPHWD+2g==
swiper@5.4.5:
version "5.4.5"
resolved "https://registry.npmmirror.com/swiper/-/swiper-5.4.5.tgz#a350f654bf68426dbb651793824925512d223c0f"
integrity sha512-7QjA0XpdOmiMoClfaZ2lYN6ICHcMm72LXiY+NF4fQLFidigameaofvpjEEiTQuw3xm5eksG5hzkaRsjQX57vtA==
dependencies:
dom7 "^4.0.4"
ssr-window "^4.0.2"
dom7 "^2.1.5"
ssr-window "^2.0.0"
swiper@^4.0.7:
version "4.5.1"
resolved "https://registry.npmmirror.com/swiper/-/swiper-4.5.1.tgz#ed43998e780ceb478610079c8d23fd425eca636f"
integrity sha512-se6I7PWWu950NAMXXT+ENtF/6SVb8mPyO+bTfNxbQBILSeLqsYp3Ndap+YOA0EczOIUlea274PKejT6gKZDseA==
dependencies:
dom7 "^2.1.3"
ssr-window "^1.0.1"
tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3"
@@ -9152,10 +9165,13 @@ vm-browserify@^1.0.1:
resolved "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
vue-awesome-swiper@^4.1.1:
version "4.1.1"
resolved "https://registry.npmmirror.com/vue-awesome-swiper/-/vue-awesome-swiper-4.1.1.tgz#8f7ab221ad003021d756b86aa618f429924900fe"
integrity sha512-50um10t6N+lJaORkpwSi1wWuMmBI1sgFc9Znsi5oUykw2cO5DzLaBHcO2JNX21R+Ue4TGoIJDhhxjBHtkFrTEQ==
vue-awesome-swiper@3.1.3:
version "3.1.3"
resolved "https://registry.npmmirror.com/vue-awesome-swiper/-/vue-awesome-swiper-3.1.3.tgz#05500b501ffb3fec9bf7eb9985bcf4ae8360ed9e"
integrity sha512-E7suzkyApO8vNZbgdEnjSmnpsmQZyRvSVXJ7sey3XYwKPOkLhH3+GnHroBw+5PZIQXvWBwdCeQsPG1xQ1r1Rhg==
dependencies:
object-assign "^4.1.1"
swiper "^4.0.7"
vue-hot-reload-api@^2.3.0:
version "2.3.4"