chore(release): 客户端班级账号收尾 + README/安装包产物更新

- 客户端设置页展示班级码与登录方式(班级账号 / 个人学生账号)
- 冒烟脚本复用已有班级凭据(现成的班级码 + 密码可直接登录时不再改密码,避免污染实例)
- README: 学生端班级账号章节、功能页面/API/验收/命令计数(verify:e2e 125)同步
- 安装包 README 默认账号补充班级码 G101/G102/G203 + 班级密码 123456

验收: pnpm verify:e2e 125/125, verify:web 18/18, verify:desktop 40/40,
      打包产物实测(portable.exe): 40/40, login=高一(1)班 classSession=true
This commit is contained in:
LoveChengke committed 2026-09-12 16:02:37 +08:00
1 parent 1073436ad6
commit d11f8e43a1
3 files changed
+23 -7

No files matched your search

+6 -6
View File
@@ -75,7 +75,7 @@ class-helper/
│ ├── prisma/migrations/ # 迁移历史(已生成并应用) │ ├── prisma/migrations/ # 迁移历史(已生成并应用)
│ ├── prisma/seed.ts # 种子数据 │ ├── prisma/seed.ts # 种子数据
│ ├── prisma.config.ts # Prisma 7 配置(迁移/种子/连接串) │ ├── prisma.config.ts # Prisma 7 配置(迁移/种子/连接串)
│ ├── scripts/verify-e2e.mjs # 后端端到端验收脚本(107 项) │ ├── scripts/verify-e2e.mjs # 后端端到端验收脚本(125 项)
│ └── src/ │ └── src/
│ ├── app.ts # Express 装配(静态托管 + 探针 + 限流 + 模块挂载) │ ├── app.ts # Express 装配(静态托管 + 探针 + 限流 + 模块挂载)
│ ├── index.ts # 启动入口(自检/初始化 + HTTP + Socket.IO + 优雅退出) │ ├── index.ts # 启动入口(自检/初始化 + HTTP + Socket.IO + 优雅退出)
@@ -220,7 +220,7 @@ pnpm verify:desktop # Electron 客户端(含灵动岛动画/收回/已读
| `pnpm dist:dir` | 打包客户端免安装目录 `release/win-unpacked`(含可执行文件,最快) | | `pnpm dist:dir` | 打包客户端免安装目录 `release/win-unpacked`(含可执行文件,最快) |
| `pnpm dist:win` | 打包客户端 nsis 安装包 + portable 单文件 EXE | | `pnpm dist:win` | 打包客户端 nsis 安装包 + portable 单文件 EXE |
| `pnpm dist:all` | 服务端安装程序 + 客户端安装程序一起打 | | `pnpm dist:all` | 服务端安装程序 + 客户端安装程序一起打 |
| `pnpm verify:e2e` | 后端端到端验收(107 项,含角色权限矩阵与导入,需服务端已启动) | | `pnpm verify:e2e` | 后端端到端验收(125 项,含角色权限矩阵、导入与班级账号,需服务端已启动) |
| `pnpm verify:web` | Web 管理端 UI 真实点击测试(Electron 驱动,18 项,含权限入口隐藏与导入弹窗) | | `pnpm verify:web` | Web 管理端 UI 真实点击测试(Electron 驱动,18 项,含权限入口隐藏与导入弹窗) |
| `pnpm verify:desktop` | EXE 客户端冒烟验证(40 项,含灵动岛动画/收回/已读/作业/叫人、课表时间轴) | | `pnpm verify:desktop` | EXE 客户端冒烟验证(40 项,含灵动岛动画/收回/已读/作业/叫人、课表时间轴) |
| `pnpm typecheck` | 全仓库类型检查(含 `vue-tsc`) | | `pnpm typecheck` | 全仓库类型检查(含 `vue-tsc`) |
@@ -302,12 +302,12 @@ pnpm verify:desktop # Electron 客户端(含灵动岛动画/收回/已读
### 功能页面 ### 功能页面
| 页面 | 功能 | | 页面 | 功能 |
| ---- | ----------------------------------------------------------------------------------------- | | ---- | -------------------------------------------------------------------------------------------- |
| 登录 | 服务器地址 + 用户名 + 密码,附带「测试连接」;已登录但服务器不可达时可「离线进入」 | | 登录 | 服务器地址 + **班级码 + 班级密码**,附带「测试连接」;已登录但服务器不可达时可「离线进入」 |
| 课表 | 按周展示 7 列课表,支持上一周/下一周/下拉选择周次,高亮"今天",显示周次范围与地点 | | 课表 | 按周展示 7 列课表,支持上一周/下一周/下拉选择周次,高亮"今天",显示周次范围与地点 |
| 作业 | 列表(全部/未完成/已完成过滤)、详情抽屉、标记完成/取消完成、附件链接、逾期高亮 | | 作业 | 列表(全部/未完成/已完成过滤)、详情抽屉、**全班标记完成**/取消、附件链接、逾期高亮 |
| 通知 | 未读红点(侧边栏徽标)、优先级标签、点击自动标为已读、全部已读、未读过滤、详情抽屉 | | 通知 | 未读红点(侧边栏徽标)、优先级标签、点击自动标为已读、全部已读、未读过滤、详情抽屉 |
| 成绩 | 表格(分数/得分率进度条/等级)+ ECharts 柱状图(各次考试平均得分率)+ 等级分布 + 最近更新 | | 成绩 | 班级账号看到**全班成绩总览**:表格(分数/得分率/等级)+ ECharts 柱状图 + 等级分布 + 最近更新 |
| 设置 | 服务器地址(保存并测试)、账号信息、离线缓存条目统计与清空、客户端版本信息、退出登录 | | 设置 | 服务器地址(保存并测试)、账号信息、离线缓存条目统计与清空、客户端版本信息、退出登录 |
> 通知到达时还会由**灵动岛**在桌面中上方浮出提醒(见上一节),上课时段自动隐藏、紧急通知立即展开。 > 通知到达时还会由**灵动岛**在桌面中上方浮出提醒(见上一节),上课时段自动隐藏、紧急通知立即展开。
+11
View File
@@ -84,6 +84,17 @@ async function provisionClassAccount() {
if (!target) return null; if (!target) return null;
const code = target.code || `SMOKE${Math.floor(Math.random() * 9000 + 1000)}`; const code = target.code || `SMOKE${Math.floor(Math.random() * 9000 + 1000)}`;
// 若现有班级码 + 目标密码已经能登录,就直接复用,避免每次冒烟都改密码
if (target.code) {
const probe = await fetch(`${base}/auth/class-login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: target.code, password }),
});
if (probe.ok) return { code: target.code, password, className: target.name };
}
const patchResponse = await fetch(`${base}/classes/${target.id}/class-account`, { const patchResponse = await fetch(`${base}/classes/${target.id}/class-account`, {
method: 'PATCH', method: 'PATCH',
headers: authHeaders, headers: authHeaders,
@@ -333,7 +333,12 @@ onMounted(async () => {
<template #header><span>账号信息</span></template> <template #header><span>账号信息</span></template>
<el-descriptions :column="1" border size="small"> <el-descriptions :column="1" border size="small">
<el-descriptions-item label="姓名">{{ auth.user?.name ?? '-' }}</el-descriptions-item> <el-descriptions-item label="姓名">{{ auth.user?.name ?? '-' }}</el-descriptions-item>
<el-descriptions-item label="用户名">{{ auth.user?.username ?? '-' }}</el-descriptions-item> <el-descriptions-item :label="auth.isClassSession ? '班级码' : '用户名'">
{{ auth.user?.username ?? '-' }}
</el-descriptions-item>
<el-descriptions-item label="登录方式">
{{ auth.isClassSession ? '班级账号(本机代表全班)' : '个人学生账号' }}
</el-descriptions-item>
<el-descriptions-item label="班级">{{ auth.user?.className ?? '未分班' }}</el-descriptions-item> <el-descriptions-item label="班级">{{ auth.user?.className ?? '未分班' }}</el-descriptions-item>
<el-descriptions-item label="年级">{{ auth.user?.grade ?? '-' }}</el-descriptions-item> <el-descriptions-item label="年级">{{ auth.user?.grade ?? '-' }}</el-descriptions-item>
</el-descriptions> </el-descriptions>