1
This commit is contained in:
488
docs/响应式设计指南.md
Normal file
488
docs/响应式设计指南.md
Normal 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 应用。
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
785
src/assets/css/animations.css
Normal file
785
src/assets/css/animations.css
Normal 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
654
src/components/ResponsiveLayout.vue
Normal file
654
src/components/ResponsiveLayout.vue
Normal 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>
|
||||
@@ -255,7 +255,7 @@ export default {
|
||||
|
||||
.logoCell {
|
||||
flex-shrink: 0;
|
||||
margin-right: 40px;
|
||||
margin: 0px 40px;
|
||||
}
|
||||
|
||||
.logoCell .logo {
|
||||
|
||||
@@ -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'
|
||||
|
||||
353
src/static/responsive_helper.js
Normal file
353
src/static/responsive_helper.js
Normal 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'); // 在中等屏幕以下添加移动端样式
|
||||
*/
|
||||
814
src/views/ResponsiveExample.vue
Normal file
814
src/views/ResponsiveExample.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
54
yarn.lock
54
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user