响应式设计系统定义了项目在不同设备和屏幕尺寸下的适配规则,包括断点定义、布局适配、组件响应式行为等。
文件位置: src/styles/responsive.css
/* 断点变量定义 */
:root {
--breakpoint-xs: 480px; /* 超小屏幕 */
--breakpoint-sm: 768px; /* 小屏幕 */
--breakpoint-md: 1024px; /* 中等屏幕 */
--breakpoint-lg: 1280px; /* 大屏幕 */
--breakpoint-xl: 1536px; /* 超大屏幕 */
}
/* 媒体查询断点 */
@media (max-width: 479px) {
/* 超小屏幕样式 */
}
@media (min-width: 480px) and (max-width: 767px) {
/* 小屏幕样式 */
}
@media (min-width: 768px) and (max-width: 1023px) {
/* 中等屏幕样式 */
}
@media (min-width: 1024px) and (max-width: 1279px) {
/* 大屏幕样式 */
}
@media (min-width: 1280px) and (max-width: 1535px) {
/* 超大屏幕样式 */
}
@media (min-width: 1536px) {
/* 极大屏幕样式 */
}/* 移动设备 */
@media (max-width: 767px) {
.mobile-only {
display: block;
}
.desktop-only {
display: none;
}
.container {
padding: 0 16px;
}
.grid-responsive {
grid-template-columns: 1fr;
gap: 16px;
}
}
/* 平板设备 */
@media (min-width: 768px) and (max-width: 1023px) {
.tablet-only {
display: block;
}
.container {
padding: 0 24px;
}
.grid-responsive {
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
}
/* 桌面设备 */
@media (min-width: 1024px) {
.mobile-only {
display: none;
}
.desktop-only {
display: block;
}
.container {
padding: 0 32px;
}
.grid-responsive {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
}/* 响应式容器 */
.container {
width: 100%;
margin: 0 auto;
padding: 0 16px;
}
@media (min-width: 480px) {
.container {
max-width: 480px;
padding: 0 20px;
}
}
@media (min-width: 768px) {
.container {
max-width: 768px;
padding: 0 24px;
}
}
@media (min-width: 1024px) {
.container {
max-width: 1024px;
padding: 0 32px;
}
}
@media (min-width: 1280px) {
.container {
max-width: 1280px;
padding: 0 40px;
}
}
@media (min-width: 1536px) {
.container {
max-width: 1536px;
padding: 0 48px;
}
}
/* 流体容器 */
.container-fluid {
width: 100%;
padding: 0 16px;
}
@media (min-width: 768px) {
.container-fluid {
padding: 0 24px;
}
}
@media (min-width: 1024px) {
.container-fluid {
padding: 0 32px;
}
}/* 响应式网格 */
.grid {
display: grid;
gap: 16px;
}
/* 移动端:单列 */
@media (max-width: 767px) {
.grid-cols-responsive {
grid-template-columns: 1fr;
}
.grid-cols-auto-responsive {
grid-template-columns: 1fr;
}
}
/* 平板端:双列 */
@media (min-width: 768px) and (max-width: 1023px) {
.grid-cols-responsive {
grid-template-columns: repeat(2, 1fr);
}
.grid-cols-auto-responsive {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.grid {
gap: 20px;
}
}
/* 桌面端:多列 */
@media (min-width: 1024px) {
.grid-cols-responsive {
grid-template-columns: repeat(3, 1fr);
}
.grid-cols-auto-responsive {
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}
.grid {
gap: 24px;
}
}
@media (min-width: 1280px) {
.grid-cols-responsive {
grid-template-columns: repeat(4, 1fr);
}
.grid-cols-auto-responsive {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
}/* 响应式Flex布局 */
.flex-responsive {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
/* 移动端:垂直排列 */
@media (max-width: 767px) {
.flex-responsive {
flex-direction: column;
}
.flex-responsive > * {
flex: 1 1 100%;
}
}
/* 平板端:水平排列,自动换行 */
@media (min-width: 768px) and (max-width: 1023px) {
.flex-responsive {
flex-direction: row;
gap: 20px;
}
.flex-responsive > * {
flex: 1 1 calc(50% - 10px);
}
}
/* 桌面端:水平排列 */
@media (min-width: 1024px) {
.flex-responsive {
flex-direction: row;
gap: 24px;
}
.flex-responsive > * {
flex: 1 1 calc(33.333% - 16px);
}
}/* 响应式导航 */
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
height: 64px;
}
/* 移动端导航 */
@media (max-width: 767px) {
.navbar {
height: 56px;
padding: 0 12px;
}
.navbar-menu {
display: none;
position: fixed;
top: 56px;
left: 0;
right: 0;
bottom: 0;
background: white;
z-index: 1000;
padding: 20px;
}
.navbar-menu.active {
display: block;
}
.navbar-toggle {
display: block;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
}
/* 桌面端导航 */
@media (min-width: 768px) {
.navbar {
padding: 0 24px;
}
.navbar-menu {
display: flex;
align-items: center;
gap: 24px;
}
.navbar-toggle {
display: none;
}
}/* 响应式卡片 */
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* 移动端卡片 */
@media (max-width: 767px) {
.card {
margin: 8px;
border-radius: 6px;
}
.card-header {
padding: 16px;
font-size: 16px;
}
.card-body {
padding: 16px;
font-size: 14px;
}
.card-footer {
padding: 12px 16px;
}
}
/* 平板端卡片 */
@media (min-width: 768px) and (max-width: 1023px) {
.card {
margin: 12px;
border-radius: 8px;
}
.card-header {
padding: 20px;
font-size: 18px;
}
.card-body {
padding: 20px;
font-size: 14px;
}
.card-footer {
padding: 16px 20px;
}
}
/* 桌面端卡片 */
@media (min-width: 1024px) {
.card {
margin: 16px;
border-radius: 10px;
}
.card-header {
padding: 24px;
font-size: 20px;
}
.card-body {
padding: 24px;
font-size: 16px;
}
.card-footer {
padding: 20px 24px;
}
}/* 响应式表格 */
.table-responsive {
width: 100%;
overflow-x: auto;
}
/* 移动端表格 */
@media (max-width: 767px) {
.table-responsive {
border-radius: 6px;
}
.table {
font-size: 12px;
}
.table th,
.table td {
padding: 8px 4px;
white-space: nowrap;
}
/* 卡片式表格 */
.table-card {
display: block;
}
.table-card thead {
display: none;
}
.table-card tbody {
display: block;
}
.table-card tr {
display: block;
margin-bottom: 16px;
background: white;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.table-card td {
display: block;
text-align: left;
padding: 4px 0;
border: none;
}
.table-card td:before {
content: attr(data-label) ": ";
font-weight: bold;
color: #666;
}
}
/* 桌面端表格 */
@media (min-width: 768px) {
.table {
font-size: 14px;
}
.table th,
.table td {
padding: 12px 16px;
}
.table-card {
display: table;
}
.table-card thead {
display: table-header-group;
}
.table-card tbody {
display: table-row-group;
}
.table-card tr {
display: table-row;
margin: 0;
background: transparent;
border-radius: 0;
padding: 0;
box-shadow: none;
}
.table-card td {
display: table-cell;
padding: 12px 16px;
border-bottom: 1px solid #eee;
}
.table-card td:before {
display: none;
}
}/* 基础字体大小 */
html {
font-size: 14px;
}
/* 移动端字体 */
@media (max-width: 767px) {
html {
font-size: 13px;
}
.text-xs { font-size: 0.7rem; }
.text-sm { font-size: 0.8rem; }
.text-base { font-size: 0.9rem; }
.text-lg { font-size: 1rem; }
.text-xl { font-size: 1.1rem; }
.text-2xl { font-size: 1.3rem; }
.text-3xl { font-size: 1.5rem; }
}
/* 平板端字体 */
@media (min-width: 768px) and (max-width: 1023px) {
html {
font-size: 14px;
}
.text-xs { font-size: 0.75rem; }
.text-sm { font-size: 0.875rem; }
.text-base { font-size: 1rem; }
.text-lg { font-size: 1.125rem; }
.text-xl { font-size: 1.25rem; }
.text-2xl { font-size: 1.5rem; }
.text-3xl { font-size: 1.875rem; }
}
/* 桌面端字体 */
@media (min-width: 1024px) {
html {
font-size: 16px;
}
.text-xs { font-size: 0.75rem; }
.text-sm { font-size: 0.875rem; }
.text-base { font-size: 1rem; }
.text-lg { font-size: 1.125rem; }
.text-xl { font-size: 1.25rem; }
.text-2xl { font-size: 1.5rem; }
.text-3xl { font-size: 1.875rem; }
.text-4xl { font-size: 2.25rem; }
.text-5xl { font-size: 3rem; }
}/* 响应式行高 */
@media (max-width: 767px) {
body {
line-height: 1.4;
}
.leading-tight { line-height: 1.2; }
.leading-normal { line-height: 1.4; }
.leading-relaxed { line-height: 1.5; }
}
@media (min-width: 768px) {
body {
line-height: 1.6;
}
.leading-tight { line-height: 1.25; }
.leading-normal { line-height: 1.6; }
.leading-relaxed { line-height: 1.75; }
}/* 响应式外边距 */
@media (max-width: 767px) {
.m-responsive { margin: 8px; }
.mx-responsive { margin-left: 8px; margin-right: 8px; }
.my-responsive { margin-top: 8px; margin-bottom: 8px; }
.mt-responsive { margin-top: 8px; }
.mb-responsive { margin-bottom: 8px; }
}
@media (min-width: 768px) and (max-width: 1023px) {
.m-responsive { margin: 16px; }
.mx-responsive { margin-left: 16px; margin-right: 16px; }
.my-responsive { margin-top: 16px; margin-bottom: 16px; }
.mt-responsive { margin-top: 16px; }
.mb-responsive { margin-bottom: 16px; }
}
@media (min-width: 1024px) {
.m-responsive { margin: 24px; }
.mx-responsive { margin-left: 24px; margin-right: 24px; }
.my-responsive { margin-top: 24px; margin-bottom: 24px; }
.mt-responsive { margin-top: 24px; }
.mb-responsive { margin-bottom: 24px; }
}/* 响应式内边距 */
@media (max-width: 767px) {
.p-responsive { padding: 12px; }
.px-responsive { padding-left: 12px; padding-right: 12px; }
.py-responsive { padding-top: 12px; padding-bottom: 12px; }
.pt-responsive { padding-top: 12px; }
.pb-responsive { padding-bottom: 12px; }
}
@media (min-width: 768px) and (max-width: 1023px) {
.p-responsive { padding: 20px; }
.px-responsive { padding-left: 20px; padding-right: 20px; }
.py-responsive { padding-top: 20px; padding-bottom: 20px; }
.pt-responsive { padding-top: 20px; }
.pb-responsive { padding-bottom: 20px; }
}
@media (min-width: 1024px) {
.p-responsive { padding: 32px; }
.px-responsive { padding-left: 32px; padding-right: 32px; }
.py-responsive { padding-top: 32px; padding-bottom: 32px; }
.pt-responsive { padding-top: 32px; }
.pb-responsive { padding-bottom: 32px; }
}/* 响应式显示隐藏 */
.hidden-xs { display: none; }
.hidden-sm { display: block; }
.hidden-md { display: block; }
.hidden-lg { display: block; }
.hidden-xl { display: block; }
@media (min-width: 480px) {
.hidden-xs { display: block; }
.hidden-sm { display: none; }
}
@media (min-width: 768px) {
.hidden-sm { display: block; }
.hidden-md { display: none; }
}
@media (min-width: 1024px) {
.hidden-md { display: block; }
.hidden-lg { display: none; }
}
@media (min-width: 1280px) {
.hidden-lg { display: block; }
.hidden-xl { display: none; }
}
@media (min-width: 1536px) {
.hidden-xl { display: block; }
}
/* 仅在特定断点显示 */
.visible-xs { display: block; }
.visible-sm { display: none; }
.visible-md { display: none; }
.visible-lg { display: none; }
.visible-xl { display: none; }
@media (min-width: 480px) {
.visible-xs { display: none; }
.visible-sm { display: block; }
}
@media (min-width: 768px) {
.visible-sm { display: none; }
.visible-md { display: block; }
}
@media (min-width: 1024px) {
.visible-md { display: none; }
.visible-lg { display: block; }
}
@media (min-width: 1280px) {
.visible-lg { display: none; }
.visible-xl { display: block; }
}/* 响应式文本对齐 */
@media (max-width: 767px) {
.text-center-mobile { text-align: center; }
.text-left-mobile { text-align: left; }
.text-right-mobile { text-align: right; }
}
@media (min-width: 768px) {
.text-center-desktop { text-align: center; }
.text-left-desktop { text-align: left; }
.text-right-desktop { text-align: right; }
}/**
* 响应式断点检测工具
*/
export const breakpoints = {
xs: 480,
sm: 768,
md: 1024,
lg: 1280,
xl: 1536
}
/**
* 获取当前断点
* @returns {String} 当前断点名称
*/
export function getCurrentBreakpoint() {
const width = window.innerWidth
if (width < breakpoints.xs) return 'xs'
if (width < breakpoints.sm) return 'sm'
if (width < breakpoints.md) return 'md'
if (width < breakpoints.lg) return 'lg'
if (width < breakpoints.xl) return 'xl'
return 'xxl'
}
/**
* 检查是否为移动设备
* @returns {Boolean} 是否为移动设备
*/
export function isMobile() {
return window.innerWidth < breakpoints.sm
}
/**
* 检查是否为平板设备
* @returns {Boolean} 是否为平板设备
*/
export function isTablet() {
const width = window.innerWidth
return width >= breakpoints.sm && width < breakpoints.lg
}
/**
* 检查是否为桌面设备
* @returns {Boolean} 是否为桌面设备
*/
export function isDesktop() {
return window.innerWidth >= breakpoints.lg
}import { ref, onMounted, onBeforeUnmount } from 'vue'
/**
* 响应式断点组合式API
*/
export function useBreakpoint() {
const currentBreakpoint = ref(getCurrentBreakpoint())
const isMobileDevice = ref(isMobile())
const isTabletDevice = ref(isTablet())
const isDesktopDevice = ref(isDesktop())
const updateBreakpoint = () => {
currentBreakpoint.value = getCurrentBreakpoint()
isMobileDevice.value = isMobile()
isTabletDevice.value = isTablet()
isDesktopDevice.value = isDesktop()
}
onMounted(() => {
window.addEventListener('resize', updateBreakpoint)
updateBreakpoint()
})
onBeforeUnmount(() => {
window.removeEventListener('resize', updateBreakpoint)
})
return {
currentBreakpoint: readonly(currentBreakpoint),
isMobile: readonly(isMobileDevice),
isTablet: readonly(isTabletDevice),
isDesktop: readonly(isDesktopDevice)
}
}<template>
<div class="container">
<div class="grid-responsive">
<div class="card">卡片1</div>
<div class="card">卡片2</div>
<div class="card">卡片3</div>
</div>
</div>
</template>
<style scoped>
.grid-responsive {
display: grid;
gap: 16px;
}
@media (max-width: 767px) {
.grid-responsive {
grid-template-columns: 1fr;
}
}
@media (min-width: 768px) {
.grid-responsive {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.grid-responsive {
grid-template-columns: repeat(3, 1fr);
}
}
</style><template>
<div>
<!-- 移动端显示 -->
<div v-if="isMobile" class="mobile-menu">
<button @click="toggleMenu">菜单</button>
</div>
<!-- 桌面端显示 -->
<nav v-else class="desktop-nav">
<a href="#home">首页</a>
<a href="#about">关于</a>
<a href="#contact">联系</a>
</nav>
</div>
</template>
<script setup>
import { useBreakpoint } from '@/utils/responsive'
const { isMobile } = useBreakpoint()
const toggleMenu = () => {
// 切换移动端菜单
}
</script><template>
<el-table
:data="tableData"
:size="tableSize"
:height="tableHeight"
>
<!-- 表格列 -->
</el-table>
</template>
<script setup>
import { computed } from 'vue'
import { useBreakpoint } from '@/utils/responsive'
const { isMobile, isTablet } = useBreakpoint()
const tableSize = computed(() => {
if (isMobile.value) return 'small'
if (isTablet.value) return 'default'
return 'large'
})
const tableHeight = computed(() => {
if (isMobile.value) return 300
if (isTablet.value) return 400
return 500
})
</script>- 性能优化: 避免过多的媒体查询,合理组织CSS结构
- 触摸友好: 移动端确保足够的触摸目标大小
- 内容优先: 优先考虑内容的可读性和可访问性
- 测试覆盖: 在不同设备和屏幕尺寸下充分测试
- 渐进增强: 从移动端开始设计,逐步增强桌面端体验
最后更新时间:2025-09-19