新增了列表显示模式,用户可以在设置中进行选择。

Signed-off-by: Yang Yongquan <i@yangyq.net>
This commit is contained in:
2026-09-16 10:07:57 +08:00
parent 5ab9648b5a
commit 2173b06b43
6 changed files with 668 additions and 97 deletions
+31
View File
@@ -17,6 +17,7 @@ export class AppSettings {
private static readonly KEY_REMINDER_TICK: string = 'reminder_tick'; // 应用内提醒上次检查时间戳
private static readonly KEY_FULL_REFETCH: string = 'full_refetch_done'; // 一次性全量重拉已完成
private static readonly KEY_DEFAULT_VIEW: string = 'default_view'; // 打开 App 默认视图
private static readonly KEY_DISPLAY_STYLE: string = 'display_style'; // 日程显示方式:'timeline' | 'list'
private static readonly KEY_POLICY_AGREED: string = 'policy_agreed'; // 是否已同意隐私政策与用户协议
// 首启功能引导"用户已看过并关闭"的标记。键名带版本号:改动引导内容后把 vN 加 1,用户即可再看一次。
// 注意:只在用户主动关闭("开始使用" / ✕ / 点遮罩)时才置 true**不再"显示前就置 true"**——
@@ -118,6 +119,36 @@ export class AppSettings {
}
}
/**
* 日程显示方式:'timeline'(竖向时间轴,默认) | 'list'(列表)。
*
* 两种风格各有偏好:时间轴强于"看一天的时间占用与冲突",列表强于"快速扫读与长标题"。
* 该设置同时作用于主界面(月/周视图下方、列表视图)与桌面服务卡片(4×4 / 6×4)。
* 列表模式下不绘制"当前时间红线"(列表没有时间刻度,红线没有落点)。
*/
static async getDisplayStyle(context: common.Context): Promise<string> {
try {
const store: preferences.Preferences =
await preferences.getPreferences(context, AppSettings.STORE);
const v: string = await store.get(AppSettings.KEY_DISPLAY_STYLE, 'timeline') as string;
return v === 'list' ? 'list' : 'timeline';
} catch (err) {
return 'timeline';
}
}
static async setDisplayStyle(context: common.Context, style: string): Promise<void> {
try {
const store: preferences.Preferences =
await preferences.getPreferences(context, AppSettings.STORE);
await store.put(AppSettings.KEY_DISPLAY_STYLE, style === 'list' ? 'list' : 'timeline');
await store.flush();
} catch (err) {
const e = err as BusinessError;
console.error(`保存日程显示方式失败: ${e.message}`);
}
}
/**
* 是否还需要一次性全量重拉:修复历史同步(REPORT 剥离 VALARM 时期)落库的残缺数据。
* 全量重拉期间忽略 etag 复用,所有资源 GET 完整 ICS;成功完成后标记,恢复增量模式。
+32 -2
View File
@@ -5,9 +5,10 @@ import { formBindingData, formProvider } from '@kit.FormKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { preferences } from '@kit.ArkData';
import { CalendarDataService, DisplayEvent } from './CalendarDataService';
import { AppSettings } from './AppSettings';
import { LunarUtil } from './LunarUtil';
import { LogUtil } from './LogUtil';
import { TimelineUtil, DayTimeline } from './TimelineUtil';
import { TimelineUtil, DayTimeline, TimelineBlock } from './TimelineUtil';
/** 卡片单条日程(按天分组:组内全天事件在前、有时间的按开始时间排序) */
export class CardItem {
@@ -15,6 +16,7 @@ export class CardItem {
time: string = ''; // 开始时间 '08:30' / '全天'
endTime: string = ''; // 结束时间 '10:00'(全天事件为空)
date: string = ''; // '9月15日'
calName: string = ''; // 所属日历本名称(列表模式卡片右侧展示,颜色同日历本)
showDate: boolean = false; // 是否为当天分组的第一条(卡片上渲染日期头)
color: string = '#007DFF';
// 当前时间红线:start/end 为日程实际起止毫秒(用于定位"现在"位置);
@@ -51,6 +53,10 @@ export class CardData {
nowRatio: number = 0; // 当前时间在当天时间轴上的比例(0~1),卡片画红线用
nowLabel: string = ''; // 当前时间文字(如 '14:05'
allDayJson: string = '[]'; // 全天/跨天日程(CardAllDay[] JSON,卡片画在最上方)
// 列表模式:当前显示日的"行"式日程(CardItem[],按全天在前、时间升序;无红线)
listJson: string = '[]';
// 日程显示方式:'timeline' | 'list'(与 App 内设置联动;列表模式卡片不画红线)
displayStyle: string = 'timeline';
// ===== 卡片翻页(上一天 / 下一天 / 回到今天)=====
dayOffset: number = 0; // 相对今天的天数偏移(0=今天)
isToday: boolean = true; // 当前显示的是否为今天(非今天不画红线)
@@ -343,7 +349,31 @@ export class CardDataService {
data.nowRatio = data.isToday ? TimelineUtil.nowRatio(nowMs) : -1;
data.nowLabel = TimelineUtil.nowLabel(nowMs);
data.dayCount = tl.blocks.length + tl.allDay.length;
LogUtil.write(`卡片数据刷新:${items.length} 条,今日 ${data.todayCount} 条,进行中 ${ongoingItems.length} 条,时间轴 ${tl.blocks.length} 块`);
// 列表模式数据:当前显示日的"行"式日程(全天在前、有时间按开始时间升序)。
// 进行中(仅今天的有时间日程)用 isNow 标记 → 卡片上以红条 + "进行中"呈现,**不画横线红线**。
const dayList: CardItem[] = [];
const dayAllDay: DisplayEvent[] = tl.allDay;
const dayTimed: DisplayEvent[] = tl.blocks
.filter((b: TimelineBlock): boolean => b.ev !== null)
.map((b: TimelineBlock): DisplayEvent => b.ev as DisplayEvent)
.sort((a: DisplayEvent, b: DisplayEvent): number => a.startTime - b.startTime);
for (const e of dayAllDay.concat(dayTimed)) {
const li = new CardItem();
li.title = e.title === '' ? '(无标题)' : e.title;
const isDayLong: boolean = e.isAllDay || spansDays(e);
const ds: Date = new Date(e.startTime);
const de: Date = new Date(e.endTime);
li.time = isDayLong ? '全天' : `${p(ds.getHours())}:${p(ds.getMinutes())}`;
li.endTime = isDayLong ? '' : `${p(de.getHours())}:${p(de.getMinutes())}`;
li.color = e.color;
li.calName = e.calName;
li.isNow = data.isToday && !isDayLong && e.startTime <= nowMs && nowMs < e.endTime;
dayList.push(li);
}
data.listJson = JSON.stringify(dayList);
// 显示方式:与 App 内设置联动(列表模式卡片不画当前时间红线)
data.displayStyle = await AppSettings.getDisplayStyle(context);
LogUtil.write(`卡片数据刷新:${items.length} 条,今日 ${data.todayCount} 条,进行中 ${ongoingItems.length} 条,时间轴 ${tl.blocks.length} 块,显示方式 ${data.displayStyle}`);
} catch (err) {
LogUtil.write(`卡片数据刷新失败: ${JSON.stringify(err)}`);
}