215 lines
6.6 KiB
TypeScript
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>
|
|
);
|
|
};
|