Files
deerflow-code/frontend-web/src/strategy-components/components/task-components/TaskCard.tsx
T
董宏凯andClaude Sonnet 4.6 b1b2540dc0 feat(theme): 浅蓝色主题适配 —— COH任务三子页 + 应用中心子页
- ThemeContext: 新增 data-theme="lightblue" 属性管理,支持红/蓝双浅色主题
- strategy-data.css: 新增 [data-theme="lightblue"] 变量覆盖块(与浅红默认共存)
- SentimentAnalyzes / TaskAnalyzes / ActionAnalyzes / KanbanColumn / TaskCard / ActionColumn:
  isDark ? dark : isLightBlue ? blue : red 三路适配
- LightAppListPage: 应用列表页品牌色(标题点、徽章、搜索 focus、计数、进入按钮)浅蓝适配
- LightAppNoteQAPage: 问答发送按钮浅蓝适配
- light-app-management.css: 新增 [data-theme="lightblue"] CSS 覆盖块(主按钮、保存、开关、输入 focus 等)
- light-app-pages.css: 浅蓝模式页面底色轻染 #F9FCFF

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:16:06 +08:00

219 lines
7.2 KiB
TypeScript

import React from 'react';
import { format } from 'date-fns';
import { Bot, Eye, Layers, Play, User, Users, Workflow } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { useTheme } from '@/contexts/ThemeContext';
import { useChatContext } from '@/contexts/ChatContext';
import { TaskListItem } from './TaskManagement';
interface TaskCardProps {
task: TaskListItem;
taskDataType?: string;
handleStartAnalysis: (task: TaskListItem) => void;
}
const getTaskTypeConfig = (taskType?: number) => {
switch (taskType) {
case 2:
return { label: '场景', icon: Workflow };
case 3:
return { label: '会商', icon: Users };
case 4:
return { label: '多智能体', icon: Layers };
case 5:
return { label: '检索', icon: Layers };
case 1:
default:
return { label: '单任务', icon: Bot };
}
};
const getTaskLevelLabel = (taskLevel?: number) => {
switch (taskLevel) {
case 2:
return '紧急';
case 3:
return '加急';
case 1:
default:
return '一般';
}
};
const getColumnLabel = (column?: string) => {
if (column === 'running' || column === 'analyzing') return '分析中';
if (column === 'completed') return '已完成';
return '待分析';
};
export const TaskCard: React.FC<TaskCardProps> = ({
task,
taskDataType = 'task',
handleStartAnalysis,
}) => {
const { theme } = useTheme();
const { setStartConfig } = useChatContext();
const navigate = useNavigate();
const isDark = theme === 'dark';
const isLightBlue = theme === 'lightblue';
const typeConfig = getTaskTypeConfig(task.taskType);
const TypeIcon = typeConfig.icon;
const isCompleted = task.column === 'completed';
const isRunning = task.column === 'running' || task.column === 'analyzing';
const openConversation = () => {
try {
const sceneItem = task.scene ? JSON.parse(task.scene) : {};
const taskJson = sceneItem?.taskJson ? JSON.parse(sceneItem.taskJson) : {};
setStartConfig({
...taskJson,
task_title: task.taskName,
task_goal: task.overview,
task_context: task.overview,
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
});
} catch (error) {
setStartConfig({
task_title: task.taskName,
task_goal: task.overview,
task_context: task.overview,
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
});
}
navigate('/qa-center/mope-chat');
};
const handleCardClick = () => {
if (taskDataType === 'sentiment') {
openConversation();
return;
}
if (isCompleted) {
navigate('/strategy-result');
return;
}
if (isRunning) {
openConversation();
}
};
const handleActionClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (isCompleted) {
handleCardClick();
return;
}
handleStartAnalysis(task);
};
const panelClass = isDark
? 'border border-[rgba(67,89,124,0.46)] bg-[rgba(19,39,72,0.9)] shadow-[0_14px_34px_rgba(3,10,22,0.2)] hover:border-[rgba(110,168,255,0.52)]'
: isLightBlue
? 'border border-[#E8E8E8] bg-white shadow-[0_2px_8px_rgba(34,34,34,0.08)] hover:border-[#80BCFF] hover:shadow-[0_4px_16px_rgba(34,34,34,0.12)]'
: 'border border-[#E8E8E8] bg-white shadow-[0_2px_8px_rgba(34,34,34,0.08)] hover:border-[#F6B0B4] hover:shadow-[0_4px_16px_rgba(34,34,34,0.12)]';
const typeBadgeClass = isDark
? 'bg-[rgba(77,141,255,0.14)] text-[#A9D2FF] border border-[rgba(108,161,255,0.24)]'
: isLightBlue ? 'bg-[#EAF4FF] text-[#0062D9] border border-[#AAD2FF]' : 'bg-[#FFF1F0] text-[#CF1322] border border-[#FFD6D6]';
const levelClass = isDark
? 'text-slate-400'
: 'text-[#8C8C8C]';
const statusClass =
task.column === 'completed'
? isDark
? 'bg-[rgba(18,184,134,0.16)] text-[#59E2B1] border border-[rgba(89,226,177,0.2)]'
: 'bg-[#EEFCF7] text-[#0F9D6C] border border-[#B7F0D5]'
: task.column === 'running' || task.column === 'analyzing'
? isDark
? 'bg-[rgba(34,197,94,0.12)] text-[#67E8A5] border border-[rgba(103,232,165,0.2)]'
: 'bg-[#EAF4FF] text-[#1677FF] border border-[#CDE2FF]'
: isDark
? 'bg-[rgba(96,165,250,0.12)] text-[#8FD1FF] border border-[rgba(143,209,255,0.2)]'
: isLightBlue ? 'bg-[#EAF4FF] text-[#0062D9] border border-[#AAD2FF]' : 'bg-[#FFF1F0] text-[#F5222D] border border-[#FFD6D6]';
const actionClass = isCompleted
? isDark
? 'bg-[rgba(12,48,82,0.95)] text-white hover:bg-[rgba(15,60,104,0.98)]'
: isLightBlue ? 'bg-[#EAF4FF] text-[#0062D9] hover:bg-[#D4E9FF]' : 'bg-[#FFF1F0] text-[#CF1322] hover:bg-[#FFE4E6]'
: isDark
? 'bg-[linear-gradient(90deg,#1677FF,#22A8F0)] text-white hover:brightness-110'
: isLightBlue ? 'bg-[linear-gradient(90deg,#0062D9,#2B8FFE)] text-white hover:brightness-105' : 'bg-[linear-gradient(90deg,#F5222D,#FF6B73)] text-white hover:brightness-105';
return (
<article
className={`group rounded p-4 transition-all duration-200 ${panelClass}`}
onClick={handleCardClick}
>
<div className="flex items-start justify-between gap-3">
<span
className={`inline-flex items-center gap-1.5 rounded-sm px-2 py-1 text-xs font-medium ${typeBadgeClass}`}
>
<TypeIcon className="h-3.5 w-3.5" />
{typeConfig.label}
</span>
<span
className={`inline-flex items-center rounded-sm px-2 py-1 text-xs font-medium ${statusClass}`}
>
{getColumnLabel(task.column)}
</span>
</div>
<h3
className={`mt-4 text-[16px] font-semibold leading-6 ${
isDark ? 'text-white' : 'text-[#222222]'
}`}
>
{task.taskName}
</h3>
<p
className={`mt-3 line-clamp-3 min-h-[63px] text-sm leading-[21px] ${
isDark ? 'text-slate-300' : 'text-[#575757]'
}`}
title={task.overview || undefined}
>
{task.overview || '暂无任务摘要'}
</p>
<div className="mt-4 flex items-center justify-between gap-3 text-sm">
<div className={`flex items-center gap-2 ${isDark ? 'text-slate-300' : 'text-[#575757]'}`}>
<User className="h-4 w-4" />
<span>{task.taskDirection || '未分配'}</span>
</div>
<div className={levelClass}>优先级:{getTaskLevelLabel(task.taskLevel)}</div>
</div>
<div
className={`mt-4 grid grid-cols-2 gap-3 text-xs ${
isDark ? 'text-slate-400' : 'text-[#8C8C8C]'
}`}
>
<div>创建于 {format(new Date(task.createTime), 'MM-dd HH:mm')}</div>
<div className="text-right">更新于 {format(new Date(task.updateTime), 'MM-dd HH:mm')}</div>
</div>
<button
type="button"
onClick={handleActionClick}
className={`mt-4 flex h-9 w-full items-center justify-center gap-1.5 rounded-sm text-sm font-medium transition ${actionClass}`}
>
{isCompleted ? <Eye className="h-3.5 w-3.5" /> : <Play className="h-3.5 w-3.5" />}
{isCompleted ? '查看结果' : '开始分析'}
</button>
</article>
);
};