- 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>
219 lines
7.2 KiB
TypeScript
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>
|
|
);
|
|
};
|