# 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
提交
{{ row.status }} · {{ row.createdAt }}
```
### 对话框适配
```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)模式下的布局需专门测试