重复日程支持单次/后续/全部范围;时间轴刻度改为 8-18 固定

重复日程编辑与删除:
- 编辑或删除重复日程前先让用户选范围:仅这一次 / 此次及以后 / 全部
- 仅这一次:主事件加 EXDATE 排除该次,另插一条带 RECURRENCE-ID 的覆盖行
- 此次及以后:主事件 RRULE 加 UNTIL 截断,剩余部分拆成新 uid 的新系列
- 全部:删除/改写整个系列
- IcsUtil 新增 vevent / buildSeries / rruleWithUntil / fmtIcsTime:
  同一个 .ics 资源内写主事件 + 全部覆盖实例(标准写法),
  此前对覆盖实例直接跳过推送、导致单次改动无法上传服务器
- SyncEngine.pushSeries:按 uid 取全系列行重建整份 .ics 后一次性 PUT
- EventDb 新增 queryByUid / purgeByUid / purgeByUidFrom / reassignOverridesFrom /
  setExdate / setRruleForUid

时间轴刻度(App + 4x4/6x6 卡片同口径):
- 8:00~18:00 恒定显示;仅当 8 点前 / 18 点后有日程时才向该侧扩展,
  其余时刻不再出现'只有一条日程就只剩一小段刻度'的观感
This commit is contained in:
2026-10-09 21:40:53 +08:00
parent d5e5d54d35
commit 5c80c0b773
7 changed files with 677 additions and 85 deletions
+11 -19
View File
@@ -109,40 +109,32 @@ struct Widget6x4Card {
return r >= 0 && Math.floor(r * 24) === h;
}
/** 视窗范围:最早日程 ~ 最晚日程结束(今天再并入当前时刻)。
* 不做"最大跨度截断" —— 现在可以滑动了,没必要砍掉日程。 */
/** 视窗范围:**8:00~18:00 恒显示**;仅当 8 点前 / 18 点后有日程时才向该侧扩展。
* (与 App 内时间轴 TimelineUtil.viewRange 同一口径,避免两处漂移。) */
private w6Range(): W6Range {
const rg = new W6Range();
const bs: TBlock6[] = this.parseBlocks();
let s: number = -1;
let e: number = -1;
let s: number = 8;
let e: number = 18;
for (const b of bs) {
const bh: number = Math.floor(b.topRatio * 24);
const eh: number = Math.ceil((b.topRatio + b.heightRatio) * 24 - 0.0001);
if (s < 0 || bh < s) {
if (bh < s) {
s = bh;
}
if (e < 0 || eh > e) {
if (eh > e) {
e = eh;
}
}
const r: number = this.w6NowR();
// 只有"今天 + 还有未结束的定时日程"时才把当前小时并入视窗(红线会显示);
// 定时日程已全部结束 → 不并入 → 时间刻度直接截断到最晚日程
// 且仅在该侧本来就有日程时才允许继续向外扩(不因当前时刻把 8 点前/18 点后空档重新显示)
const nh: number = (r >= 0 && !this.w6AllTimedEnded()) ? Math.floor(r * 24) : -1;
if (s < 0) {
s = nh >= 0 ? nh : 8;
if (nh >= 0 && nh < s && s < 8) {
s = nh;
}
if (e < 0) {
e = nh >= 0 ? nh + 1 : 20;
}
if (nh >= 0) {
if (nh < s) {
s = nh;
}
if (nh + 1 > e) {
e = nh + 1;
}
if (nh >= 0 && nh + 1 > e && e > 18) {
e = nh + 1;
}
if (s < 0) {
s = 0;