feat(web-admin): 手机小屏适配(1Panel 风格抽屉导航)并补齐移动端自动化验证

## 布局与交互(沿用 1Panel 风格)

- 新增 `src/composables/useResponsive.ts`:断点 768 / 992px + resize/orientationchange 监听
- `AdminLayout.vue`:
  - >992px 桌面保持原样(210px 深色侧栏 + 顶栏连接状态 + "已接收 N 条实时事件")
  - <=768px 手机:侧边栏不再渲染,顶栏出现汉堡按钮(`aria-expanded` 标记状态),
    点击唤出 240px 深色抽屉菜单,点菜单项自动收起;顶栏显示当前页标题,
    用户菜单收成头像图标(身份信息移入下拉菜单首行)
  - 复用同一套 `.ch-sidebar` 样式,抽屉内容与桌面侧栏视觉一致
- `styles/index.css` 增加全局响应式规则:工具栏控件铺满整行、`.table-card` 表格卡片内横向滚动、
  弹窗 `calc(100vw - 24px)`、消息框/抽屉自适应、`.stat-grid` 小屏两列、分页居中、标签页可横滑
- 各视图:7 个表格卡片加 `.table-card`;班级详情/作业详情的 `el-descriptions` 小屏降为单列;
  登录页卡片自适应宽度并加安全内边距

## 自动化验证(scripts/ui-smoke)

- 新增 `runMobileChecks`:把窗口真实缩到 390×844,用真实点击验证 7 项——
  侧边栏收起 + 汉堡出现、抽屉滑出且菜单完整、抽屉内点击"通知发布"跳转并收起、
  页面无横向溢出(表格自身滚动不计入)、表格在卡片内可滚动、工具栏控件铺满、弹窗宽度自适应
- 移动端截图留档到 `docs/screenshots/web-mobile/`(`UI_SMOKE_SHOTS_DIR` 可覆盖)
- 踩坑修复:隐藏窗口下 Chromium 会冻结 CSS transition,Vue `<Transition>` 收不到 transitionend,
  Element Plus 抽屉会卡在 `enter-from`(表现为"点了没反应")——移动端检查改为 `showInactive()` 并断言
  "抽屉滑到位/收起到位",而不是只看元素是否存在

## 安装包升级健壮性

- `scripts/nsis/server-installer.nsi`:覆盖安装前先 `RMDir /r web|server|node_modules`,
  避免旧 hash 分片、旧 dist 模块、旧依赖残留导致"新旧版本混装"
  (此前 web/assets 里累积了 4 份 AdminLayout 分片)

## 验证结果(均在安装后的生产实例上)

- `pnpm verify:web` 15/15 ✅(8 项原有 + 7 项移动端)
- `pnpm verify:e2e` 62/62 ✅
- 覆盖安装:`.env` 与数据库保持不变,`web/assets` 只剩当前版本分片,服务 `healthz` 正常
- `pnpm lint` / `format:check` / `typecheck` 全部通过
This commit is contained in:
LoveChengke committed 2026-09-12 10:09:11 +08:00
1 parent e434032b6e
commit f759aac0a2
17 files changed
+714 -52

No files matched your search

+145
View File
@@ -2,6 +2,9 @@
--ch-bg: #f5f7fa;
--ch-card-radius: 10px;
--ch-border: #e4e7ed;
/* 1Panel 风格:固定顶栏 + 深色侧栏 */
--ch-header-height: 56px;
--ch-aside-width: 210px;
}
* {
@@ -20,6 +23,8 @@ body {
font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: var(--ch-bg);
color: #303133;
/* 小屏上禁止整页横向滚动(内容用卡片内滚动承载) */
overflow-x: hidden;
}
.page {
@@ -117,3 +122,143 @@ body {
background: #f56c6c;
margin-right: 6px;
}
/* ---------------------------------------------------------------- 小屏适配 */
/*
* 断点:<=768px 手机(侧边栏抽屉化、工具栏铺满、表格卡片内横向滚动),
* 769-992px 平板(保留侧边栏,适度压缩),>992px 桌面(保持原样)。
*/
/* 平板:内容区收紧一点,避免横向滚动 */
@media (max-width: 992px) {
.page {
padding: 14px 14px 20px;
}
.page-header {
margin-bottom: 12px;
}
}
@media (max-width: 768px) {
.page {
padding: 12px 12px calc(16px + env(safe-area-inset-bottom, 0px));
}
.page-header {
margin-bottom: 10px;
gap: 8px;
}
.page-title {
font-size: 16px;
}
.page-subtitle {
font-size: 12px;
}
/* 工具栏:控件纵向铺满,方便拇指点击 */
.toolbar {
width: 100%;
gap: 8px;
}
.toolbar > .el-select,
.toolbar > .el-input,
.toolbar > .el-date-editor,
.toolbar > .el-input-number,
.toolbar > .el-dropdown,
.toolbar > .el-button {
width: 100% !important;
}
.toolbar > .el-button {
justify-content: center;
}
/* 统计卡片:小屏两列 */
.stat-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
.stat-card {
padding: 12px;
}
.stat-value {
font-size: 22px;
}
/* 表格:放进卡片内横向滚动,避免整页左右晃动 */
.table-card > .el-card__body {
padding: 8px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table-card .el-table {
min-width: 560px;
}
.table-card .el-table__inner-wrapper {
min-width: 100%;
}
/* 弹窗 / 消息框 / 抽屉:小屏自适应宽度 */
.el-dialog {
width: calc(100vw - 24px) !important;
max-width: 560px;
margin: 10px auto !important;
}
.el-dialog__body {
padding: 12px 14px !important;
}
.el-dialog__header {
padding: 14px 14px 6px !important;
}
.el-message-box {
width: calc(100vw - 40px) !important;
}
.el-drawer.ltr,
.el-drawer.rtl {
width: 86vw !important;
}
/* 侧边栏抽屉固定宽度(选择器与 .el-drawer.ltr 同权重,靠后生效) */
.el-drawer.ch-nav-drawer {
width: 240px !important;
max-width: 82vw;
}
/* 表单标签顶部对齐,输入框占满整行 */
.el-form-item {
margin-bottom: 14px;
}
.page .el-descriptions__label {
width: 76px;
}
.el-pagination {
justify-content: center;
flex-wrap: wrap;
gap: 4px;
}
/* 标签页在窄屏下可横向滚动,避免换行挤压 */
.el-tabs__nav-wrap {
overflow-x: auto;
}
.el-message {
min-width: 0;
width: calc(100vw - 32px);
}
}