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
@@ -6,6 +6,9 @@ import {
|
||||
DEFAULT_CLASSISLAND_NOTIFICATION_CHANNEL,
|
||||
DEFAULT_ISLAND_APPEARANCE,
|
||||
DEFAULT_SERVER_URL,
|
||||
HOMEWORK_PHRASE_DEFAULTS,
|
||||
HOMEWORK_PHRASE_MAX_COUNT,
|
||||
HOMEWORK_PHRASE_MAX_LENGTH,
|
||||
type ClassIslandNotificationChannel,
|
||||
type IslandAppearance,
|
||||
} from '@classhelper/shared';
|
||||
@@ -16,7 +19,6 @@ const DEFAULT_HOMEWORK_BOARD: HomeworkBoardSettings = {
|
||||
mode: 'board',
|
||||
showTime: false,
|
||||
fontSize: 15,
|
||||
todayOnly: true,
|
||||
};
|
||||
|
||||
interface PersistedConfig {
|
||||
@@ -31,6 +33,8 @@ interface PersistedConfig {
|
||||
homeworkBoard?: Partial<HomeworkBoardSettings>;
|
||||
/** 通知显示位置(向后兼容:旧配置文件没有该字段时按默认 both 补齐) */
|
||||
notificationChannel?: ClassIslandNotificationChannel;
|
||||
/** 作业录入快捷短语(向后兼容:旧配置没有该字段时用默认那一组) */
|
||||
homeworkPhrases?: string[];
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: PersistedConfig = {
|
||||
@@ -41,8 +45,28 @@ const DEFAULT_CONFIG: PersistedConfig = {
|
||||
island: { ...DEFAULT_ISLAND_APPEARANCE },
|
||||
homeworkBoard: { ...DEFAULT_HOMEWORK_BOARD },
|
||||
notificationChannel: DEFAULT_CLASSISLAND_NOTIFICATION_CHANNEL,
|
||||
homeworkPhrases: [...HOMEWORK_PHRASE_DEFAULTS],
|
||||
};
|
||||
|
||||
/**
|
||||
* 作业快捷短语的合法化:去空白、去重、限长限量。
|
||||
* 空数组是**合法值**(老师把短语全删了 = 不想用快捷短语),因此不能拿默认值顶回去。
|
||||
*/
|
||||
function normalizeHomeworkPhrases(input?: string[]): string[] {
|
||||
if (!Array.isArray(input)) return [...HOMEWORK_PHRASE_DEFAULTS];
|
||||
const seen = new Set<string>();
|
||||
const result: string[] = [];
|
||||
for (const raw of input) {
|
||||
if (typeof raw !== 'string') continue;
|
||||
const value = raw.trim().slice(0, HOMEWORK_PHRASE_MAX_LENGTH);
|
||||
if (!value || seen.has(value)) continue;
|
||||
seen.add(value);
|
||||
result.push(value);
|
||||
if (result.length >= HOMEWORK_PHRASE_MAX_COUNT) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 通知显示位置的白名单校验:非法值一律回落到默认值,避免坏配置把提醒"静默吞掉" */
|
||||
function normalizeNotificationChannel(
|
||||
input?: ClassIslandNotificationChannel,
|
||||
@@ -62,7 +86,6 @@ function normalizeHomeworkBoard(input?: Partial<HomeworkBoardSettings>): Homewor
|
||||
return {
|
||||
mode,
|
||||
showTime: input?.showTime === true,
|
||||
todayOnly: input?.todayOnly !== false,
|
||||
fontSize: Math.min(28, Math.max(11, Math.round(rawFont))),
|
||||
};
|
||||
}
|
||||
@@ -102,6 +125,9 @@ function normalizeIslandAppearance(input?: Partial<IslandAppearance>): IslandApp
|
||||
? (input?.style as IslandAppearance['style'])
|
||||
: DEFAULT_ISLAND_APPEARANCE.style,
|
||||
idleSliver: input?.idleSliver === true,
|
||||
marginX: clamp(input?.marginX, 0, 200, DEFAULT_ISLAND_APPEARANCE.marginX),
|
||||
marginY: clamp(input?.marginY, 0, 160, DEFAULT_ISLAND_APPEARANCE.marginY),
|
||||
followCursorDisplay: input?.followCursorDisplay === true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -146,6 +172,7 @@ function readPersisted(): PersistedConfig {
|
||||
island: normalizeIslandAppearance(parsed.island),
|
||||
homeworkBoard: normalizeHomeworkBoard(parsed.homeworkBoard),
|
||||
notificationChannel: normalizeNotificationChannel(parsed.notificationChannel),
|
||||
homeworkPhrases: normalizeHomeworkPhrases(parsed.homeworkPhrases),
|
||||
};
|
||||
} catch {
|
||||
return { ...DEFAULT_CONFIG };
|
||||
@@ -168,6 +195,7 @@ export function getConfig(): DesktopStoredConfig {
|
||||
island: normalizeIslandAppearance(persisted.island),
|
||||
homeworkBoard: normalizeHomeworkBoard(persisted.homeworkBoard),
|
||||
notificationChannel: normalizeNotificationChannel(persisted.notificationChannel),
|
||||
homeworkPhrases: normalizeHomeworkPhrases(persisted.homeworkPhrases),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -190,6 +218,9 @@ export function saveConfig(patch: Partial<DesktopStoredConfig>): DesktopStoredCo
|
||||
...patch.homeworkBoard,
|
||||
});
|
||||
}
|
||||
if (patch.homeworkPhrases !== undefined) {
|
||||
persisted.homeworkPhrases = normalizeHomeworkPhrases(patch.homeworkPhrases);
|
||||
}
|
||||
if (patch.notificationChannel !== undefined) {
|
||||
persisted.notificationChannel = normalizeNotificationChannel(patch.notificationChannel);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user