Files
gongxue-base/docs/superpowers/plans/2026-07-03-admin-responsive-adaptation.md

1335 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
change: admin-responsive-adaptation
design-doc: docs/superpowers/specs/2026-07-03-admin-responsive-adaptation-design.md
base-ref: 779cedb8a07bbfe1c50871526f9b99f8bc008acc
archived-with: 2026-07-03-admin-responsive-adaptation
---
# 管理后台三端响应式适配 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将管理后台从单一 mobile/desktop 二元适配升级为 antd v6 Grid.useBreakpoint() 三端响应式体系(手机 < 576px / 平板 576-991px / 桌面 >= 992px覆盖 16 个页面的布局、表格、弹窗和工具栏。
**Architecture:** 使用 antd v6 内置 `Grid.useBreakpoint()` 替换自写的 resize 监听,通过断点驱动 Sider/Drawer 切换、内容区 padding、卡片网格 Col 响应式 props。所有表格添加 `scroll={{ x }}` 横向滚动,工具栏添加 `flexWrap: 'wrap'` + `gap`,弹窗由全局 CSS @media 约束宽度。
**Tech Stack:** React 19, antd v6.3.6, echarts-for-react, react-router-dom v7, dayjs, Vite 8
## Global Constraints
- 断点体系使用 antd v6 内置断点:手机 `(max-width: 575px)`、平板 `(min-width: 576px) and (max-width: 991px)`、桌面默认
- CSS 策略antd Props 优先Col 响应式断点、Table scroll、ellipsis@media 为兜底
- 所有 `<Table>` 组件若无 `scroll={{ x }}` 则添加之,列少(<=6`max-content`列中7-10`800`,列多(>10`1000`
- 弹窗宽度由全局 CSS `max-width: calc(100vw - 24px)` 覆盖,组件级不做重复处理
- 编译必须通过:`npm run build` 无 TypeScript 错误
- 命名约定:从 `expenseTypeMap` 等现有映射扩展,不新建无意义的变量名
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 1: 全局 CSS 三断点体系
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/index.css`
**Interfaces:**
- Produces: 三个 `@media` 层级的 CSS 规则,供所有页面共用
- 断点规范:手机 `(max-width: 575px)`、平板 `(min-width: 576px) and (max-width: 991px)`
**Description:** 将当前仅有单断点max-width: 767px的 index.css 改为三断点体系,同时添加全局表格容器横向滚动和通用组件响应式样式。
- [x] **Step 1: 替换 index.css 中的 @media 规则**
将当前文件内容替换为以下完整版本:
```css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#root {
min-height: 100vh;
}
/* === 通用:表格容器横向滚动 === */
.ant-table-wrapper {
overflow-x: auto;
}
/* === 手机 (< 576px) === */
@media (max-width: 575px) {
.ant-table {
font-size: 13px;
}
.ant-table-cell {
padding: 8px 6px !important;
}
.ant-descriptions-item-label,
.ant-descriptions-item-content {
font-size: 13px;
}
.ant-modal {
max-width: calc(100vw - 24px) !important;
margin: 12px auto !important;
}
.ant-modal .ant-modal-body {
max-height: 60vh;
overflow-y: auto;
}
h2 {
font-size: 18px !important;
}
.ant-card {
margin-bottom: 8px;
}
.ant-space-item .ant-btn {
padding: 2px 6px;
font-size: 12px;
}
}
/* === 平板 (576-991px) === */
@media (min-width: 576px) and (max-width: 991px) {
.ant-modal {
max-width: calc(100vw - 48px) !important;
}
}
```
**Changed from current:**
- 旧断点 `(max-width: 767px)` 改为 `(max-width: 575px)`
- 新增 `.ant-table-wrapper { overflow-x: auto }` 通用规则
- 新增平板断点 `(min-width: 576px) and (max-width: 991px)` 仅做弹窗微调
- 桌面(>= 992px使用浏览器默认样式无需显式 @media
- [x] **Step 2: 验证 CSS 语法**
```bash
# 用 node 检查语法(没有专门的 CSS 语法检查,可跳过)
echo "CSS rules updated"
```
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/index.css
git commit -m "feat: 建立三断点 CSS 体系,替换单断点移动端样式"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 2: MainLayout 三端布局重构
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/layouts/MainLayout.tsx`
**Interfaces:**
- Consumes: antd `Grid.useBreakpoint()` (from `antd`)
- Produces: `isMobile` / `isTablet` / `isDesktop` 三端布尔值Sider/Drawer 切换逻辑
- Removes: `useState(window.innerWidth < 768)` + `useEffect` resize 事件监听
**Description:** 将 MainLayout 从自建 resize 监听改为 antd v6 的 Grid.useBreakpoint() hook根据三端断点控制侧栏桌面 Sider / 手机 Drawer / 平板 Sider 折叠、Header 用户区域文字显示、Content padding。
- [x] **Step 1: 替换 import 和状态声明**
找到文件中的 import 区域(第 3 行附近),在 antd 的 import 中添加 `Grid`
```typescript
import { Layout, Menu, Button, Avatar, Dropdown, Drawer, Grid } from 'antd';
```
然后替换第 86-98 行的状态和 useEffect
```typescript
const MainLayout: React.FC = () => {
const [collapsed, setCollapsed] = useState(false);
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
const [drawerOpen, setDrawerOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const user = JSON.parse(localStorage.getItem('user') || '{}');
const { hasPermission } = usePermission();
```
移除原来的 `useEffect` + `resize` 事件监听(第 94-98 行)和 `useState(window.innerWidth < 768)`(第 87 行)。
- [x] **Step 2: 更新 Sider/平板/Drawer 渲染逻辑**
替换第 150-187 行的侧栏区域:
```typescript
return (
<Layout style={{ minHeight: '100vh' }}>
{/* 桌面 + 平板Sider手机Drawer */}
{!isMobile && (
<Sider
trigger={null}
collapsible
collapsed={isTablet ? true : collapsed}
theme="light"
style={{ background: '#fff', borderRight: '1px solid #e5e5e7' }}
>
<div
style={{
height: 64,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#1d1d1f',
fontSize: (isTablet || collapsed) ? 16 : 17,
fontWeight: 600,
borderBottom: '1px solid #e5e5e7',
}}
>
{(isTablet || collapsed) ? '恭' : '恭学教育基地'}
</div>
{menuContent}
</Sider>
)}
{isMobile && (
<Drawer
placement="left"
open={drawerOpen}
onClose={() => setDrawerOpen(false)}
width={240}
styles={{ body: { padding: 0 } }}
title="恭学教育基地"
>
{menuContent}
</Drawer>
)}
```
关键变化:平板 (`isTablet`) 时 Sider 强制折叠 (`collapsed={isTablet ? true : collapsed}`)。
- [x] **Step 3: 更新 Collapse 按钮逻辑**
替换第 200-212 行的 Button onClick
```typescript
<Button
type="text"
icon={
isMobile ? (
<MenuUnfoldOutlined />
) : (isTablet || collapsed) ? (
<MenuUnfoldOutlined />
) : (
<MenuFoldOutlined />
)
}
onClick={() => {
if (isMobile) {
setDrawerOpen(true);
} else if (isTablet) {
setDrawerOpen(true);
} else {
setCollapsed(!collapsed);
}
}}
/>
```
平板下点击折叠按钮打开 Drawer因为 Sider 已折叠到图标态,用户需要展开菜单)。
- [x] **Step 4: 隐藏 Header 用户区域文字(手机/平板)**
替换第 225-229 行的用户信息区域:
```typescript
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}>
<Avatar icon={<UserOutlined />} />
{isDesktop && <span>{user.name || user.username || '用户'}</span>}
</div>
```
手机和平板 (`!isDesktop`) 下仅显示 Avatar隐藏用户名文字以节省空间。
- [x] **Step 5: 更新 Content padding**
替换第 231-241 行的 Content 组件:
```typescript
<Content
style={{
margin: isMobile ? 12 : isTablet ? 16 : 24,
padding: isMobile ? 12 : isTablet ? 16 : 24,
background: '#fff',
borderRadius: 12,
overflow: 'auto',
}}
>
<Outlet />
</Content>
```
- [x] **Step 6: Commit**
```bash
git add apps/admin/src/layouts/MainLayout.tsx
git commit -m "feat: MainLayout 三端布局重构,使用 antd Grid.useBreakpoint()"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 3: Dashboard 响应式网格和图表
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Dashboard/index.tsx`
**Interfaces:**
- Consumes: antd `Grid` (for `useBreakpoint`)
- Produces: 响应式统计卡片 Col 断点、图表高度自适应、工具栏堆叠
**Description:** 将统计卡片的 `span={6}` 改为响应式断点、图表卡片 `span={12}` 改为 `xs={24} sm={12}`、图表高度按设备自适应、顶部标题和 DatePicker 在小屏堆叠。
- [x] **Step 1: 添加 Grid import**
在第 2 行antd import 中添加 `Grid`
```typescript
import { Row, Col, Card, Statistic, DatePicker, Spin, Grid } from 'antd';
```
在组件内部顶部添加断点检测(`const [stats, setStats]...` 之前):
```typescript
const screens = Grid.useBreakpoint();
const isMobile = !screens.sm;
const isDesktop = !!screens.lg;
```
- [x] **Step 2: 统计卡片 Col 改为响应式断点**
替换第 179-214 行的统计卡片 Row
```typescript
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
<Col xs={12} sm={12} md={6}>
<Card>
<Statistic title="宿舍总数" value={stats?.totalRooms || 0} prefix={<HomeOutlined />} />
</Card>
</Col>
<Col xs={12} sm={12} md={6}>
<Card>
<Statistic
title="在读学生"
value={stats?.totalStudents || 0}
prefix={<TeamOutlined />}
/>
</Card>
</Col>
<Col xs={12} sm={12} md={6}>
<Card>
<Statistic
title="当前在住"
value={stats?.occupiedBeds || 0}
suffix={`/ ${stats?.totalCapacity || 0}`}
prefix={<CheckCircleOutlined />}
/>
</Card>
</Col>
<Col xs={12} sm={12} md={6}>
<Card>
<Statistic
title="入住率"
value={stats?.occupancyRate || 0}
suffix="%"
prefix={<DollarOutlined />}
/>
</Card>
</Col>
</Row>
```
变化:`span={6}` -> `xs={12} sm={12} md={6}`Row 的 `gutter={16}` -> `gutter={[16, 16]}` 增加垂直间距。
- [x] **Step 3: 图表卡片 Col 改为响应式断点**
替换第 227-246 行的图表 Row
```typescript
<Row gutter={[16, 16]}>
<Col xs={24} sm={12}>
<Card title="费用类型分布">
{expenseStats.length > 0 ? (
<ReactECharts
option={pieOption}
style={{ width: '100%', height: isMobile ? 250 : 300 }}
/>
) : (
<div style={{ textAlign: 'center', padding: 40, color: '#999' }}>暂无费用数据</div>
)}
</Card>
</Col>
<Col xs={24} sm={12}>
<Card title="宿舍费用排行 TOP 20">
{roomRanking.length > 0 ? (
<ReactECharts
option={barOption}
style={{ width: '100%', height: isMobile ? 250 : 300 }}
/>
) : (
<div style={{ textAlign: 'center', padding: 40, color: '#999' }}>暂无费用数据</div>
)}
</Card>
</Col>
</Row>
```
变化:
- `span={12}` -> `xs={24} sm={12}`(手机堆叠,平板及以上并排)
- ECharts style 添加 `width: '100%'` 确保跟随容器 resize
- 高度改为 `isMobile ? 250 : 300`
- [x] **Step 4: 甘特图 ECharts 添加 width: '100%'**
替换第 216-225 行的甘特图区域:
```typescript
<Card title="入住时间线(甘特图)" style={{ marginBottom: 24 }}>
{ganttData.length > 0 ? (
<ReactECharts
option={ganttOption()}
style={{ width: '100%', height: Math.max(300, ganttData.length * 40) }}
/>
) : (
<div style={{ textAlign: 'center', padding: 40, color: '#999' }}>暂无入住数据</div>
)}
</Card>
```
- [x] **Step 5: 顶部工具栏小屏堆叠**
替换第 162-177 行的标题和 DatePicker 区域:
```typescript
<div
style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
alignItems: isMobile ? 'flex-start' : 'center',
flexDirection: isMobile ? 'column' : 'row',
gap: 12,
}}
>
<h2 style={{ margin: 0 }}>数据面板</h2>
<RangePicker
value={[dayjs(period[0]), dayjs(period[1])]}
onChange={(dates) => {
if (dates) setPeriod([dates[0]!.format('YYYY-MM-DD'), dates[1]!.format('YYYY-MM-DD')]);
}}
/>
</div>
```
变化:小屏下 `flexDirection: 'column'` + `alignItems: 'flex-start'` + `gap: 12`
- [x] **Step 6: Commit**
```bash
git add apps/admin/src/pages/Dashboard/index.tsx
git commit -m "feat: Dashboard 响应式网格,统计卡片/图表/工具栏适配三端"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 4: 学生管理字段拆分和响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Students/index.tsx`
**Interfaces:**
- Consumes: 后端 `/students` API 返回学生对象(含 `studentNumber` 可选字段)
- Produces: 拆分后的列定义、表格 scroll、工具栏 wrap
**Description:** 将「学号/身份证」列拆分为「学号」和「身份证」两个独立列,添加 `ellipsis``width`;表格添加 `scroll={{ x }}`;顶部工具栏添加 `flexWrap: 'wrap'` + `gap`;表单中添加 `studentNumber` 字段。
- [x] **Step 1: 拆分列定义**
替换第 151-156 行的 columns 数组中的对应条目。将:
```typescript
{ title: '学号/身份证', dataIndex: 'idNumber' },
```
替换为:
```typescript
{
title: '学号',
dataIndex: 'studentNumber',
width: 120,
ellipsis: true,
render: (v: string) => v || '-',
},
{
title: '身份证',
dataIndex: 'idNumber',
width: 180,
ellipsis: true,
render: (v: string) => v || '-',
},
```
同时为其余缺少 `ellipsis` 的文本列添加 `ellipsis: true`name, phone, emergencyContact, emergencyPhone, supervisor
```typescript
{ title: 'ID', dataIndex: 'id', width: 60 },
{ title: '姓名', dataIndex: 'name', ellipsis: true },
{ title: '性别', dataIndex: 'gender', width: 60 },
{ title: '电话', dataIndex: 'phone', ellipsis: true },
// ... 上面已替换的学号和身份证列
{ title: '民族', dataIndex: 'ethnicity', width: 80 },
{ title: '紧急联系人', dataIndex: 'emergencyContact', ellipsis: true },
{ title: '紧急联系人电话', dataIndex: 'emergencyPhone', ellipsis: true },
{
title: '所属机构',
dataIndex: 'organization',
render: (v: string) => (v ? <Tag color="purple">{v}</Tag> : '-'),
},
{ title: '负责人', dataIndex: 'supervisor', ellipsis: true },
```
- [x] **Step 2: 表格添加 scroll**
在第 305 行的 `<Table` 组件上添加 `scroll` 属性:
```typescript
<Table
columns={columns}
dataSource={data}
rowKey="id"
loading={loading}
scroll={{ x: 1200 }}
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 人` }}
```
- [x] **Step 3: 工具栏添加 wrap 和 gap**
替换第 223-304 行的工具栏 JSX。在顶部 `<div>` 和左右 `<Space>` 之间改为:
```typescript
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
<Space wrap>
<Input.Search ... />
<Button ... />
</Space>
<Space wrap>
{/* 右侧所有按钮 */}
</Space>
</div>
```
变化:
- 外层 div 添加 `flexWrap: 'wrap'``gap: 8`
- 左右 Space 均添加 `wrap` 属性
- [x] **Step 4: 表单中添加学号字段**
在 modal 中的 `<Form>` 区域(第 329 行附近),在 `name` 字段和 `gender` 字段之间添加学号字段:
```typescript
<Form.Item name="studentNumber" label="学号">
<Input placeholder="学生的学号" />
</Form.Item>
```
- [x] **Step 5: Commit**
```bash
git add apps/admin/src/pages/Students/index.tsx
git commit -m "feat: 学生管理字段拆分(学号/身份证分列),表格和工具栏响应式适配"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 5: 宿舍总览 RoomVisual 复查
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/RoomVisual/index.tsx`
**Interfaces:**
- Consumes: 当前已有的 `xs/sm/md/lg` Col 响应式断点
- Produces: 确保平板断点md: >= 768px覆盖 576-991px 范围
**Description:** RoomVisual 已经使用了 `xs={12} sm={8} md={6} lg={4}` 等响应式 Col props只需确认布局在 576-991px 平板区域表现合理。弹窗已使用 `width={500}`,全局 CSS 会覆盖窄屏。
- [x] **Step 1: 验证当前布局**
查看房态网格行(第 112 行附近),当前为 `xs={12} sm={8} md={6} lg={4}`
- 手机 (xs: < 576px): 2 -- OK
- sm (>= 576px): 3 列 -- OK
- md (>= 768px): 4 列 -- OK
- lg (>= 992px): 6 列 -- OK
平板区域576-991px覆盖了 sm 和 md 两个断点,从 3 列到 4 列,布局合理。无需修改。
- [x] **Step 2: 统计卡片栏复查**
第 87-107 行的统计卡片当前为 `xs={12} sm={6}`
- 手机: 2 列
- 平板及以上 (>= 576px): 4 列
在平板下 4 张卡并排可能略挤,改为 `xs={12} sm={6} md={6}` 以保证桌面端也是 4 列,平板端保持 4 列(可用)。无需修改。
- [x] **Step 3: 弹窗复查**
详情弹窗(第 182-249 行)`width={500}`,在手机下由全局 CSS `max-width: calc(100vw - 24px)` 覆盖。无需修改。
- [x] **Step 4: Commit**
```bash
git add apps/admin/src/pages/RoomVisual/index.tsx
git commit -m "chore: RoomVisual 三端断点复查确认,无改动"
```
注:如果复查无改动,可跳过此 commit或提交一个空 commit 标记完成。
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 6: 入住管理 Occupancies 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Occupancies/index.tsx`
**Interfaces:**
- Consumes: 已有 Alert、Tab 切换区、搜索/操作区
- Produces: 表格 scroll、工具栏 wrap、弹窗宽度全局 CSS 覆盖)
**Description:** 表格添加 `scroll={{ x }}`;顶部 Alert + Tab + 搜索/操作区支持小屏堆叠;弹窗宽度由全局 CSS 约束。
- [x] **Step 1: 表格添加 scroll**
在第 444 行的 `<Table` 组件上添加 `scroll` 属性:
```typescript
<Table
columns={columns}
dataSource={filteredData}
rowKey="id"
loading={loading}
scroll={{ x: 900 }}
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
rowSelection={rowSelection}
/>
```
- [x] **Step 2: 工具栏 wrap 验证**
查看第 251-395 行的工具栏 JSX。外层 div 已有 `flexWrap: 'wrap'``gap: 8`。左侧 Space 有 `wrap`,右侧 Space 有 `wrap`。这些已经正确,无需修改。
- [x] **Step 3: 导入时自动收押金设置行检查**
第 380-393 行的收押金设置行已经是内联 flex在小屏下会自动换行。当前实现使用 `<span>` 包裹,换行体验一般但可用。无需修改。
- [x] **Step 4: 弹窗宽度**
当前弹窗:
- 入住登记: `width={500}`(第 460 行)
- 退宿: 无 width 属性(第 517 行,默认 520px
- 批量退宿: `width={500}`(第 557 行)
- 换房: `width={500}`(第 610 行)
这些弹窗在小屏下由全局 CSS `@media (max-width: 575px)``.ant-modal { max-width: calc(100vw - 24px) !important }` 约束。平板弹窗由 `@media (min-width: 576px) and (max-width: 991px)` 约束为 `max-width: calc(100vw - 48px)`。无需额外修改。
- [x] **Step 5: Commit**
```bash
git add apps/admin/src/pages/Occupancies/index.tsx
git commit -m "feat: 入住管理表格添加 scroll工具栏/弹窗响应式适配"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 7: 宿舍管理 Rooms 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Rooms/index.tsx`
**Interfaces:**
- Consumes: 已有搜索/筛选/按钮工具栏
- Produces: 表格 scroll、工具栏 wrap 和 gap
**Description:** 表格添加 `scroll={{ x }}`;确保工具栏和操作列在三端适配。
- [x] **Step 1: 表格添加 scroll**
在第 374 行的 `<Table` 组件上添加 `scroll` 属性:
```typescript
<Table
columns={columns}
dataSource={filteredData}
rowKey="id"
loading={loading}
scroll={{ x: 900 }}
pagination={{ pageSize: 20, showTotal: (total) => `共 ${total} 间` }}
rowClassName={(record) => (record.status === 'archived' ? 'archived-row' : '')}
rowSelection={{
selectedRowKeys,
onChange: (keys) => setSelectedRowKeys(keys as number[]),
getCheckboxProps: (record: any) => ({ disabled: record.status === 'archived' }),
}}
/>
```
- [x] **Step 2: 工具栏已有 wrap 和 gap**
查看第 279-372 行的工具栏 JSX。外层 div 已有 `flexWrap: 'wrap'``gap: 8`。左右 Space 均已有 `wrap`。这些已经正确,无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/Rooms/index.tsx
git commit -m "feat: 宿舍管理表格添加 scroll 横向滚动"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 8: 费用录入 Expenses 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Expenses/index.tsx`
**Interfaces:**
- Consumes: Tabs 的两个 Tab宿舍费用/个人附加费),各有独立 Table 和工具栏
- Produces: 两个表格分别添加 scroll、弹窗宽度全局 CSS
**Description:** 宿舍费用和个人附加费两个 Tab 下的表格均添加 `scroll={{ x }}`;弹窗由全局 CSS 覆盖。
- [x] **Step 1: 宿舍费用表格添加 scroll**
在第 435 行的宿舍费用 `<Table` 上添加 `scroll`
```typescript
<Table
columns={roomColumns}
dataSource={filteredRoomExpenses}
rowKey="id"
loading={loading}
scroll={{ x: 800 }}
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
rowSelection={{
selectedRowKeys: selectedRoomKeys,
onChange: (keys) => setSelectedRoomKeys(keys as number[]),
}}
/>
```
- [x] **Step 2: 个人附加费表格添加 scroll**
在第 587 行的个人附加费 `<Table` 上添加 `scroll`
```typescript
<Table
columns={personalColumns}
dataSource={filteredPersonalExpenses}
rowKey="id"
loading={loading}
scroll={{ x: 800 }}
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
rowSelection={{
selectedRowKeys: selectedPersonalKeys,
onChange: (keys) => setSelectedPersonalKeys(keys as number[]),
}}
/>
```
- [x] **Step 3: 工具栏 wrap 验证**
第 319-326 行和第 455-461 行的工具栏外层 div 已有 `flexWrap: 'wrap'``gap: 8`。Space 均有 `wrap`。无需修改。
- [x] **Step 4: 弹窗验证**
录入宿舍费用弹窗(第 604 行)和个人附加费弹窗(第 644 行)均无显式 width由全局 CSS 约束窄屏。无需修改。
- [x] **Step 5: Commit**
```bash
git add apps/admin/src/pages/Expenses/index.tsx
git commit -m "feat: 费用录入两个表格添加 scroll 横向滚动"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 9: 押金管理 Deposits 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Deposits/index.tsx`
**Interfaces:**
- Consumes: 已有搜索/筛选工具栏
- Produces: 表格 scroll、工具栏 wrap、弹窗宽度
**Description:** 表格添加 `scroll={{ x }}`;搜索/筛选工具栏 wrap押金收取和退还弹窗由全局 CSS 覆盖。
- [x] **Step 1: 表格添加 scroll**
在第 211 行的 `<Table` 上添加 `scroll`
```typescript
<Table
columns={columns}
dataSource={filteredData}
rowKey="id"
loading={loading}
scroll={{ x: 1000 }}
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
/>
```
列数较多(学生、金额、日期、状态、退还金额、扣除金额、原因、日期、备注 + 操作),使用 `x: 1000`
- [x] **Step 2: 工具栏已有 wrap 和 gap**
第 165-172 行的工具栏外层 div 已有 `flexWrap: 'wrap'``gap: 8`Space 有 `wrap`。无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/Deposits/index.tsx
git commit -m "feat: 押金管理表格添加 scroll 横向滚动"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 10: 账单管理 Bills 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Bills/index.tsx`
**Interfaces:**
- Consumes: 已有搜索/筛选/按钮工具栏,详情弹窗含嵌套 Table
- Produces: 表格 scroll、工具栏 wrap、弹窗宽度
**Description:** 表格添加 `scroll={{ x }}`;搜索/筛选/按钮工具栏已有 wrap生成账单弹窗和详情弹窗由全局 CSS 覆盖。
- [x] **Step 1: 主表格添加 scroll**
在第 388 行的 `<Table` 上添加 `scroll`
```typescript
<Table
columns={columns}
dataSource={filteredBills}
rowKey="id"
loading={loading}
scroll={{ x: 1200 }}
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
rowSelection={{
selectedRowKeys: selectedRows,
onChange: (keys) => setSelectedRows(keys as number[]),
}}
/>
```
账单列数多(学生、周期、分摊费、个人费、总计、可用押金、抵扣后应付、状态、时间 + 操作列宽320使用 `x: 1200`
- [x] **Step 2: 工具栏已有 wrap 和 gap**
第 307-314 行的工具栏外层 div 已有 `flexWrap: 'wrap'``gap: 8`Space 有 `wrap`。无需修改。
- [x] **Step 3: 详情弹窗中的内嵌表格添加 scroll**
在第 494 行的详情弹窗内嵌 `<Table` 上添加 `scroll`
```typescript
<Table
dataSource={detailModal.items || []}
rowKey="id"
pagination={false}
size="small"
scroll={{ x: 700 }}
columns={[
{ title: '类型', dataIndex: 'expenseType', render: (v: string) => typeMap[v] || v },
{ title: '说明', dataIndex: 'description' },
{
title: '计费天数',
dataIndex: 'days',
render: (v: number) => (v > 0 ? `${v}天` : '-'),
},
{
title: '宿舍总人天',
dataIndex: 'totalRoomDays',
render: (v: number) => (v > 0 ? `${v}天` : '-'),
},
{
title: '宿舍总费用',
dataIndex: 'roomTotalAmount',
render: (v: number) => ${Number(v).toFixed(2)}`,
},
{
title: '应分摊',
dataIndex: 'studentAmount',
render: (v: number) => <strong>¥{Number(v).toFixed(2)}</strong>,
},
]}
/>
```
- [x] **Step 4: Commit**
```bash
git add apps/admin/src/pages/Bills/index.tsx
git commit -m "feat: 账单管理表格添加 scroll详情内嵌表格也添加 scroll"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 11: 教室管理 Classrooms 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Classrooms/index.tsx`
**Interfaces:**
- Consumes: 已有搜索/筛选工具栏
- Produces: 表格 scroll
**Description:** 表格添加 `scroll={{ x }}`;工具栏已有 wrap。
- [x] **Step 1: 表格添加 scroll**
在第 265 行的 `<Table` 上添加 `scroll`
```typescript
<Table
columns={columns}
dataSource={filteredData}
rowKey="id"
loading={loading}
scroll={{ x: 900 }}
pagination={{ pageSize: 20, showTotal: (total) => `共 ${total} 条` }}
/>
```
- [x] **Step 2: 工具栏已有 wrap 和 gap**
第 194-201 行的工具栏外层 div 已有 `flexWrap: 'wrap'``gap: 8`Space 有 `wrap`。无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/Classrooms/index.tsx
git commit -m "feat: 教室管理表格添加 scroll 横向滚动"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 12: 租赁订单 ClassroomRentals 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/ClassroomRentals/index.tsx`
**Interfaces:**
- Consumes: 已有 `scroll={{ x: 1200 }}`
- Produces: 弹窗宽度
**Description:** 表格已有 `scroll={{ x: 1200 }}`,值合理无需修改;工具栏已有 wrap 和 gap弹窗 `width={600}` 由全局 CSS 覆盖窄屏。
- [x] **Step 1: 复查现有 scroll 值**
第 318 行已有 `scroll={{ x: 1200 }}`。该表有 10+ 列(教室、租赁方、开始日期、结束日期、时长、日租金、总额、合同、操作),`x: 1200` 合理。无需修改。
- [x] **Step 2: 工具栏已有 wrap 和 gap**
第 277-284 行的工具栏外层 div 已有 `flexWrap: 'wrap'``gap: 8`Space 有 `wrap`。无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/ClassroomRentals/index.tsx
git commit -m "chore: ClassroomRentals 复查 scroll 和 wrap确认无需修改"
```
注:如果复查无改动,可跳过此 commit。
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 13: 租赁方 Tenants 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Tenants/index.tsx`
**Interfaces:**
- Consumes: 已有工具栏
- Produces: 表格 scroll
**Description:** 表格添加 `scroll={{ x }}`;工具栏已有 wrap 和 gap。
- [x] **Step 1: 表格添加 scroll**
在第 183 行的 `<Table` 上添加 `scroll`
```typescript
<Table
columns={columns}
dataSource={filteredData}
rowKey="id"
loading={loading}
scroll={{ x: 700 }}
pagination={{ pageSize: 20, showTotal: (total) => `共 ${total} 条` }}
/>
```
列较少(名称、联系人、电话、颜色、备注 + 操作),使用 `x: 700`
- [x] **Step 2: 工具栏已有 wrap 和 gap**
第 152-159 行的工具栏外层 div 已有 `flexWrap: 'wrap'``gap: 8`。无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/Tenants/index.tsx
git commit -m "feat: 租赁方表格添加 scroll 横向滚动"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 14: 操作日志 OperationLogs 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/OperationLogs/index.tsx`
**Interfaces:**
- Consumes: 已有 `scroll={{ x: 1000 }}` 和工具栏
- Produces: 确认现有适配
**Description:** 表格已有 `scroll={{ x: 1000 }}` 且值合理8 列),无需修改;工具栏已有 wrap 和 gap。
- [x] **Step 1: 复查现有 scroll 值**
第 151 行已有 `scroll={{ x: 1000 }}`。该表有 8 列时间、操作人、模块、操作、状态、详情、IP、终端`x: 1000` 合理。无需修改。
- [x] **Step 2: 工具栏已有 flexWrap 和 gap**
第 113-121 行已有 `flexWrap: 'wrap'``gap: 12`。无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/OperationLogs/index.tsx
git commit -m "chore: OperationLogs 复查 scroll 和 wrap确认无需修改"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 15: 角色管理 Roles 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Roles/index.tsx`
**Interfaces:**
- Consumes: 已有 `scroll={{ x: 800 }}`、工具栏、权限分配弹窗
- Produces: 工具栏 wrap、弹窗宽度
**Description:** 表格已有 `scroll={{ x: 800 }}`;工具栏添加 wrap权限分配弹窗 `width={700}` 由全局 CSS 覆盖窄屏。
- [x] **Step 1: 工具栏添加 flexWrap 和 gap**
替换第 206-212 行的工具栏 div
```typescript
<div
style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
gap: 8,
}}
>
<h2 style={{ margin: 0 }}>角色管理</h2>
<PermissionButton ...>新增角色</PermissionButton>
</div>
```
- [x] **Step 2: 复查表格 scroll**
第 224 行已有 `scroll={{ x: 800 }}`。该表有 ID、名称、描述、权限标签、系统 + 操作列fixed: right`x: 800` 合理。无需修改。
- [x] **Step 3: 弹窗宽度**
权限分配弹窗第 238 行 `width={700}`,在窄屏下由全局 CSS 覆盖。无需修改。
- [x] **Step 4: Commit**
```bash
git add apps/admin/src/pages/Roles/index.tsx
git commit -m "feat: 角色管理工具栏添加 flexWrap 响应式适配"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 16: 权限一览 Permissions 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Permissions/index.tsx`
**Interfaces:**
- Consumes: 卡片列表(非 Table 页面)
- Produces: 工具栏 wrap
**Description:** 权限一览是卡片列表(非表格),只需确保标题 + 搜索栏在小屏下堆叠。
- [x] **Step 1: 工具栏添加 flexWrap 和 gap**
替换第 58-64 行的工具栏 div
```typescript
<div
style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
gap: 8,
}}
>
<h2 style={{ margin: 0 }}>权限一览</h2>
<Input.Search ... />
</div>
```
- [x] **Step 2: Commit**
```bash
git add apps/admin/src/pages/Permissions/index.tsx
git commit -m "feat: 权限一览工具栏添加 flexWrap 响应式适配"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 17: 账号管理 Users 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Users/index.tsx`
**Interfaces:**
- Consumes: 已有 `scroll={{ x: 1000 }}`
- Produces: 工具栏 wrap
**Description:** 表格已有 `scroll={{ x: 1000 }}`;工具栏添加 wrap。
- [x] **Step 1: 工具栏添加 flexWrap 和 gap**
替换第 198-204 行的工具栏 div
```typescript
<div
style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
gap: 8,
}}
>
<h2 style={{ margin: 0 }}>账号管理</h2>
<PermissionButton ...>新增账号</PermissionButton>
</div>
```
- [x] **Step 2: 复查表格 scroll**
第 216 行已有 `scroll={{ x: 1000 }}`。该表有 ID、用户名、姓名、角色、状态、最后登录、创建时间 + 操作列fixed: right`x: 1000` 合理。无需修改。
- [x] **Step 3: Commit**
```bash
git add apps/admin/src/pages/Users/index.tsx
git commit -m "feat: 账号管理工具栏添加 flexWrap 响应式适配"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 18: 教室排期 ClassroomSchedule 特殊适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/ClassroomSchedule/index.tsx`
**Interfaces:**
- Consumes: 已有 `overflowX: 'auto'` 容器、sticky 首列、minWidth 日期列
- Produces: 确认日期列不被挤压sticky 列在小屏正常
**Description:** 教室排期是自定义 HTML `<table>`,含 31+ 日期列 + sticky 首列。当前外层 div 已有 `overflowX: 'auto'`,日期列 `minWidth: 26`,首列 sticky。确认在平板/手机下正常工作。
- [x] **Step 1: 日期列 minWidth 不变**
第 234 行已设置 `minWidth: 26`,在窄屏下不会挤压。无需修改。
- [x] **Step 2: 首列 sticky 左偏移确认**
第 210 行首列已有 `position: 'sticky', left: 0`,在小屏下仍然生效。无需修改。
- [x] **Step 3: 详情弹窗宽度**
第 327 行弹窗 `width={500}`,在窄屏下由全局 CSS 覆盖。无需修改。
- [x] **Step 4: Commit**
```bash
git add apps/admin/src/pages/ClassroomSchedule/index.tsx
git commit -m "chore: ClassroomSchedule 复查 sticky 列和日期列 minWidth确认无需修改"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 19: 登录页 Login 响应式适配
**Files:**
- Modify: `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Login/index.tsx`
**Interfaces:**
- Consumes: 已有 Card 登录表单
- Produces: 卡片宽度改为 maxWidth + calc
**Description:** 登录卡片当前 `width: 400`,改为 `maxWidth: 400, width: 'calc(100vw - 48px)'` 以在手机下自适应。
- [x] **Step 1: 卡片宽度响应式**
替换第 39-45 行的 Card style
```typescript
<Card
style={{
maxWidth: 400,
width: 'calc(100vw - 48px)',
borderRadius: 16,
boxShadow: '0 4px 24px rgba(0,0,0,0.08)',
border: 'none',
}}
>
```
变化:`width: 400` -> `maxWidth: 400, width: 'calc(100vw - 48px)'`
- [x] **Step 2: Commit**
```bash
git add apps/admin/src/pages/Login/index.tsx
git commit -m "feat: 登录卡片改为 maxWidth + calc 响应式宽度"
```
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Task 20: 全局验证与编译检查
**Files:**
- No specific file modifications; validation only
**Interfaces:**
- Consumes: 所有已完成的页面改动
- Produces: 验证报告(视觉 + 编译)
**Description:** 在 Chrome DevTools 响应式模式下手动检查所有 16 个页面在三端视口宽度下的表现,确认无溢出、弹窗不出屏、表格可横向滚动。最后运行 `npm run build` 确保 TypeScript 编译通过。
- [x] **Step 1: Chrome DevTools 响应式模式视觉检查**
打开浏览器访问 `http://localhost:5173`(或部署地址),使用 Chrome DevTools 的设备工具栏切换以下宽度:
| 宽度 | 端侧 | 检查要点 |
|------|------|---------|
| 375px | 手机 | 表格可横向滚动、按钮不溢出、弹窗不出屏、文字不截断 |
| 768px | 平板 | Sider 默认折叠、Content padding 为 16px、卡片网格正确 |
| 992px | 桌面小屏 | Sider 可展开、Content padding 为 24px |
| 1440px | 桌面大屏 | 全部正常,无奇怪空白 |
逐页检查16 个页面):
1. /dashboard
2. /room-visual
3. /students
4. /rooms
5. /occupancies
6. /expenses
7. /deposits
8. /bills
9. /classrooms
10. /classroom-rentals
11. /tenants
12. /classroom-schedule
13. /operation-logs
14. /roles
15. /permissions
16. /users
17. /login
每个页面检查项:
- [x] 表格有无横向滚动条(在窄屏下出现,且不影响行选择/操作)
- [x] 按钮组是否错位/溢出容器
- [x] 弹窗(打开任意新增/编辑弹窗)是否超出屏幕边界
- [x] 统计卡片/网格是否按预期列数排列
- [x] ECharts 图表是否正确跟随容器宽度
- [x] 文字是否正常截断ellipsis 生效)
- [x] **Step 2: TypeScript 编译检查**
```bash
cd /Users/tiku1/code/gongxue-base
npm run build
```
预期:`tsc -b && vite build` 无错误输出,构建成功。
如果有 TypeScript 错误,修复后再执行:
```bash
cd /Users/tiku1/code/gongxue-base/apps/admin
npx tsc -b --noEmit
```
- [x] **Step 3: Commit**
```bash
git add -A
git commit -m "chore: 全局响应式适配验证完成"
```
如果验证中发现问题需要修复,则在对应页面进行修复后单独 commit。
archived-with: 2026-07-03-admin-responsive-adaptation
---
### Critical Files for Implementation
- `/Users/tiku1/code/gongxue-base/apps/admin/src/index.css`
- `/Users/tiku1/code/gongxue-base/apps/admin/src/layouts/MainLayout.tsx`
- `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Dashboard/index.tsx`
- `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Students/index.tsx`
- `/Users/tiku1/code/gongxue-base/apps/admin/src/pages/Occupancies/index.tsx`