feat(homeworks,island,classisland,desktop,web,server): 作业按天查看与教室机器录入 + 提醒上课时段暂存规则 + 灵动岛边距/跟随鼠标屏幕
- 作业:新增 Homework.assignDate(本地日期 YYYY-MM-DD;UTC 切片在晚上会把今天的作业算成明天),列表按天过滤与排序,新增 GET /homeworks?date= 与 GET /homeworks/days(日期高亮)供客户端与 Web 共用;客户端作业页改为"看作业 + 录作业"(不再显示完成状态),支持快捷短语(存本机配置,「设置 → 作业录入」可增删/恢复默认) - 教室机器录入:POST /homeworks 去掉 requireRole,权限下沉到 homeworks.service(staff 或本班班级账号);班级账号的 sub 是班级 id,写 createdBy 前换成该班班主任,避免撞 User 外键 - 提醒类型:ClassIslandPush.kind(notification / call)。通知类上课时段暂存、下课补弹,叫人/紧急立即弹;客户端灵动岛与 ClassIsland 插件共用同一套规则 - 灵动岛:新增 marginX / marginY(默认仍 8px,不动老外观)与「跟随鼠标屏幕」;文案统一成"灵动岛 / ClassIsland" - 设置页:新增「ClassIsland 联动」(联动状态 + 通知模式)与「作业录入」分区 - 校验:verify:e2e 增补 assignDate 默认当天 / ?date= 过滤 / days / 非法日期 422 / 班级账号录入 201 与跨班 403;verify:classisland 与插件静态契约增补上课时段暂存断言(57/57) - 文档:README 与 AGENTS.md 同步(作业按天、上课时段策略、两个方向的开关表、新增踩坑) 门禁:pnpm lint 0 error、pnpm typecheck 四包通过、verify:classisland-plugin 57/57 通过。 未跑:verify:e2e / verify:classisland(需后端启动)、verify:web / verify:desktop(需先构建产物)。
This commit is contained in:
1 parent
d0830f6830
commit
7536abfc95
33 files changed
+1236
-162
No files matched your search
@@ -758,21 +758,30 @@ class IslandController {
|
||||
* - 全部取整,避免 `Math.round` 在 0.5 边界上左右跳变(那会表现为 ±0.5px 的横向抖动)。
|
||||
*/
|
||||
private resolveAnchor(): IslandAnchor {
|
||||
const area = screen.getPrimaryDisplay().workArea;
|
||||
const margin = ISLAND_MARGIN;
|
||||
// 多显示器教室电脑:打开"跟随鼠标屏幕"后,岛出现在鼠标所在的那块屏(讲台切换投影时不用改设置)
|
||||
const display = this.appearance.followCursorDisplay
|
||||
? screen.getDisplayNearestPoint(screen.getCursorScreenPoint())
|
||||
: screen.getPrimaryDisplay();
|
||||
const area = display.workArea;
|
||||
const marginX = Math.round(this.appearance.marginX ?? ISLAND_MARGIN);
|
||||
const marginY = Math.round(this.appearance.marginY ?? ISLAND_MARGIN);
|
||||
const center = Math.round(area.x + area.width / 2);
|
||||
|
||||
// 6 个停靠位置(顶/底 × 左/中/右),与 WinIsland 的 DockPosition 一一对应
|
||||
const position = this.appearance.position;
|
||||
const vMode: IslandAnchor['vMode'] = position.startsWith('bottom') ? 'bottom' : 'top';
|
||||
const vValue = vMode === 'bottom' ? area.y + area.height - margin : area.y + margin;
|
||||
const vValue = vMode === 'bottom' ? area.y + area.height - marginY : area.y + marginY;
|
||||
const hMode: IslandAnchor['hMode'] = position.endsWith('left')
|
||||
? 'left'
|
||||
: position.endsWith('right')
|
||||
? 'right'
|
||||
: 'center';
|
||||
const hValue =
|
||||
hMode === 'left' ? area.x + margin : hMode === 'right' ? area.x + area.width - margin : center;
|
||||
hMode === 'left'
|
||||
? area.x + marginX
|
||||
: hMode === 'right'
|
||||
? area.x + area.width - marginX
|
||||
: center;
|
||||
return { hMode, hValue, vMode, vValue };
|
||||
}
|
||||
|
||||
@@ -1336,6 +1345,10 @@ export function normalizeAppearance(input: IslandAppearance): IslandAppearance {
|
||||
alwaysOnTop: input.alwaysOnTop !== false,
|
||||
style: ISLAND_STYLES.includes(input.style) ? input.style : 'black',
|
||||
idleSliver: input.idleSliver === true,
|
||||
// 边距:0 表示贴着屏幕边缘,上限给到 200/160 是为了"多显示器 + 任务栏在侧面"这类布局
|
||||
marginX: clamp(input.marginX ?? DEFAULT_ISLAND_APPEARANCE.marginX, 0, 200),
|
||||
marginY: clamp(input.marginY ?? DEFAULT_ISLAND_APPEARANCE.marginY, 0, 160),
|
||||
followCursorDisplay: input.followCursorDisplay === true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user