feat: 重做灵动岛编辑器与课表档案时间表编辑

灵动岛
- 重构自定义方案编辑器:组件库常驻、拖拽添加与排序、方案切换/新建/复制/删除、编辑模式预览与缩放,容器在编辑时展开全部子组件。
- 新增天气简报组件:主进程固定请求 Open-Meteo,只接受数值经纬度,失败退避并保留本次运行缓存;冒烟模式不访问外网。
- 倒计时支持目标名称与日期;课程表组件新增「今日时间点」,按时间点类型折叠/展开,课中不再常驻倒计时。
- preload 暴露 getIslandWeather 与时间点订阅,契约同步 desktop.d.ts。

课表档案
- 时间点默认课程:布局返回 defaultCourse,日课表没有条目时回退;新增 apply-default 覆盖所有引用课表;全校时间表不携带默认课程。
- 新增 ClassIsland 式时间表编辑器(列表/时间轴视图、拖动把柄、按类型添加、默认时长与缩放)与「课表信息」表单。
- 课表页默认进入单表编辑,底部保留列表/周/月视图;时间表支持新路由删除。
- 「课间」统一改称「课间休息」。

验证
- pnpm typecheck、pnpm lint 通过。
- verify:e2e 隔离验收 248/248 项通过(含默认课程回退、覆盖写入与时间表删除)。
- ui-smoke 覆盖单表编辑、课表信息与时间轴视图,并更新回归截图。
This commit is contained in:
LoveChengke committed 2026-10-25 16:41:29 +08:00
1 parent 938285bb9c
commit 682818dd4a
41 files changed
+4349 -1095

No files matched your search

@@ -37,14 +37,63 @@ const containers = ['group', 'stack', 'slide', 'rolling'] as const;
<el-switch v-model="node.settings.seconds" @change="emit('change')" />
</el-form-item>
</template>
<el-form-item v-else-if="node.kind === 'countdown'" label="倒计时目标">
<el-select v-model="node.settings.target" @change="emit('change')">
<el-option label="自动:距上课 / 下课" value="auto" />
<el-option label="下一节课" value="next" />
<el-option label="本节下课" value="end" />
<el-option label="放学" value="school" />
</el-select>
</el-form-item>
<template v-else-if="node.kind === 'countdown'">
<el-form-item label="倒计时目标">
<el-select v-model="node.settings.target" @change="emit('change')">
<el-option label="自动:距上课 / 下课" value="auto" />
<el-option label="下一节课" value="next" />
<el-option label="本节下课" value="end" />
<el-option label="放学" value="school" />
<el-option label="指定日期" value="date" />
</el-select>
</el-form-item>
<template v-if="node.settings.target === 'date'">
<el-form-item label="目标名称">
<el-input v-model="node.settings.countdownTitle" :maxlength="64" @change="emit('change')" />
</el-form-item>
<el-form-item label="目标日期">
<el-date-picker
v-model="node.settings.targetDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
@change="emit('change')"
/>
</el-form-item>
</template>
</template>
<template v-else-if="node.kind === 'weather'">
<el-form-item label="地点名称">
<el-input
v-model="node.settings.locationName"
placeholder="例如:学校"
:maxlength="64"
@change="emit('change')"
/>
</el-form-item>
<el-form-item label="纬度">
<el-input-number
v-model="node.settings.latitude"
:min="-90"
:max="90"
:precision="4"
@change="emit('change')"
/>
</el-form-item>
<el-form-item label="经度">
<el-input-number
v-model="node.settings.longitude"
:min="-180"
:max="180"
:precision="4"
@change="emit('change')"
/>
</el-form-item>
<p class="hint">
填写地点坐标后每 15 分钟更新天气。数据来自
Open-Meteo,断网时显示本次运行的缓存;天气提示不作为官方预警。
</p>
</template>
<template v-else-if="node.kind === 'text'">
<el-form-item label="文字内容">
<el-input v-model="node.settings.text" type="textarea" :rows="3" @change="emit('change')" />
@@ -57,20 +106,24 @@ const containers = ['group', 'stack', 'slide', 'rolling'] as const;
<template v-else-if="node.kind === 'schedule'">
<el-form-item label="显示课程">
<el-select v-model="node.settings.display" @change="emit('change')">
<el-option label="当前课程" value="current" />
<el-option label="当前时间点" value="current" />
<el-option label="下一节课" value="next" />
<el-option label="今日课表" value="today" />
<el-option label="今日时间点" value="today" />
</el-select>
</el-form-item>
<el-form-item v-if="node.settings.display === 'today'" label="显示节数">
<el-form-item v-if="node.settings.display === 'today'" label="显示时间点数">
<el-input-number v-model="node.settings.count" :min="1" :max="30" @change="emit('change')" />
</el-form-item>
<el-form-item label="显示课程时间">
<el-form-item label="当前时间点显示起止时间">
<el-switch v-model="node.settings.showPeriod" @change="emit('change')" />
</el-form-item>
<el-form-item label="显示上课进度">
<el-form-item label="当前课程显示进度">
<el-switch v-model="node.settings.showProgress" @change="emit('change')" />
</el-form-item>
<p class="hint">
今日时间点只展开当前项并显示时间段;课间休息与默认隐藏的时间点折叠时完全隐藏,轮到时才展开。
下课前一分钟的倒计时由「提醒」设置里的下课提醒负责。
</p>
</template>
<template v-else-if="containers.includes(node.kind as (typeof containers)[number])">
<el-form-item label="排列方向">
@@ -80,7 +133,7 @@ const containers = ['group', 'stack', 'slide', 'rolling'] as const;
</el-select>
</el-form-item>
<el-form-item label="组件间距">
<el-input-number v-model="node.settings.gap" :min="0" :max="80" @change="emit('change')" />
<el-input-number v-model="node.settings.gap" :min="0" :max="64" @change="emit('change')" />
</el-form-item>
<el-form-item v-if="node.kind === 'slide'" label="切换间隔(秒)">
<el-input-number