--- 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 → │ │ ├─ 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 表格横向滚动 所有 `` 统一添加 `scroll={{ x }}`。具体值: - 列少(≤6 列):`scroll={{ x: 'max-content' }}` 或省略(antd 自动处理) - 列中(7-10 列):`scroll={{ x: 800 }}` - 列多(>10 列或含长文本列):`scroll={{ x: 1000 }}` 或更大 操作列(最后一列)使用 `width` 固定宽度,必要时添加 `fixed: 'right'` 在宽表场景下提升体验。 ### 3.4 Dashboard 响应式网格 ``` 统计卡片 (4 张): // 手机2列 平板2列 桌面4列 图表卡片 (2 张): // 手机堆叠 平板及以上并排 ``` 顶部工具栏(标题 + DatePicker)在小屏下从 `flex` 横向排列改为 `flexDirection: 'column'` 堆叠。 ### 3.5 ECharts 图表 通过 `echarts-for-react` 的内置 `ResizeObserver` 自动适配: ```tsx ``` 容器宽度由 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 `
` 含 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 无报错