最近,我在为我的个人网站添加新的功能,遇到了一系列问题,包括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
函数- 预聚合:先将扁平数据处理成
Map<主类, Map<子类, 任务[]>>
的嵌套结构 - 逐层计算:从最内层的任务(叶子节点)开始,确保每一层父节点的
value
精确等于其直属子节点value
的总和。这从根本上保证了数据的逻辑一致性,从而解决了渲染问题
- 预聚合:先将扁平数据处理成
问题三:下钻失灵
- 现象:点击第二层分类,图表非但没有下钻到第三层(具体任务),反而直接返回了顶层
- 根源:
- ECharts的
nodeClick
默认行为不符合预期 - 点击事件处理器
handleChartClick
没有区分节点类型,导致点击任何非叶子节点都会触发不必要的状态更新,重置图表
- ECharts的
- 修复:
- 明确配置:在ECharts的series配置中,显式设置
nodeClick: 'zoomToNode'
,让图表正确地缩放到被点击的节点 - 精确事件处理:在构建数据时,为真正的任务节点(叶子节点)添加一个元数据标记
isLeaf: true
。然后在事件处理器中增加判断,仅当点击的是叶子节点时,才触发更新任务详情列表的逻辑
- 明确配置:在ECharts的series配置中,显式设置
// handleChartClick
if (params.data && params.data.isLeaf === true) {
// ... 只有点击叶子节点时,才执行更新列表的逻辑
}
总结
- 用户体验是起点和终点:无论是UI的视觉区分,还是AI的精准洞察,最终都服务于用户能否更高效、更清晰地获取信息
- 数据是驱动一切的血液:错误的数据预处理会导致AI产生误导性结论;不精确的数据聚合则会使数据可视化组件彻底崩溃
- 细节决定健壮性:一个被浏览器扩展破坏的CSS渐变,一个未加区分的点击事件处理器,这些看似微小的疏忽,都可能导致核心功能的失效