You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中实现类Chrome按天分组排序的历史记录功能

Angular+NgRx实现Chrome风格按天分组历史记录

实现逻辑核心是将平级的历史记录数组按本地日期维度做聚合,先按时间倒序排列所有记录,再按所属日期拆分分组,最终按「日期分组头+组内记录」的结构渲染即可,具体实现步骤如下:

1 定义分组数据结构

首先补充分组对应的TS类型,约束分组数据格式:

interface HistoryGroup {
  dateLabel: string; // 分组显示文案,如今天、昨天、2024/5/20
  dateTimestamp: number; // 分组日期0点时间戳,用于排序
  items: History[]; // 该日期下的所有历史记录
}

2 实现分组排序工具方法

方法会自动处理时区偏差、日期文案生成、排序逻辑,直接复制到组件或公共工具类中即可:

/**
 * 历史记录按天分组排序
 * @param histories 原始平级历史记录数组
 */
groupHistoriesByDay(histories: History[]): HistoryGroup[] {
  if (!histories?.length) return [];
  const groupMap = new Map<string, HistoryGroup>();

  // 所有记录先按访问时间倒序:最新访问的排在最前
  const sortedHistories = [...histories].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());

  // 计算今天、昨天的0点时间戳,用于特殊日期文案判断
  const now = new Date();
  const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
  const yesterdayStart = todayStart - 24 * 60 * 60 * 1000;

  sortedHistories.forEach(item => {
    const itemDate = new Date(item.date);
    // 取记录对应本地日期的0点时间戳作为分组键,避免UTC时区差导致的日期分组错误
    const itemDayStart = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()).getTime();

    // 生成分组显示文案
    let dateLabel: string;
    if (itemDayStart === todayStart) {
      dateLabel = '今天';
    } else if (itemDayStart === yesterdayStart) {
      dateLabel = '昨天';
    } else {
      dateLabel = itemDate.toLocaleDateString();
    }

    // 分组不存在则初始化
    if (!groupMap.has(String(itemDayStart))) {
      groupMap.set(String(itemDayStart), {
        dateLabel,
        dateTimestamp: itemDayStart,
        items: []
      });
    }
    // 记录归入对应分组
    groupMap.get(String(itemDayStart))!.items.push(item);
  });

  // 分组按日期从新到旧排序后返回
  return Array.from(groupMap.values()).sort((a, b) => b.dateTimestamp - a.dateTimestamp);
}

3 组件内对接数据流

在原有组件逻辑基础上,新增经过分组处理的可观察流,利用RxJS的map操作符自动响应历史数据变更,无需手动订阅:

// 分组后的历史记录流
historyGroups$: Observable<HistoryGroup[]> = this.Histories$.pipe(
  map(histories => this.groupHistoriesByDay(histories))
);

注意:你原有onAddHistory方法存在变量名笔误,传入store的histo变量未定义,需要替换为方法内声明的history实例,否则新增记录逻辑会报错。

如果项目中多个地方需要用到分组后的历史数据,可以把分组逻辑封装到NgRx的selector中,符合全局状态管理的复用规范。

4 修改模板渲染分组结构

替换原有直接循环平级记录的模板逻辑,改为两层循环:先遍历日期分组渲染日期头,再遍历组内记录渲染条目,结构和Chrome历史页完全一致:

<!-- 空状态提示 -->
<div mat-subheader *ngIf="(historyGroups$ | async)?.length === 0">The documents you visit will show here</div>

<!-- 遍历所有日期分组 -->
<ng-container *ngFor="let group of historyGroups$ | async">
  <!-- 日期分组标题 -->
  <div mat-subheader class="history-date-header">{{ group.dateLabel }}</div>
  <!-- 遍历当前分组下的历史记录 -->
  <mat-list-item 
    *ngFor="let history of group.items" 
    class="historyClicking" 
    (mouseover)="history.showTrash=true" 
    (mouseout)="history.showTrash=false" 
    (click)="onclick()"
  >
    <div class="block">
      <div matList>{{history.id}}</div>
      <!-- 可在此处补充favicon、页面标题、url、删除按钮等元素 -->
    </div>
  </mat-list-item>
</ng-container>

可选优化

  • 如果需要更丰富的相对日期文案(如「上周二」「3天前」),直接修改dateLabel的生成逻辑即可
  • 可以给日期头加对应样式,和Chrome历史页的标题字号、间距保持一致
  • 组内记录如果需要二次排序(比如相同时间的按访问频次排序),直接在sortedHistories的排序逻辑里加判断条件即可

内容的提问来源于stack exchange,提问作者Picolo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 14:27:16