# 018-responsive.mdc (Deep Reference) > 该文件为原始详细规范归档,供 Tier 3 按需读取。 --- # 📱 Responsive Design & Multi-Device Standards ## 断点体系(与 Tailwind 统一) > **⚠️ 双前端区分**:本文件中的 Element Plus 移动端适配内容**仅适用于管理端** (`Case-Database-Frontend-admin/`)。 > 用户端 (`Case-Database-Frontend-user/`) 使用 Headless UI + Tailwind CSS,**禁止引入 Element Plus**。 | 断点 | Tailwind 前缀 | 最小宽度 | 目标设备 | |------|--------------|---------|---------| | 默认 | (无前缀) | 0px | 手机竖屏 (< 640px) | | sm | `sm:` | 640px | 手机横屏 / 小平板 | | md | `md:` | 768px | 平板竖屏 (iPad) | | lg | `lg:` | 1024px | 平板横屏 / 小屏笔记本 | | xl | `xl:` | 1280px | 桌面 | | 2xl | `2xl:` | 1536px | 大屏桌面 / 4K | > **原则**:移动优先(Mobile-First)— 先写手机样式,用断点向上覆盖。 ```html
...
``` --- ## 布局组件响应式模式 ### 侧边栏布局(后台管理系统标准模式) ```vue ``` ### `useDevice` Composable ```typescript // src/composables/useDevice.ts export function useDevice() { const width = ref(window.innerWidth) const handleResize = useDebounceFn(() => { width.value = window.innerWidth }, 100) onMounted(() => window.addEventListener('resize', handleResize)) onUnmounted(() => window.removeEventListener('resize', handleResize)) return { width: readonly(width), isMobile: computed(() => width.value < 768), isTablet: computed(() => width.value >= 768 && width.value < 1024), isDesktop: computed(() => width.value >= 1024), isTouch: computed(() => 'ontouchstart' in window), } } ``` --- ## Element Plus 移动端适配(仅管理端) ### 组件尺寸策略 ```vue ``` ### 对话框适配 ```vue ``` ### 分页适配 ```vue ``` ### 表单布局 ```vue ``` --- ## 触摸与手势优化 ### 最小触摸目标尺寸 ```scss // src/assets/styles/touch.scss // 根据 WCAG 2.5.5,触摸目标最小 44×44px .touch-target { min-width: 44px; min-height: 44px; display: flex; align-items: center; justify-content: center; } @media (hover: none) and (pointer: coarse) { // 触屏设备:增大可点击区域 .el-button { min-height: 44px; padding-left: 16px; padding-right: 16px; } .el-input__wrapper { min-height: 44px; } } ``` ### 滑动手势(移动端列表操作) ```typescript // src/composables/useSwipeAction.ts export function useSwipeAction(onDelete: () => void, onEdit: () => void) { const startX = ref(0) const offsetX = ref(0) const THRESHOLD = 80 function onTouchStart(e: TouchEvent) { startX.value = e.touches[0].clientX } function onTouchMove(e: TouchEvent) { const diff = e.touches[0].clientX - startX.value offsetX.value = Math.max(-160, Math.min(0, diff)) // 最多滑动 160px } function onTouchEnd() { if (offsetX.value < -THRESHOLD) { // 滑过阈值:显示操作按钮 } else { offsetX.value = 0 // 弹回 } } return { offsetX, onTouchStart, onTouchMove, onTouchEnd } } ``` --- ## 图片与媒体响应式 ```vue ``` --- ## 字体与文字排版 ```scss // 流式字体大小:随屏幕宽度线性变化 :root { --font-base: clamp(14px, 1vw + 12px, 16px); --font-heading: clamp(20px, 3vw + 10px, 36px); } body { font-size: var(--font-base); } h1 { font-size: var(--font-heading); } // 行高:移动端更宽松(小屏阅读舒适度) p { line-height: 1.6; @media (max-width: 767px) { line-height: 1.8; } } ``` --- ## 安全区域(刘海屏 / 全面屏) ```css /* 底部安全区域(iOS 全面屏 Home Bar)*/ .bottom-nav { padding-bottom: env(safe-area-inset-bottom, 16px); } /* 顶部安全区域(刘海屏)*/ .top-bar { padding-top: env(safe-area-inset-top, 0px); } ``` ```typescript // tailwind.config.ts — 添加安全区域工具类 theme: { extend: { padding: { 'safe-bottom': 'env(safe-area-inset-bottom, 16px)', 'safe-top': 'env(safe-area-inset-top, 0px)', }, }, }, ``` --- ## 响应式测试矩阵 | 设备 | 分辨率 | 断点 | 关键功能检查 | |------|--------|------|------------| | iPhone SE (3代) | 375×667 | xs | 侧边栏抽屉、表单垂直布局 | | iPhone 14 Pro | 393×852 | xs | 安全区域、全面屏底部导航 | | iPhone 14 Pro Max | 430×932 | sm | 横屏布局、键盘遮挡处理 | | iPad (10代) | 820×1180 | md | 平板双栏、对话框宽度 | | iPad Pro 12.9" | 1024×1366 | lg | 分页组件、表格完整列 | | MacBook Air 13" | 1280×800 | xl | 完整侧边栏、桌面表格 | | 4K 显示器 | 1920×1080 | 2xl | 最大宽度限制、留白 | ### 测试脚本(Playwright) ```typescript // tests/responsive.spec.ts import { test, expect } from '@playwright/test' const viewports = [ { name: 'mobile', width: 375, height: 812 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'desktop', width: 1280, height: 800 }, ] for (const vp of viewports) { test(`dashboard layout on ${vp.name}`, async ({ page }) => { await page.setViewportSize({ width: vp.width, height: vp.height }) await page.goto('/dashboard') if (vp.name === 'mobile') { // 移动端:侧边栏默认隐藏 await expect(page.locator('aside')).toHaveCSS('transform', 'matrix(1, 0, 0, 1, -256, 0)') // 汉堡按钮可见 await expect(page.locator('[aria-label="打开菜单"]')).toBeVisible() } else { // 平板/桌面:侧边栏默认显示 await expect(page.locator('aside')).toBeVisible() } }) } ``` --- ## 规则 - 所有新页面必须在 375px / 768px / 1280px 三个断点下验证 - 禁止使用 `px` 设置字体大小(用 `rem` 或 Tailwind 文本类) - 触摸目标最小 44×44px - 禁止使用 `:hover` 作为唯一交互反馈(移动端无 hover) - 管理端:所有 `el-dialog` 必须处理移动端 `fullscreen` 属性(用户端使用 Headless UI Dialog) - 图片必须设置 `loading="lazy"` 和 `aspect-ratio`(防止布局抖动 CLS) - 横屏(landscape)模式下的布局需专门测试