/// 「学习」首页需要的一份排程摘要。 /// /// 目前由 [StudyPlan.sample] 提供示例数据;接上新内核后,这里应改为由排程 /// 返回的真实数据(到期条目数、新识记额度、历史平均速度等),UI 不需要改。 class StudyPlan { const StudyPlan({ required this.deckName, required this.algorithm, required this.totalItems, required this.activatedItems, required this.dueItems, required this.newItems, required this.secondsPerItem, }); /// 当前单元集名称。 final String deckName; /// 该单元集使用的调度算法标识,如 `NSP-0`、`SM-2`。 final String algorithm; /// 单元集条目总数。 final int totalItems; /// 已激活(学过至少一次)的条目数。 final int activatedItems; /// 今天到期、需要复习的条目数。 final int dueItems; /// 今天还可以引入的新识记条目数。 final int newItems; /// 单个条目完成一轮多谜题测验的平均耗时(秒)。 /// /// 用于把「今天要学多少」翻译成用户能感知的时间承诺。实测速度应当来自 /// 内核,这里先用经验值。 final int secondsPerItem; /// 今天总共要处理的条目数。 int get itemCount => dueItems + newItems; /// 今天是否无事可做。 bool get isEmpty => itemCount == 0; /// 单元集整体完成度,0..1。 double get progress => totalItems == 0 ? 0 : activatedItems / totalItems; /// 完成度百分比,保留一位小数。 String get progressPercent => '${(progress * 100).toStringAsFixed(1)}%'; /// 预计用时(分钟,向上取整)。条目数为 0 时返回 0。 int get estimatedMinutes => _minutes(itemCount, secondsPerItem); /// 预计用时的展示文案。 String get estimatedLabel => isEmpty ? '今天没有到期的内容' : '预计用时 $estimatedMinutes 分钟'; /// 示例数据:高考必背古诗文,NSP-0 调度。 static const StudyPlan sample = StudyPlan( deckName: '高考必背古诗文', algorithm: 'NSP-0', totalItems: 950, activatedItems: 295, dueItems: 18, newItems: 35, secondsPerItem: 13, ); static int _minutes(int itemCount, int secondsPerItem) { if (itemCount <= 0 || secondsPerItem <= 0) { return 0; } final int totalSeconds = itemCount * secondsPerItem; return (totalSeconds + 59) ~/ 60; } }