Nexus#3:一次贯穿前后端的bug修复

2025-09-20

最近,我在为我的个人网站添加新的功能,遇到了一系列问题,包括UI美化、AI总结输出不对、图表数据对接经过一番折腾,最终都得以解决

一、UI美化

我为我的计时结构添加了“事物项”标签,旨在记录那些我想要知道时长的具体事务。实际体验中,这类卡片与其他普通卡片混淆,导致体验不佳

条件化渲染与主题化设计

SortableTaskItem 中,我定义了一个核心状态判断:

const hasInstanceTag = task.instanceTag && task.instanceTag.trim() !== '';

基于 hasInstanceTag ,我进行了双重改造:

1. 内容优先级调整: 当存在“事物项”时,它将成为卡片的主标题,而原始任务名则以标签形式作为补充说明

<h3 className="font-medium text-white ...">
  {/* 优先显示事物项名称 */}
  {hasInstanceTag ? task.instanceTag : task.name}
  {hasInstanceTag && (
    <span className="text-xs text-orange-300 ml-2 whitespace-nowrap">
      {task.name}
    </span>
  )}
</h3>

2. 视觉主题化: 为了让带有“事物项”的卡片脱颖而出,我设计了一套独立的橙色主题,覆盖了从边框、背景到文字和按钮的所有元素,与普通卡片的蓝/绿主题形成鲜明对比

<Card 
  className={`... ${
    hasInstanceTag ? 'bg-slate-800' : 'bg-gray-900'
  } ${
    task.isRunning 
      ? (hasInstanceTag ? 'border-orange-400' : 'border-blue-300')
      : (hasInstanceTag ? 'border-orange-600' : 'border-gray-600')
  } ...`}
>
  ...
</Card>

意外的插曲:与darkreader的冲突

起初我为“事物项”卡片设计了一个紫色渐变背景 (bg-gradient-to-r from-purple-900 to-indigo-900)。然而,测试时发现,当启用 DarkReader 这类暗色模式扩展时,渐变背景会被错误地渲染为透明,导致卡片内容几乎无法阅读

原因在于这类扩展的颜色反转算法难以完美处理复杂的CSS gradient解决方案是改用稳定的单一深色 bg-slate-800,并将主题色从紫色切换到橙色,后者在 DarkReader 下的对比度和表现都更为可靠


二、用于AI总结的冗余信息

奇怪的AI总结

“…值得注意的是,77%的任务处于暂停状态,显示多任务并行但完成度较低的工作模式。”

设计状态时考虑到了多种情况,但大部分被弃用了,而并没有移除相关逻辑。 elapsedTime 为0,默认状态为 isPaused: true。AI将这些“未开始”的任务错误地解读为“已开始但中途暂停”

数据预处理 + Prompt工程

1. 数据预处理

ai-service.ts 中,重构了 prepareAnalysisData 。不再统计任务的运行/暂停数量,而是根据 elapsedTime 是否大于0,将任务划分为 “有效计时任务”“无计时任务”

// ai-service.ts
const tasksWithTime = tasks.filter(task => task.elapsedTime > 0);
const tasksWithoutTime = tasks.filter(task => task.elapsedTime === 0);

// 传递给AI的数据结构也相应调整
return {
  // ...
  tasksWithActualTimeCount: tasksWithTime.length,
  // ...
};

2. Prompt优化

  • System Prompt (系统提示词):明确指示:这是一个纯计时系统,重点关注时间投入而非任务状态。请忽略任务的状态信息…不要提及任务状态分布,只分析时长数据

  • User Prompt (用户提示词):移除所有状态相关的描述,只提供纯粹的、经过筛选的(elapsedTime > 0)任务和时长列表


三、逻辑错乱的旭日图

经过排查,这是一个由三个bug交织而成的烂摊子,需要一次彻底的重构

问题一:两层重叠

  • 现象:旭日图的第一层(主分类)与第二层(子分类)在视觉上严重重叠
  • 根源:这是一个经典的数据聚合错误。在构建层级数据时,父节点的 value 被重复计算,导致其值远大于所有子节点 value 之和。ECharts严格按 value 渲染面积,这必然导致视觉错乱
  • 修复:重写 buildEChartsSunburstData 函数
    1. 预聚合:先将扁平数据处理成 Map<主类, Map<子类, 任务[]>> 的嵌套结构
    2. 逐层计算:从最内层的任务(叶子节点)开始,确保每一层父节点的 value 精确等于其直属子节点 value 的总和。这从根本上保证了数据的逻辑一致性,从而解决了渲染问题

问题三:下钻失灵

  • 现象:点击第二层分类,图表非但没有下钻到第三层(具体任务),反而直接返回了顶层
  • 根源
    1. ECharts的 nodeClick 默认行为不符合预期
    2. 点击事件处理器 handleChartClick 没有区分节点类型,导致点击任何非叶子节点都会触发不必要的状态更新,重置图表
  • 修复
    1. 明确配置:在ECharts的series配置中,显式设置 nodeClick: 'zoomToNode',让图表正确地缩放到被点击的节点
    2. 精确事件处理:在构建数据时,为真正的任务节点(叶子节点)添加一个元数据标记 isLeaf: true。然后在事件处理器中增加判断,仅当点击的是叶子节点时,才触发更新任务详情列表的逻辑
// handleChartClick
if (params.data && params.data.isLeaf === true) {
  // ... 只有点击叶子节点时,才执行更新列表的逻辑
}

总结

  1. 用户体验是起点和终点:无论是UI的视觉区分,还是AI的精准洞察,最终都服务于用户能否更高效、更清晰地获取信息
  2. 数据是驱动一切的血液:错误的数据预处理会导致AI产生误导性结论;不精确的数据聚合则会使数据可视化组件彻底崩溃
  3. 细节决定健壮性:一个被浏览器扩展破坏的CSS渐变,一个未加区分的点击事件处理器,这些看似微小的疏忽,都可能导致核心功能的失效