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,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 {