diff --git a/CHANGELOG.md b/CHANGELOG.md index 2028986..f7c12f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 未发布 +## v0.4.1 — 2026-11-13 + +- 大屏客户端界面更紧凑:默认窗口由 1180×760 缩小到 920×640,内容最大宽度 880;页眉、标题、卡片间距与内边距收紧。 +- 班级信息改为整行横条(左侧班级名,右侧消息接收与班级音量),不再留出大块空白;「呼叫老师」与「声音测试」卡片并排,图标与文字对齐,设置项占满一行。窄窗口自动变为单列。 + ## v0.4.0 — 2026-11-13 - 桌面「呼叫老师」悬浮球不再始终置顶:PPT 放映或最大化窗口会盖住它,回到桌面仍可见;悬浮球加大、改为醒目的橙色和白色粗体大字,确认面板与按钮同步放大。 diff --git a/api/openapi.yaml b/api/openapi.yaml index 5d7183b..65f76fd 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "课堂叮咚 API", - "version": "0.4.0" + "version": "0.4.1" }, "servers": [ { diff --git a/deploy/.env.example b/deploy/.env.example index c5d84a2..f797d41 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -1,5 +1,5 @@ DOMAIN=classding.example.com -CLASSDING_IMAGE=ghcr.io/alumos/classding-server:v0.4.0 +CLASSDING_IMAGE=ghcr.io/alumos/classding-server:v0.4.1 # openssl rand -hex 32 JWT_SECRET= # 仅首次部署设置;创建成功后清空用户名/密码并重新创建容器 diff --git a/deploy/compose.1panel.yaml b/deploy/compose.1panel.yaml index ffcf119..cd88cad 100644 --- a/deploy/compose.1panel.yaml +++ b/deploy/compose.1panel.yaml @@ -1,7 +1,7 @@ # 粘贴到 1Panel 的编排编辑器。首次部署请填写两项密码;升级保留原值和原编排名称。 services: classding: - image: ghcr.io/alumos/classding-server:0.4.0 + image: ghcr.io/alumos/classding-server:0.4.1 platform: linux/amd64 restart: unless-stopped ports: diff --git a/deploy/compose.yaml b/deploy/compose.yaml index c385cb2..c1d9d04 100644 --- a/deploy/compose.yaml +++ b/deploy/compose.yaml @@ -1,6 +1,6 @@ services: server: - image: ${CLASSDING_IMAGE:-ghcr.io/alumos/classding-server:v0.4.0} + image: ${CLASSDING_IMAGE:-ghcr.io/alumos/classding-server:v0.4.1} restart: unless-stopped environment: DATA_DIR: /data diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md index 199516e..9e5135e 100644 --- a/docs/INTEGRATION.md +++ b/docs/INTEGRATION.md @@ -14,7 +14,7 @@ - AuthResponse: {access_token,user};refresh token 放 HttpOnly Cookie。 ## REST -GET config => {speech_enabled:false,registration_enabled:true,version:'0.4.0'} +GET config => {speech_enabled:false,registration_enabled:true,version:'0.4.1'} POST auth/register {username,password,display_name} => AuthResponse POST auth/login {username,password} => AuthResponse POST auth/refresh => AuthResponse;POST auth/logout => {ok:true} diff --git a/docs/releases/v0.4.1.md b/docs/releases/v0.4.1.md new file mode 100644 index 0000000..bf3dfbc --- /dev/null +++ b/docs/releases/v0.4.1.md @@ -0,0 +1,19 @@ +# 课堂叮咚 v0.4.1 + +大屏客户端界面更紧凑。只改大屏客户端的界面,服务端只更新了版本号。 + +## 大屏客户端 + +- **窗口更小**:默认窗口由 1180×760 缩小到 920×640,内容最大宽度 880。 +- **排布更紧凑**:页眉、标题、卡片间距和内边距都收紧了。班级信息改为整行横条,左侧是班级名,右侧是“消息接收”和“班级音量”,不再留出大块空白。 +- **卡片对齐**:「呼叫老师」与「声音测试」两张卡片并排,图标列和文字列对齐;设置项占满一行。窗口拉窄时自动变为单列。 + +## 升级 + +Windows amd64 安装包:`ClassDingScreen-amd64-installer.exe`,大屏升级到 v0.4.1 即可看到新界面。 + +服务端镜像 `ghcr.io/alumos/classding-server:0.4.1` 与 v0.4.0 功能相同,可以不升级;如果升级,只需替换镜像。 + +## 已知限制 + +- 主窗口大小、悬浮球层级和多显示器/缩放比例下的表现,需要在真实 Windows 设备上验收。 diff --git a/package.json b/package.json index f263fd1..f2fb463 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "classding", "private": true, - "version": "0.4.0", + "version": "0.4.1", "type": "module", "packageManager": "pnpm@11.25.0", "engines": { diff --git a/screen/README.md b/screen/README.md index 6fa2b2f..d10dc18 100644 --- a/screen/README.md +++ b/screen/README.md @@ -52,7 +52,7 @@ wails build -platform windows/amd64 -nsis -installscope user ## 学生呼叫老师 -绑定后 Windows 桌面左侧会显示橙色「呼叫老师」原生悬浮球(白色粗体大字)。它处在普通窗口层级,不会一直置顶:PPT 放映、最大化窗口会正常盖住它,回到桌面或其他窗口旁边时仍可见;主窗口关闭到托盘后仍可使用,不依赖网页中的浮层。拖动可移到其他显示器;松手停在最近显示器工作区内,避开任务栏,按显示器 DPI 调整大小。悬浮入口平时不抢焦点,只有点击展开二次确认时才激活;取消或 Escape 不发送,默认键盘焦点在取消按钮,Tab 可切换到确认按钮。全屏通知、课堂工具展示期间临时隐藏,结束后恢复,不移动或改变已有作业窗。 +绑定后 Windows 桌面左侧会显示橙色「呼叫老师」原生悬浮球(白色粗体大字)。主窗口默认 920×640,内容紧凑排布。它处在普通窗口层级,不会一直置顶:PPT 放映、最大化窗口会正常盖住它,回到桌面或其他窗口旁边时仍可见;主窗口关闭到托盘后仍可使用,不依赖网页中的浮层。拖动可移到其他显示器;松手停在最近显示器工作区内,避开任务栏,按显示器 DPI 调整大小。悬浮入口平时不抢焦点,只有点击展开二次确认时才激活;取消或 Escape 不发送,默认键盘焦点在取消按钮,Tab 可切换到确认按钮。全屏通知、课堂工具展示期间临时隐藏,结束后恢复,不移动或改变已有作业窗。 确认后由原有 WebView service 复用设备认证和 loopback 代理发送 `POST /api/v1/device/student-calls`,Go 原生层不保存另一份密钥。发送中禁止重复提交;成功须收到有效的服务器记录才显示「呼叫已发送」。本班所有关联老师在打开的教师网页收到提醒,任一老师标记已读后共享为已处理。服务端按同班 30 秒限流,客户端显示 `Retry-After` 等待时间。网络异常或响应丢失会显示结果未确认,用户主动重试沿用同一个幂等编号;不会离线排队或自动补发,退出程序后不保留未确认编号。 diff --git a/screen/frontend/package.json b/screen/frontend/package.json index 7af1e76..3ed2650 100644 --- a/screen/frontend/package.json +++ b/screen/frontend/package.json @@ -1,6 +1,6 @@ { "name": "@classding/screen", - "version": "0.4.0", + "version": "0.4.1", "private": true, "type": "module", "scripts": { diff --git a/screen/frontend/src/main.tsx b/screen/frontend/src/main.tsx index c8f0705..243dd2f 100644 --- a/screen/frontend/src/main.tsx +++ b/screen/frontend/src/main.tsx @@ -344,18 +344,19 @@ function App() {
-
- - - - - 已绑定 - + + + +
+
+ 当前班级 + + 已绑定 + +
+

