164 lines
6.7 KiB
Markdown
164 lines
6.7 KiB
Markdown
---
|
||
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 无报错
|