前端给AI消息做日期分组与时间线 先说结论:把一长条 AI 对话消息变成清爽的时间线,核心就两步—— 按会话(session)分组 、再 按日期(今天/昨天/更早)打标签 。信息层用 reduce 归并,渲染层做粘性日期头,不用引第三方库。下面是我前两周真踩过的活儿,代码能直接抄。 起因 我在给一个自己搭的 AI 小助手做 web 端聊天界面。后端把消息一股脑按时间正序吐回来,前端就一条接一条往下堆。测了两天,消息攒到三百多条的时候,整个面板糊成一坨——你根本分不清哪句是今早问的,哪句是上周那次会话留下的。成果同事(其实就是我自己兼)看了一眼说:太脏了。 行吧。 第一步:数据先分好组,别在 JSX 里硬塞 我见过有人直接在模板里 if (上一条日期 !== 这条日期) 插分隔线,能跑,但乱。先在数据层把扁平数组捏成分组结构。 // 假设每条 msg: { id, sessionId, content, role, createdAt } function groupBySession (messages) { const map = new Map (); for (const m of messages) { if (!map.has(m.sessionId)) map .set (m.sessionId, []); map .get (m.sessionId) .push (m); } // Map 保插入顺序,会话天然按首次出现排好 return [...map.entries()] .map (([sessionId, list]) => ({ sessionId, list, startAt: list[ 0 ].createdAt, })); } 会话内再不用动,组与组之间按 startAt 倒序就是最近的会话在最上面。 第二步:日期标签,别去算"几天前" 这步是我栽过的坑。一开始我吭哧吭哧写 Math.floor((now - t) / 86400000) 去算差几天,结果跨午夜就错——晚上23:50和凌晨0:10明明隔了20分钟,算出来"0天",可它俩分属两天,得分到两组。 正解是比 日历日 ,不是比时间差: function dayLabel(ts) { const d = new Date(ts) ; const today = new Date() ; const ymd = x => x.getFullYear() * 10000 + (x.getMonth() + 1 ) * 100 + x.getDate() ; const diff = ymd(today) - ymd(d) ; if ( diff === 0 ) return '今天' ; if ( diff === 1 ) return '昨天' ; if (diff < 7) return d.toLocaleDateString('zh-CN', { weekday: 'long' }) ; // 周三 return `${d.getMonth() + 1}月${d.getDate()}日` ; } 把日期戳压成 年*10000+月*100+日 这个整数再相减,天数差就稳了,不受具体几点几分干扰。 第三步:粘性日期头,滚动时吸顶 时间线的"清爽感"一大半来自滚动时那个日期标签会黏在顶上。纯 CSS 就够: .day-label { position : sticky; top : 0 ; z-index : 2 ; /* 别忘了背景色,不然下面的消息会透上来糊一起 */ background : #f7f8fa ; } background 那行我忘了写,调了十分钟以为是 z-index 的事,结果是文字和下面气泡叠透了。记一下。 一点真实的取舍 会话分组我没做"自动归并相近时间的消息"那种智能合并——比如隔了2小时的两次提问算不算一个会话。试过,阈值定多少都有人觉得不对,干脆老老实实信后端给的 sessionId ,前端只管展示。少做一点反而稳。 另外说句实话,难看的不是布局,是内容本身。我那个 AI 小助手第一版回答又干又短,时间线做得再漂亮,点开每条都是两行废话也白搭。后来我给它配了点私有资料喂进去(RAG 那套),回答才有了血肉,时间线翻起来才像回事。 对了,搭这个小助手我基本没写后端——拖一拖配一配,挂个现成模型、传几份文档当知识库,推出成一个 API,前端直接调。我一个偏前端的人,零代码就把"智能体"那部分糊出来了,省下的精力全砸在这个时间线交互上了。 收尾 分组的活儿听着小,做到"一眼就清爽"挺费心思的——比 ymd 那个整数 trick 还琐碎的细节有七八处。但使用者滑动时那个日期标签稳稳吸在顶上的瞬间,值。 你们做聊天记录的日期分组,是信后端 sessionId 还是前端自己按时间切?评论区聊聊,我那套阈值方案就是被同行劝退的。 (背后那个智能体我挂的是讯飞的 MaaS,现成基础模型直接调 API,没自己部署算力,省心。)
- 上一篇 > `??` 和 `||` 搞混,线上使用者头像全挂了
- 下一篇 > 前端给AI消息做日期分组与时间线