{state.className}

+

{state.deviceName}

- 当前班级 -

{state.className}

-

{state.deviceName}

-
@@ -374,24 +375,24 @@ function App() {
- +

需要老师来教室?

-

向本班所有关联老师发送呼叫。关闭主窗口后,桌面上的橙色悬浮球仍可呼叫。

+

通知本班所有老师,也可点桌面橙色悬浮球。

{state.studentCallMessage && (

{state.studentCallMessage}

)} +
-
@@ -425,7 +426,7 @@ function App() { - +
diff --git a/screen/frontend/src/styles.css b/screen/frontend/src/styles.css index 94a260f..a238d54 100644 --- a/screen/frontend/src/styles.css +++ b/screen/frontend/src/styles.css @@ -25,7 +25,7 @@ body { /* One page width for header, content and footer, so their edges line up. */ :root { --cd-gutter: clamp(20px, 4vw, 56px); - --cd-page: min(1120px, calc(100vw - 2 * var(--cd-gutter))); + --cd-page: min(880px, calc(100vw - 2 * var(--cd-gutter))); --cd-page-inset: max(var(--cd-gutter), calc((100% - var(--cd-page)) / 2)); } .shell { @@ -35,7 +35,7 @@ body { background: #f7f9f4; } .topbar { - height: 88px; + height: 64px; display: flex; justify-content: space-between; align-items: center; @@ -44,12 +44,12 @@ body { background: #fbfcf9; } .topbar .cd-brand-mark { - width: 38px; - height: 38px; + width: 32px; + height: 32px; border-radius: 12px; } .topbar .cd-brand strong { - font-size: 19px; + font-size: 17px; } .topbar-right { display: flex; @@ -74,7 +74,7 @@ body { display: flex; justify-content: space-between; gap: 20px; - padding: 25px var(--cd-page-inset); + padding: 14px var(--cd-page-inset); color: #a2afa1; font-size: 11px; } @@ -85,10 +85,10 @@ body { width: var(--cd-page); margin: auto; display: grid; - grid-template-columns: minmax(0, 1fr) 490px; - gap: clamp(40px, 6vw, 110px); + grid-template-columns: minmax(0, 1fr) 400px; + gap: clamp(28px, 4vw, 56px); align-items: center; - padding: 60px 0; + padding: 28px 0; } .bind-intro { position: relative; @@ -114,9 +114,9 @@ body { } .bind-intro h1, .dashboard-heading h1 { - margin-top: 23px; + margin-top: 14px; color: #244738; - font-size: clamp(38px, 4.4vw, 62px); + font-size: clamp(30px, 3.4vw, 44px); line-height: 1.24; letter-spacing: -0.04em; font-weight: 700; @@ -127,16 +127,16 @@ body { } .bind-intro > p, .dashboard-heading p { - margin-top: 22px; + margin-top: 12px; max-width: 460px; - font-size: 16px; + font-size: 14px; line-height: 1.9; color: #87988a; } .intro-steps { display: grid; - gap: 20px; - margin-top: 49px; + gap: 12px; + margin-top: 26px; color: #6d8072; font-size: 15px; } @@ -175,7 +175,7 @@ body { position: relative; } .binding-card { - padding: 36px 42px 28px; + padding: 24px 28px 20px; border-color: #e7eee4; box-shadow: 0 22px 70px #416b4010; } @@ -189,7 +189,7 @@ body { color: #4d8c58; } .card-heading { - margin: 25px 0 27px; + margin: 16px 0 18px; } .card-overline, .section-label { @@ -223,7 +223,7 @@ body { cursor: text; } .code-boxes span { - height: 58px; + height: 50px; display: grid; place-items: center; border: 1px solid #e2eae0; @@ -251,7 +251,7 @@ body { } .advanced-toggle { width: 100%; - padding: 18px 0; + padding: 12px 0; border: 0; background: none; display: flex; @@ -309,7 +309,7 @@ body { justify-content: center; align-items: center; gap: 6px; - margin-top: 21px; + margin-top: 14px; color: #abb8a9; font-size: 11px; } @@ -318,36 +318,36 @@ body { align-items: center; justify-content: center; gap: 6px; - margin-top: 23px; + margin-top: 14px; color: #a2afa0; font-size: 12px; } .dashboard { width: var(--cd-page); - margin: auto; - padding: 56px 0; + margin: 0 auto; + padding: 22px 0; } .dashboard-heading { display: flex; justify-content: space-between; align-items: flex-end; - gap: 20px; - margin-bottom: 34px; + gap: 16px; + margin-bottom: 16px; } .dashboard-heading h1 { - font-size: 43px; - margin-top: 15px; + font-size: 28px; + margin-top: 8px; } .dashboard-heading h1 span { color: #82ac76; } .dashboard-heading p { - margin-top: 10px; + margin-top: 4px; } .dashboard-grid { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); - gap: 20px; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 14px; align-items: stretch; } .class-card, @@ -356,16 +356,13 @@ body { border-color: #e5ede1; box-shadow: 0 15px 50px #416b400b; } +/* A full-width strip: class on the left, live status on the right. */ .class-card { - padding: 30px; - min-height: 0; + grid-column: 1 / -1; display: flex; - flex-direction: column; -} -.class-card-top { - display: flex; - justify-content: space-between; - align-items: flex-start; + align-items: center; + gap: 16px; + padding: 16px 20px; } .class-icon, .setting-icon { @@ -378,34 +375,40 @@ body { .class-icon { width: 48px; height: 48px; + flex-shrink: 0; } -.class-card .section-label { - margin-top: 38px; +.class-info { + flex: 1; + min-width: 0; +} +.class-info-top { + display: flex; + align-items: center; + gap: 10px; } .class-card h2 { - font-size: 29px; - margin-top: 10px; + font-size: 26px; + margin-top: 2px; color: #2a4939; } -.class-card > p { - font-size: 14px; +.class-info > p { + font-size: 13px; color: #9ba99b; - margin-top: 7px; -} -.class-divider { - height: 1px; - background: #eaf0e7; - margin: 27px 0; + margin-top: 2px; } .class-summary { - display: flex; - gap: 45px; - margin-top: auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + flex: 0 0 300px; } .class-summary > div { display: flex; align-items: center; - gap: 10px; + gap: 12px; + padding: 14px 16px; + border-radius: 12px; + background: #f4f8f1; color: #83a084; } .class-summary span { @@ -418,18 +421,21 @@ body { color: #536c59; font-size: 15px; } +/* The side cards join the dashboard grid directly: call and sound side by + side, settings across the full width. */ .dashboard-side { - display: flex; - flex-direction: column; - gap: 20px; - min-width: 0; + display: contents; +} +.dashboard-grid .settings-card { + grid-column: 1 / -1; } /* The call card uses the same orange as the desktop floating ball. */ +/* Same structure as the sound card below: icon column, then text and button. */ .student-call-card { display: flex; align-items: center; - gap: 20px; - padding: 22px 26px; + gap: 16px; + padding: 16px 20px; border-color: #fed7aa; background: linear-gradient(135deg, #fff7ed, #ffffff 70%); box-shadow: 0 15px 50px #c2410c0d; @@ -439,8 +445,10 @@ body { min-width: 0; } .call-icon { - width: 56px; - height: 56px; + /* 66px tile centred in the same 118px column as the sound card's bell. */ + width: 48px; + height: 48px; + margin: 0 12px; flex-shrink: 0; display: grid; place-items: center; @@ -450,7 +458,7 @@ body { box-shadow: 0 8px 20px #f9731633; } .student-call-card h3 { - font-size: 18px; + font-size: 16px; color: #9a3412; } .student-call-card p { @@ -464,8 +472,8 @@ body { font-weight: 600; } .student-call-card .call-button { - flex-shrink: 0; - min-height: 46px; + margin-top: 10px; + min-height: 40px; padding: 0 22px; font-size: 16px; font-weight: 700; @@ -480,13 +488,13 @@ body { .ready-card { display: flex; align-items: center; - gap: 28px; - padding: 24px 28px; + gap: 16px; + padding: 16px 20px; } .ready-visual { position: relative; - width: 118px; - height: 118px; + width: 72px; + height: 72px; display: grid; place-items: center; flex-shrink: 0; @@ -494,11 +502,11 @@ body { .ready-bell { position: relative; z-index: 1; - width: 66px; - height: 66px; + width: 48px; + height: 48px; display: grid; place-items: center; - border-radius: 22px; + border-radius: 16px; background: #e7f2e2; color: #70a577; } @@ -508,13 +516,13 @@ body { border-radius: 50%; } .orbit-one { - inset: 10px; + inset: 6px; } .orbit-two { inset: 0; } .ready-card h3 { - font-size: 18px; + font-size: 16px; color: #3a5b43; } .ready-card p, @@ -525,25 +533,25 @@ body { margin-top: 6px; } .ready-card .cd-button { - margin-top: 13px; + margin-top: 10px; min-height: 40px; font-size: 14px; } .settings-card { - padding: 4px 24px; + padding: 2px 20px; } .setting-row { display: flex; align-items: center; - gap: 13px; - padding: 17px 0; + gap: 12px; + padding: 11px 0; } .setting-row + .setting-row { border-top: 1px solid #edf1ea; } .setting-icon { - width: 38px; - height: 38px; + width: 34px; + height: 34px; border-radius: 10px; } .setting-row > div { @@ -567,7 +575,7 @@ body { display: flex; justify-content: space-between; gap: 20px; - margin-top: 24px; + margin-top: 14px; color: #a4b1a2; font-size: 13px; } @@ -695,9 +703,12 @@ body { .intro-decoration { display: none; } - .student-call-card { + .class-card { flex-wrap: wrap; } + .class-summary { + flex-basis: 100%; + } .dashboard-grid { grid-template-columns: 1fr; } @@ -865,8 +876,8 @@ body { } .screen-homework-panel { - margin-top: 24px; - padding: 26px; + margin-top: 14px; + padding: 18px 20px; } .screen-homework-heading { display: flex; diff --git a/screen/main.go b/screen/main.go index 7c565b8..bbbe859 100644 --- a/screen/main.go +++ b/screen/main.go @@ -20,7 +20,7 @@ func main() { log.Fatal(err) } err = wails.Run(&options.App{ - Title: "课堂叮咚大屏端", Width: 1180, Height: 760, MinWidth: 420, MinHeight: 600, + Title: "课堂叮咚大屏端", Width: 920, Height: 640, MinWidth: 420, MinHeight: 520, StartHidden: app.hidden, Windows: &windows.Options{WindowClassName: "ClassDingMainWindow"}, BackgroundColour: options.NewRGB(247, 249, 244), diff --git a/screen/wails.json b/screen/wails.json index 04fd593..34315e1 100644 --- a/screen/wails.json +++ b/screen/wails.json @@ -11,7 +11,7 @@ "info": { "companyName": "ClassDing", "productName": "课堂叮咚大屏端", - "productVersion": "0.4.0", + "productVersion": "0.4.1", "copyright": "ClassDing" } } diff --git a/scripts/api-generator/package.json b/scripts/api-generator/package.json index 207d1ae..a7c8fcd 100644 --- a/scripts/api-generator/package.json +++ b/scripts/api-generator/package.json @@ -1,6 +1,6 @@ { "name": "@classding/api-generator", - "version": "0.4.0", + "version": "0.4.1", "private": true, "type": "module", "scripts": { diff --git a/server/internal/app/models.go b/server/internal/app/models.go index 6277ac8..067700b 100644 --- a/server/internal/app/models.go +++ b/server/internal/app/models.go @@ -3,7 +3,7 @@ package app import "time" // Version is the release reported by the server and offered to classroom clients. -const Version = "0.4.0" +const Version = "0.4.1" // Role decides which workspace an account may sign in to and what it governs. type Role string diff --git a/server/internal/app/routes.go b/server/internal/app/routes.go index 735c6a8..b15d1b6 100644 --- a/server/internal/app/routes.go +++ b/server/internal/app/routes.go @@ -137,7 +137,7 @@ func clientRelease(_ http.ResponseWriter, _ *http.Request, _ *User) (any, error) "version": Version, "url": releaseBase + "/ClassDingScreen-amd64-installer.exe", "sha256": "", - "notes": "v0.4.0:桌面呼叫悬浮球不再始终置顶、字号更大、颜色更醒目;主界面重新对齐;断网后更快恢复连接。SHA-256 校验文件见对应 GitHub Release。", + "notes": "v0.4.1:大屏客户端界面更紧凑,默认窗口缩小到 920×640;含 v0.4.0 的悬浮球不置顶与网络稳定性改进。SHA-256 校验文件见对应 GitHub Release。", "min_supported": "0.1.0", }, nil } diff --git a/web/apps/admin/package.json b/web/apps/admin/package.json index 36d6823..4b35dcf 100644 --- a/web/apps/admin/package.json +++ b/web/apps/admin/package.json @@ -1,6 +1,6 @@ { "name": "@classding/admin", - "version": "0.4.0", + "version": "0.4.1", "private": true, "type": "module", "scripts": { diff --git a/web/apps/teacher/package.json b/web/apps/teacher/package.json index 9ec789f..c02b4c0 100644 --- a/web/apps/teacher/package.json +++ b/web/apps/teacher/package.json @@ -1,6 +1,6 @@ { "name": "@classding/teacher", - "version": "0.4.0", + "version": "0.4.1", "private": true, "type": "module", "scripts": { diff --git a/web/packages/api/package.json b/web/packages/api/package.json index f30f8dd..11ac9e2 100644 --- a/web/packages/api/package.json +++ b/web/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@classding/api", - "version": "0.4.0", + "version": "0.4.1", "private": true, "type": "module", "exports": { diff --git a/web/packages/ui/package.json b/web/packages/ui/package.json index b2680af..3bed8cc 100644 --- a/web/packages/ui/package.json +++ b/web/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@classding/ui", - "version": "0.4.0", + "version": "0.4.1", "private": true, "type": "module", "exports": {