---
comet_change: admin-responsive-adaptation
role: technical-design
canonical_spec: openspec
archived-with: 2026-07-03-admin-responsive-adaptation
status: final
---

# 管理后台三端响应式适配 — 技术设计

## 1. 架构概览

```
┌─────────────────────────────────────────────────────────────────┐
│                        Admin Frontend                           │
├─────────────────────────────────────────────────────────────────┤
│  App.tsx                                                        │
│  └─ ConfigProvider (antd theme + locale)                        │
│     └─ BrowserRouter                                            │
│        ├─ LoginPage                                             │
│        └─ MainLayout                                            │
│           ├─ Sider/Drawer (断点决定)                             │
│           ├─ Header (用户区域断点隐藏文字)                        │
│           └─ Content → <Outlet>                                 │
│              ├─ Dashboard (统计卡片 + ECharts)                   │
│              ├─ RoomVisual (房态网格卡片)                        │
│              ├─ [14 个表格型页面] (Table + 搜索/操作工具栏)       │
│              └─ ClassroomSchedule (HTML 排期大表)                │
└─────────────────────────────────────────────────────────────────┘
```

## 2. 断点体系

使用 antd v6 内置 `Grid.useBreakpoint()` hook，映射三类设备行为：

| 端侧 | antd 断点 | 视口宽度 | 布局行为 |
|------|-----------|---------|---------|
| 手机 | `xs` | < 576px | Drawer 抽屉菜单，padding 12px |
| 平板 | `sm`, `md` | 576-991px | 侧栏默认折叠，padding 16px |
| 桌面 | `lg`, `xl`, `xxl` | ≥ 992px | 侧栏可折叠（默认展开），padding 24px |

**选择理由：** 使用 antd 内置断点与 `Row/Col` 响应式 props 天然一致，避免维护两套断点逻辑。992px 与最初规划的 1024px 相差 32px，实际设备无感知。

## 3. 核心决策

### 3.1 MainLayout 响应式检测

**当前：** `useState(window.innerWidth < 768)` + `resize` 事件 → `isMobile` 布尔值

**目标：** `Grid.useBreakpoint()` → `{ xs, sm, md, lg, xl, xxl }` 布尔值

```typescript
const screens = Grid.useBreakpoint();
const isMobile = !screens.sm;                        // < 576px (仅 xs)
const isTablet = (screens.sm || screens.md) && !screens.lg; // 576-991px
const isDesktop = !!screens.lg;                      // ≥ 992px
```

影响范围：仅 `MainLayout.tsx` 一个文件，`useEffect` + `resize` 事件监听可移除。

### 3.2 CSS 策略：antd Props 优先

| 场景 | 优先方案 | 兜底方案 |
|------|---------|---------|
| 表格列过多 | `scroll={{ x }}` | 无（antd 自带滚动条） |
| 卡片网格 | `Col` 响应式断点 props | `index.css` @media |
| 文字截断 | `ellipsis: true` | `text-overflow: ellipsis` CSS |
| 弹窗宽度 | antd `width` prop | `index.css` `max-width` 约束 |
| 工具栏换行 | `flexWrap: 'wrap'` + `gap` inline style | — |

**`index.css` 追加的 @media 规则（总计约 40 行）：**
- `(max-width: 575px)`: 表格字体 13px、弹窗 max-width 约束、Modal body max-height
- `(min-width: 576px) and (max-width: 991px)`: 平板特有的间距微调
- 通用: `.ant-table-wrapper { overflow-x: auto }` 确保所有表格容器可滚动

### 3.3 表格横向滚动

所有 `<Table>` 统一添加 `scroll={{ x }}`。具体值：
- 列少（≤6 列）：`scroll={{ x: 'max-content' }}` 或省略（antd 自动处理）
- 列中（7-10 列）：`scroll={{ x: 800 }}`
- 列多（>10 列或含长文本列）：`scroll={{ x: 1000 }}` 或更大

操作列（最后一列）使用 `width` 固定宽度，必要时添加 `fixed: 'right'` 在宽表场景下提升体验。

### 3.4 Dashboard 响应式网格

```
统计卡片 (4 张):
  <Col xs={12} sm={12} md={6}>   // 手机2列 平板2列 桌面4列

图表卡片 (2 张):
  <Col xs={24} sm={12}>          // 手机堆叠 平板及以上并排
```

顶部工具栏（标题 + DatePicker）在小屏下从 `flex` 横向排列改为 `flexDirection: 'column'` 堆叠。

### 3.5 ECharts 图表

通过 `echarts-for-react` 的内置 `ResizeObserver` 自动适配：
```tsx
<ReactECharts
  option={option}
  style={{ width: '100%', height: isMobile ? 250 : 300 }}
  opts={{ renderer: 'canvas' }}
/>
```
容器宽度由 antd `Col` 响应式断点控制，图表自动跟随。甘特图动态高度逻辑不变（`Math.max(300, data.length * 40)`）。

### 3.6 弹窗适配

**全局 CSS（index.css）：**
```css
@media (max-width: 575px) {
  .ant-modal { max-width: calc(100vw - 24px) !important; }
  .ant-modal-body { max-height: 60vh; overflow-y: auto; }
}
```

**组件级别：** 各弹窗 `width` 在桌面端固定值（400-600px），移动端由全局 CSS 覆盖为 `max-width` 约束。

### 3.7 教室排期表

最复杂的适配场景 — HTML `<table>` 含 31+ 日期列 + sticky 首列：
- 外层 `div` 保持 `overflowX: 'auto'`
- 首列（教室名）`position: sticky; left: 0` 保持已有
- 日期列 `minWidth: 26` 不压窄
- `overflowX` 容器在平板/手机下自动出现横向滚动条

### 3.8 学生字段拆分

学生管理页将 `{ title: '学号/身份证', dataIndex: 'idNumber' }` 一列拆为两列：
```typescript
{ title: '学号', dataIndex: 'studentNumber', width: 120, ellipsis: true },
{ title: '身份证', dataIndex: 'idNumber', width: 180, ellipsis: true },
```
向后兼容：若后端暂未返回 `studentNumber` 字段，该列显示 `-`，不报错。

## 4. 实现顺序

```
Phase 1: 基础
  1. index.css 三断点体系
  2. MainLayout useBreakpoint 重构

Phase 2: 高优先级页面
  3. Dashboard (4 张统计卡 + 3 张图)
  4. 学生管理 (表格 + 字段拆分)
  5. 入住管理 (表格 + 弹窗多)

Phase 3: 批量页面
  6-17. 剩余 12 个表格型页面（模式统一，效率高）

Phase 4: 收尾
  18. 教室排期表（特殊 HTML table）
  19. 登录页（单卡片）
  20. 全局验证
```

## 5. 验证计划

- **视觉检查：** Chrome DevTools 响应式模式 → 375 / 768 / 992 / 1440 四个宽度
- **每个页面检查：** 表格有无横向滚动 → 按钮是否错位/溢出 → 弹窗是否出屏 → 文字是否截断
- **编译检查：** `npm run build` 确保 TypeScript 无报错
