Files
deerflow-code/frontend-web/src/strategy-components/components/task-components/TaskCard.tsx
T
2026-06-05 22:22:38 +08:00

215 lines
6.6 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 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)]'
: 'border border-[#E8E8E8] bg-white shadow-[0_10px_24px_rgba(245,34,45,0.06)] hover:border-[#F6B0B4] hover:shadow-[0_16px_32px_rgba(245,34,45,0.12)]';
const typeBadgeClass = isDark
? 'bg-[rgba(77,141,255,0.14)] text-[#A9D2FF] border border-[rgba(108,161,255,0.24)]'
: '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)]'
: '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)]'
: 'bg-[#FFF1F0] text-[#CF1322] hover:bg-[#FFE4E6]'
: isDark
? 'bg-[linear-gradient(90deg,#1677FF,#22A8F0)] text-white hover:brightness-110'
: 'bg-[linear-gradient(90deg,#F5222D,#FF6B73)] text-white hover:brightness-105';
return (
<article
className={`group rounded-[24px] 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-full px-3 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-full px-3 py-1 text-xs font-medium ${statusClass}`}
>
{getColumnLabel(task.column)}
</span>
</div>
<h3
className={`mt-5 text-[16px] font-semibold leading-7 ${
isDark ? 'text-white' : 'text-[#222222]'
}`}
>
{task.taskName}
</h3>
<p
className={`mt-3 min-h-[56px] text-sm leading-7 ${
isDark ? 'text-slate-300' : 'text-[#575757]'
}`}
>
{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-5 flex h-12 w-full items-center justify-center gap-2 rounded-full text-sm font-medium transition ${actionClass}`}
>
{isCompleted ? <Eye className="h-4 w-4" /> : <Play className="h-4 w-4" />}
{isCompleted ? '查看结果' : '开始分析'}
</button>
</article>
);
};