解决 StrategyRoutes.tsx import 冲突:保留双方各自唯一的页面导入(AgentsPage/ChatPage/ChatsPage + AdminUserLeaderboardPage/SettingsPage),去掉与下方重复的 13 个 import。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
董宏凯andClaude Opus 4.8 committed 2026-06-05 19:46:19 +08:00
commit f845805706
7 files changed
+1048 -701

No files matched your search

@@ -55,14 +55,6 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
}, 180);
};
const closeNavPanel = () => {
if (hoverCloseTimerRef.current !== null) {
window.clearTimeout(hoverCloseTimerRef.current);
hoverCloseTimerRef.current = null;
}
setHoveredTopId(null);
};
const hasActiveChild = (item: MenuItem): boolean => {
if (item.path && pathname === item.path) {
return true;
@@ -83,7 +75,9 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
if (matchedTopItem) {
setActiveTopId(matchedTopItem.id);
}
closeNavPanel();
// 不在导航时强制关闭 flyout:flyout 绝对定位在 group 盒子之外,强制关闭后鼠标
// 仍停在 flyout 区域却收不到 group 的 mouseenter,导致连续点击第二个任务时面板
// 消失且不回来。开合只交给 hover(mouseleave 有 180ms 宽限),即可连续点选。
}, [pathname, visibleMenuItems]);
const toggleExpanded = (id: string) => {
@@ -122,14 +116,12 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
if (item.children?.length) {
const firstPath = findFirstRoutePath(item.children);
if (firstPath) {
closeNavPanel();
navigate(firstPath);
}
return;
}
if (item.path) {
closeNavPanel();
navigate(item.path);
}
};
@@ -141,7 +133,6 @@ export function PageSidebarDark({ collapsed, onToggleCollapsed }: PageSidebarDar
}
if (item.path) {
closeNavPanel();
navigate(item.path);
}
};
@@ -184,6 +184,21 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
: 'light'
: readPersistedTheme();
useEffect(() => {
if (typeof document === 'undefined') return;
const root = document.documentElement;
if (theme === 'dark') {
root.setAttribute('theme-mode', 'dark');
root.classList.add('dark');
return;
}
root.removeAttribute('theme-mode');
root.classList.remove('dark');
}, [theme]);
const toggleTheme = () => {
setNextTheme(theme === 'dark' ? 'light' : 'dark');
markExplicitChoice();
+44 -57
View File
@@ -1,48 +1,47 @@
import { Navigate, Route, Routes } from "react-router-dom";
import { Toaster } from "sonner";
import { SidebarProvider } from "@/components/ui/sidebar";
import { getStoredAuth } from "@/core/auth";
import { RouteBaseProvider } from "@/core/route-base";
import { SidebarProvider } from "@/components/ui/sidebar";
import GeneralQA from "@/strategy-components/components/chat-qa/GeneralQA";
import { ChatRuntime } from "./WorkspaceRoutes";
import AgentsPage from "./AgentsPage";
import ChatsPage from "./ChatsPage";
import ChatPage from "./ChatPage";
import NewAgentPage from "./NewAgentPage";
import FanganTyPage from "@/strategy-components/components/task-components/FanganTy";
import SentimentAnalyzes from "@/strategy-components/components/task-components/SentimentAnalyzes";
import TaskManagement from "@/strategy-components/components/task-components/TaskManagement";
import TaskPgFrom from "@/strategy-components/components/task-components/TaskPgFrom";
import TaskXkFrom from "@/strategy-components/components/task-components/TaskXkFrom";
import TaskAnalyzes from "@/strategy-components/components/task-components/TaskAnalyzes";
import XingdongTyPage from "@/strategy-components/components/task-components/XingdongTy";
import XXTyPage from "@/strategy-components/components/task-components/XXTy";
import { ActionAnalyzes } from "@/strategy-components/components/task-components/ActionAnalyzes";
import KnowledgeBaseResourceManagement from "@/strategy-components/components/resource-management/KnowledgeBaseResourceManagement";
import NetworkActorResourceManagement from "@/strategy-components/components/resource-management/NetworkActorResourceManagement";
import RoundtablePlanningPage from "@/roundtable-planning/pages/RoundtablePlanningPage";
import "@/roundtable-planning/styles/roundtable-planning.css";
import AgentChatPage from "./AgentChatPage";
import SkillsPage from "./SkillsPage";
import SkillEvolutionPage from "./SkillEvolutionPage";
import MemoryPage from "./MemoryPage";
import ScheduledTasksPage from "./ScheduledTasksPage";
import ScheduledTaskRunDetailPage from "./ScheduledTaskRunDetailPage";
import RecommendedQuestionsPage from "./RecommendedQuestionsPage";
import PromptPrefixSettingsPage from "./PromptPrefixSettingsPage";
import PrivateSquareSettingsPage from "./PrivateSquareSettingsPage";
import LlmMetricsPage from "./LlmMetricsPage";
import UsersPage from "./UsersPage";
import AdminScheduledTasksPage from "./AdminScheduledTasksPage";
import AdminUserActivityPage from "./AdminUserActivityPage";
import AgentsPage from "./AgentsPage";
import ChatPage from "./ChatPage";
import ChatsPage from "./ChatsPage";
import AdminUserLeaderboardPage from "./AdminUserLeaderboardPage";
import AdminUserLeaderboardSettingsPage from "./AdminUserLeaderboardSettingsPage";
import TagsPage from "./TagsPage";
import LightAppNewNotePage from "./LightAppNewNotePage";
import LightAppNoteListPage from "./LightAppNoteListPage";
import LightAppNoteQAPage from "./LightAppNoteQAPage";
import TaskXkFrom from "@/strategy-components/components/task-components/TaskXkFrom";
import TaskManagement from "@/strategy-components/components/task-components/TaskManagement";
import TaskPgFrom from "@/strategy-components/components/task-components/TaskPgFrom";
import FanganTyPage from "@/strategy-components/components/task-components/FanganTy";
import XingdongTyPage from "@/strategy-components/components/task-components/XingdongTy";
import XXTyPage from "@/strategy-components/components/task-components/XXTy";
import SentimentAnalyzes from "@/strategy-components/components/task-components/SentimentAnalyzes";
import TaskAnalyzes from "@/strategy-components/components/task-components/TaskAnalyzes";
import { ActionAnalyzes } from "@/strategy-components/components/task-components/ActionAnalyzes";
import NetworkActorResourceManagement from "@/strategy-components/components/resource-management/NetworkActorResourceManagement";
import KnowledgeBaseResourceManagement from "@/strategy-components/components/resource-management/KnowledgeBaseResourceManagement";
import RoundtablePlanningPage from "@/roundtable-planning/pages/RoundtablePlanningPage";
import "@/roundtable-planning/styles/roundtable-planning.css";
import MemoryPage from "./MemoryPage";
import NewAgentPage from "./NewAgentPage";
import PromptPrefixSettingsPage from "./PromptPrefixSettingsPage";
import PrivateSquareSettingsPage from "./PrivateSquareSettingsPage";
import RecommendedQuestionsPage from "./RecommendedQuestionsPage";
import ScheduledTaskRunDetailPage from "./ScheduledTaskRunDetailPage";
import ScheduledTasksPage from "./ScheduledTasksPage";
import SkillEvolutionPage from "./SkillEvolutionPage";
import SkillsPage from "./SkillsPage";
import TagsPage from "./TagsPage";
import UsersPage from "./UsersPage";
import LlmMetricsPage from "./LlmMetricsPage";
import AdminScheduledTasksPage from "./AdminScheduledTasksPage";
import AdminUserActivityPage from "./AdminUserActivityPage";
import { ChatRuntime } from "./WorkspaceRoutes";
export default function StrategyRoutes() {
return (
@@ -52,14 +51,13 @@ export default function StrategyRoutes() {
<Routes>
<Route index element={<Navigate to="qa/general" replace />} />
{/* 问答中心 */}
<Route path="qa/general" element={<GeneralQA />} />
<Route
path="qa/expert-qa"
element={
<GeneralQA
chatHeadTitle="专家问答"
chatDesign="支持单轮RAG、多轮RAG、深度检索及结果对比"
chatHeadTitle="涓撳闂瓟"
chatDesign="鏀寔鍗曡疆RAG銆佸杞甊AG銆佹繁搴︽绱㈠強缁撴灉瀵规瘮"
/>
}
/>
@@ -71,10 +69,9 @@ export default function StrategyRoutes() {
</ChatRuntime>
}
/>
{/* 任务管理 */}
<Route path="task-management" element={<TaskManagement />}>
<Route index element={<Navigate to="xk-task-jc" replace />} />
{/* XK任务 */}
<Route
path="xk-task-jc"
element={<TaskXkFrom gopath="#/pageIndie/pageHome/pageSwrw" />}
@@ -83,7 +80,6 @@ export default function StrategyRoutes() {
path="xk-task-dispatch"
element={<TaskXkFrom gopath="#/pageIndie/pageJczl" />}
/>
{/* pg中任务 */}
<Route
path="effect-task-pg"
element={<TaskPgFrom gopath="task" />}
@@ -96,12 +92,11 @@ export default function StrategyRoutes() {
path="effect-info-pg"
element={<TaskPgFrom gopath="info" />}
/>
{/* COH中任务 */}
<Route
path="plan-task-sentiment"
element={
<SentimentAnalyzes
title="3QFX"
title="三情分析"
category="规划任务"
subCategory="规划任务"
/>
@@ -111,9 +106,9 @@ export default function StrategyRoutes() {
path="plan-task/plan-task-fx"
element={
<TaskAnalyzes
title="任务FX"
category="规划任务"
subCategory="规划任务"
title="浠诲姟FX"
category="瑙勫垝浠诲姟"
subCategory="瑙勫垝浠诲姟"
/>
}
/>
@@ -122,17 +117,16 @@ export default function StrategyRoutes() {
element={
<ActionAnalyzes
title="xdfx"
category="规划任务"
subCategory="规划任务"
category="瑙勫垝浠诲姟"
subCategory="瑙勫垝浠诲姟"
/>
}
/>
{/* MNTY任务 */}
<Route path="mnty-task-mechanism" element={<XingdongTyPage />} />
<Route path="mnty-task-plan" element={<FanganTyPage />} />
<Route path="mnty-task-zsty" element={<XXTyPage />} />
</Route>
{/* 资源管理 */}
<Route
path="resource-management/resource-network-actor"
element={<NetworkActorResourceManagement />}
@@ -143,16 +137,13 @@ export default function StrategyRoutes() {
/>
<Route
path="resource-management/resource-evaluation-index"
element={
<TaskXkFrom gopath="#/pageIndie/pageZyjc/pageZyzl" />
}
element={<TaskXkFrom gopath="#/pageIndie/pageZyjc/pageZyzl" />}
/>
{/* 轻应用 */}
<Route path="light-app/new-note" element={<LightAppNewNotePage />} />
<Route path="light-app/notes" element={<LightAppNoteListPage />} />
<Route path="light-app/note-qa" element={<LightAppNoteQAPage />} />
{/* 对话 */}
<Route path="chats" element={<ChatsPage />} />
<Route
path="chats/:thread_id"
@@ -163,7 +154,6 @@ export default function StrategyRoutes() {
}
/>
{/* 智能体管理 */}
<Route path="agents" element={<AgentsPage />} />
<Route path="agents/new" element={<NewAgentPage />} />
<Route
@@ -179,12 +169,10 @@ export default function StrategyRoutes() {
}
/>
{/* 技能 / 记忆 */}
<Route path="skills" element={<SkillsPage />} />
<Route path="skills/evolution" element={<SkillEvolutionPage />} />
<Route path="memory" element={<MemoryPage />} />
{/* 定时任务 */}
<Route
path="scheduled"
element={<Navigate to="/page/strategy/scheduled/tasks" replace />}
@@ -195,7 +183,6 @@ export default function StrategyRoutes() {
element={<ScheduledTaskRunDetailPage />}
/>
{/* 设置 / 管理 — 权限与 workspace 侧一致,非 admin 重定向到 chats/new */}
<Route
path="settings/recommended-questions"
element={
@@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { ChevronRight, FileText } from 'lucide-react';
import { Pagination } from 'tdesign-react';
import { useTheme } from '@/contexts/ThemeContext';
import { TaskListItem } from './TaskManagement';
import { ChevronRight, FileText } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { TaskCard } from './TaskCard';
import { Pagination } from 'tdesign-react';
interface KanbanColumnProps {
title: string;
@@ -11,64 +11,73 @@ interface KanbanColumnProps {
color: string;
tasks: TaskListItem[];
taskType: string;
TaskDataType?: string; // 区分来源类型
TaskDataType?: string;
pageIndexSearch: (taskType: string, pageIndex: number) => void;
handleStartAnalysis: (task: TaskListItem) => void
handleStartAnalysis: (task: TaskListItem) => void;
}
export const KanbanColumn: React.FC<KanbanColumnProps> = ({ title, count, color, tasks,taskType, TaskDataType = 'task' , pageIndexSearch, handleStartAnalysis }) => {
export const KanbanColumn: React.FC<KanbanColumnProps> = ({
title,
count,
color,
tasks,
taskType,
TaskDataType = 'task',
pageIndexSearch,
handleStartAnalysis,
}) => {
const { theme } = useTheme();
const isDark = theme === 'dark';
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
// 当前页码,默认为第 1 页
const [pageNum, setPageNum] = useState<number>(1);
// 每页显示数量,默认为 10 条
const [pageSize, setPageSize] = useState<number>(10); // 每页显示 10 条
/**
* 页码变化时的回调函数
* 当用户切换页码时触发,更新页码并重新获取数据
* @param value - 新的页码值
*/
const [pageNum, setPageNum] = useState(1);
const pageIndexChange = async (value: number) => {
// 更新 ref 中存储的页码值
// 更新页码状态,触发组件重新渲染
console.log('taskType', taskType)
setPageNum(value);
await pageIndexSearch(taskType, value);
}
};
/**
* 每页显示数量变化时的回调函数
* 当用户切换每页显示条数时触发,更新每页数量并重新获取数据
* @param value - 新的每页显示数量
*/
const pageSizeChange = async (value: number) => {
// 更新 ref 中存储的每页显示数量
// 更新每页显示数量状态,触发组件重新渲染
setPageSize(value);
}
return (
<div className="flex flex-col min-h-0 flex-1">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${color}`} />
<h2 className={`font-semibold text-sm ${textPrimary}`}>{title}</h2>
<span className={`px-2 py-0.5 rounded-full text-xs ${isDark ? 'bg-slate-700/50 text-slate-400' : 'bg-red-50 text-red-500'
}`}>{count}</span>
<div
className={`theme-transition flex h-full min-h-0 flex-col rounded-[28px] border p-4 md:p-5 ${
isDark
? 'theme-card border-[rgba(110,136,176,0.24)] shadow-[0_18px_50px_rgba(2,8,20,0.24)]'
: 'border-slate-200 bg-white shadow-sm'
}`}
>
<div className="mb-4 flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
<div className={`mt-1 h-2.5 w-2.5 rounded-full ${color}`} />
<div>
<div className="flex items-center gap-2">
<h2 className={`text-base font-semibold ${isDark ? 'text-slate-50' : 'text-slate-900'}`}>{title}</h2>
<span
className={`rounded-full px-2 py-0.5 text-xs ${
isDark ? 'bg-[rgba(20,47,86,0.92)] text-slate-300' : 'bg-slate-100 text-slate-600'
}`}
>
{count}
</span>
</div>
<p className={`mt-1 text-xs ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>
按时间倒序展示,支持分页切换。
</p>
</div>
</div>
{/* <button className={`p-1 rounded transition-colors ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-red-50'
}`}>
<ChevronRight className={`w-4 h-4 ${textMuted}`} />
</button> */}
<iframe></iframe>
<div>
<div className="flex items-center gap-2">
<div
className={`hidden rounded-xl p-2 lg:flex ${
isDark ? 'bg-[rgba(10,30,54,0.82)] text-slate-300' : 'bg-slate-50 text-slate-500'
}`}
>
<ChevronRight className="h-4 w-4" />
</div>
<Pagination
className="sentiment-pagination"
total={count}
size="small"
theme="simple"
current={pageNum}
pageSize={pageSize}
pageSize={10}
maxPageBtn={3}
showPageSize={false}
totalContent={false}
@@ -76,16 +85,39 @@ export const KanbanColumn: React.FC<KanbanColumnProps> = ({ title, count, color,
/>
</div>
</div>
<div className="flex-1 overflow-y-auto space-y-3 pr-1 scrollbar-thin">
<div className="scrollbar-thin flex-1 space-y-3 overflow-y-auto pr-1">
{tasks.length === 0 ? (
<div className={`flex flex-col items-center justify-center py-8 ${textMuted}`}>
<FileText className="w-8 h-8 mb-2 opacity-50" />
<p className="text-sm">暂无任务</p>
<div
className={`flex h-full min-h-[260px] flex-col items-center justify-center rounded-2xl border border-dashed ${
isDark
? 'border-[rgba(110,136,176,0.24)] bg-[rgba(9,25,46,0.38)]'
: 'border-slate-200 bg-slate-50'
}`}
>
<div
className={`mb-3 flex h-12 w-12 items-center justify-center rounded-2xl ${
isDark ? 'bg-[rgba(13,42,78,0.92)] text-slate-300' : 'bg-white text-slate-500'
}`}
>
<FileText className="h-5 w-5" />
</div>
<div className={`text-sm ${isDark ? 'text-slate-300' : 'text-slate-600'}`}>暂无任务</div>
<div className={`mt-1 text-xs ${isDark ? 'text-slate-500' : 'text-slate-400'}`}>
可以尝试调整筛选条件或切换数据模式。
</div>
</div>
) : (
tasks.map(task => <TaskCard key={task.id} task={task} TaskType={TaskDataType} handleStartAnalysis={handleStartAnalysis}/>)
tasks.map((task) => (
<TaskCard
key={task.id}
task={task}
TaskType={TaskDataType}
handleStartAnalysis={handleStartAnalysis}
/>
))
)}
</div>
</div>
);
}
};
@@ -1,38 +1,13 @@
import React, { useEffect, useRef, useState } from 'react';
import {
Plus,
Search,
Eye,
Play,
Pause,
Clock,
User,
ChevronRight,
Filter,
FileText,
AlertTriangle,
CheckCircle,
XCircle,
MoreHorizontal,
Edit,
Download,
Bot,
Users,
Workflow,
X,
ArrowRight,
Target,
Layers,
} from 'lucide-react';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { ArrowRight, Bot, Clock3, Layers, Search, Sparkles, Users, X } from 'lucide-react';
import { DateRangePicker, DateRangeValue, Loading } from 'tdesign-react';
import dayjs from 'dayjs';
import { useNavigate } from 'react-router-dom';
import { useTheme } from '@/contexts/ThemeContext';
// 导入请求服务,用于与后端 API 交互
import { useChatContext } from '@/contexts/ChatContext';
import { requestCrudService } from '@/service/request-axios-service';
import { KanbanColumn } from './KanbanColumn';
import { TaskListItem } from './TaskManagement';
import { DateRangePicker, DateRangePickerProps, DateRangeValue, Loading } from 'tdesign-react';
import dayjs from 'dayjs';
import { useChatContext } from '@/contexts/ChatContext';
import { useNavigate } from 'react-router-dom';
interface TaskListPageProps {
title: string;
@@ -40,350 +15,575 @@ interface TaskListPageProps {
subCategory: string;
}
interface stateItem {
startTime: string,
endTime: string,
task: TaskListItem,
}
type TaskType = 'single' | 'consultation' | 'scene' | 'multi-agent' | 'task-search';
type TaskStatus = 'draft' | 'pending' | 'running' | 'paused' | 'blocked' | 'completed';
type ColumnType = 'unanalyzed' | 'analyzing' | 'completed';
const service = requestCrudService;
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
const ENABLE_SENTIMENT_DEMO_BY_DEFAULT = import.meta.env.DEV;
const SentimentAnalyzes: React.FC<TaskListPageProps> = ({ title, category, subCategory}) => {
const buildMockTask = (
id: string,
name: string,
overview: string,
column: ColumnType,
type: TaskType,
priority: 1 | 2 | 3,
progress: number,
createTime: string,
updateTime: string,
owner: string,
): TaskListItem => ({
id,
taskName: name,
overview,
column,
type,
priority,
progress,
createTime,
updateTime,
owner,
creator: owner,
executor: owner,
scene: JSON.stringify({
taskJson: JSON.stringify({
task_title: name,
task_goal: overview,
}),
}),
});
const MOCK_TASKS: Record<ColumnType, TaskListItem[]> = {
unanalyzed: [
buildMockTask(
'demo-u-1',
'新能源价格舆情波动分析',
'聚焦近 7 日新能源整车与动力电池价格讨论热度,识别社媒与媒体端异常扩散信号。',
'unanalyzed',
'single',
2,
0,
'2026-06-05 09:10:00',
'2026-06-05 09:10:00',
'王晓楠',
),
buildMockTask(
'demo-u-2',
'重点项目负面反馈聚类',
'从任务评论、审批意见和业务群消息中抽取高频情绪主题,辅助判断风险聚焦点。',
'unanalyzed',
'consultation',
3,
0,
'2026-06-05 08:40:00',
'2026-06-05 08:40:00',
'刘志诚',
),
buildMockTask(
'demo-u-3',
'竞品发布会用户情绪追踪',
'围绕发布会直播、短视频评论与论坛帖子,建立情绪转折时间线并识别主要触发事件。',
'unanalyzed',
'scene',
1,
0,
'2026-06-04 18:20:00',
'2026-06-04 18:20:00',
'陈思雨',
),
buildMockTask(
'demo-u-4',
'区域渠道满意度扫描',
'基于周报、工单和售后回访文本,发现华东区域渠道服务满意度下行趋势。',
'unanalyzed',
'task-search',
2,
0,
'2026-06-04 15:35:00',
'2026-06-04 15:35:00',
'赵明宇',
),
],
analyzing: [
buildMockTask(
'demo-a-1',
'新车上市口碑监测会商',
'正在联合多个智能体汇总平台评价、媒体测评与用户问答,生成阶段性情绪画像。',
'analyzing',
'multi-agent',
3,
56,
'2026-06-05 07:50:00',
'2026-06-05 10:05:00',
'张雨桐',
),
buildMockTask(
'demo-a-2',
'投诉热点归因拆解',
'拆分售后投诉中关于续航、交付和门店服务的负面占比,分析主要归因与蔓延路径。',
'analyzing',
'consultation',
2,
78,
'2026-06-04 20:10:00',
'2026-06-05 09:48:00',
'何嘉成',
),
],
completed: [
buildMockTask(
'demo-c-1',
'车展传播效果情绪复盘',
'已输出传播节点热度变化、核心评论主题以及 KOL 内容对情绪曲线的影响分析。',
'completed',
'single',
1,
100,
'2026-06-03 14:20:00',
'2026-06-04 17:30:00',
'孙怡宁',
),
buildMockTask(
'demo-c-2',
'智能驾驶争议议题扫描',
'已完成争议议题聚类、情绪倾向分层和外部平台扩散链路分析,可直接查看结论。',
'completed',
'scene',
3,
100,
'2026-06-03 09:00:00',
'2026-06-04 11:45:00',
'李承泽',
),
buildMockTask(
'demo-c-3',
'品牌联名活动舆情总结',
'已完成用户态度分布统计与正负面声音对比,附带评论样本摘要和建议动作。',
'completed',
'task-search',
2,
100,
'2026-06-02 16:50:00',
'2026-06-03 19:12:00',
'周清越',
),
],
};
const paginateTasks = (tasks: TaskListItem[], pageIndex: number, pageSize: number) => {
const start = (pageIndex - 1) * pageSize;
return tasks.slice(start, start + pageSize);
};
const SentimentAnalyzes: React.FC<TaskListPageProps> = ({ title, category, subCategory }) => {
const { theme } = useTheme();
const isDark = theme === 'dark';
const router = useNavigate();
// 主题颜色变量
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-100';
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-white';
const { setStartConfig } = useChatContext();
const navigate = useNavigate();
const isDark = theme === 'dark';
const shellClass = isDark
? 'strategy-data min-h-full bg-[radial-gradient(circle_at_top_left,rgba(27,94,168,0.28),transparent_32%),linear-gradient(180deg,#081322_0%,#0d1d33_42%,#0a1628_100%)] text-slate-100'
: 'strategy-data min-h-full bg-[#f8fafc] text-slate-900';
const metricCardClass = isDark
? 'border border-[rgba(110,136,176,0.24)] bg-[linear-gradient(180deg,rgba(12,32,58,0.96),rgba(9,22,39,0.96))] shadow-[0_14px_42px_rgba(2,8,20,0.30)]'
: 'border border-slate-200 bg-white shadow-sm';
const inputClass = isDark
? 'border-[rgba(110,136,176,0.3)] bg-[rgba(8,23,42,0.92)] text-slate-100 placeholder:text-slate-500'
: 'border-slate-200 bg-white text-slate-900 placeholder:text-slate-400';
const panelGlowClass = isDark
? 'before:pointer-events-none before:absolute before:inset-x-8 before:top-0 before:h-px before:bg-[linear-gradient(90deg,transparent,rgba(96,165,250,0.45),transparent)]'
: '';
const [searchQuery, setSearchQuery] = useState('');
const [modeFilter, setModeFilter] = useState<string>('all');
const [showAnalysisModal, setShowAnalysisModal] = useState(false);
const [selectedTaskForAnalysis, setSelectedTaskForAnalysis] = useState<TaskListItem | null>(null);
const [unanalyzedTotal, setUnanalyzedTotal] = useState<number>(0); // 未分析
const [runningTotal, setRunningTotal] = useState<number>(0); // 分析中
const [completedTotal, setCompletedTotal] = useState<number>(0); // 已完成
const [unanalyzedList, setUnanalyzedList] = useState<TaskListItem[]>([]); // 未分析
const [runningList, setRunningList] = useState<TaskListItem[]>([]); // 分析中
const [completedList, setCompletedList] = useState<TaskListItem[]>([]); // 已完成
const [unanalyzedLoading, setUnanalyzedLoading] = useState<boolean>(false); // 未分析
const [runningLoading, setRunningLoading] = useState<boolean>(false); // 分析中
const [completedLoading, setCompletedLoading] = useState<boolean>(false); // 已完成
const [presets] = useState<DateRangePickerProps['presets']>({
最近7天: [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()],
最近3天: [dayjs().subtract(2, 'day').toDate(), dayjs().toDate()],
今天: [dayjs().toDate(), dayjs().toDate()],
});
const [range, setRange] = useState<DateRangeValue>([]);
const state = useRef<stateItem>({
startTime: '',
endTime: '',
task: {} as TaskListItem,
})
const [useDemoData, setUseDemoData] = useState(ENABLE_SENTIMENT_DEMO_BY_DEFAULT);
const [unanalyzedTotal, setUnanalyzedTotal] = useState(0);
const [runningTotal, setRunningTotal] = useState(0);
const [completedTotal, setCompletedTotal] = useState(0);
const [unanalyzedList, setUnanalyzedList] = useState<TaskListItem[]>([]);
const [runningList, setRunningList] = useState<TaskListItem[]>([]);
const [completedList, setCompletedList] = useState<TaskListItem[]>([]);
const [unanalyzedLoading, setUnanalyzedLoading] = useState(false);
const [runningLoading, setRunningLoading] = useState(false);
const [completedLoading, setCompletedLoading] = useState(false);
const [dateRange, setDateRange] = useState<DateRangeValue>([
dayjs().startOf('month').format('YYYY-MM-DD'),
dayjs().endOf('day').format('YYYY-MM-DD'),
]);
// 时间日期选择器查询
const setRangeChange = async (val) => {
setRange(val);
console.log('时间日期', val);
state.current.startTime = '';
state.current.endTime = '';
if (val.length > 0) {
state.current.startTime = val[0];
state.current.endTime = val[1];
}
const pageSizeRef = useRef(10);
await taskDataChange();
}
const filteredMock = useMemo(() => {
const keyword = searchQuery.trim().toLowerCase();
const filterItems = (items: TaskListItem[]) => {
if (!keyword) return items;
return items.filter((item) => {
const text = `${item.taskName} ${item.overview} ${item.owner || ''}`.toLowerCase();
return text.includes(keyword);
});
};
// 处理开始分析按钮点击
const handleStartAnalysis = (task: TaskListItem) => {
setSelectedTaskForAnalysis(task);
state.current.task = task;
setShowAnalysisModal(true);
};
return {
unanalyzed: filterItems(MOCK_TASKS.unanalyzed),
analyzing: filterItems(MOCK_TASKS.analyzing),
completed: filterItems(MOCK_TASKS.completed),
};
}, [searchQuery]);
// 处理分析模式选择
const handleSelectAnalysisMode = (mode: TaskType) => {
setShowAnalysisModal(false);
console.log('mode', mode);
if (mode === 'consultation') {
console.log('state.current.task', state.current.task)
const sceneItem = JSON.parse(state.current.task.scene);
if (sceneItem.taskJson) {
sceneItem.taskJson = JSON.parse(sceneItem.taskJson);
setStartConfig({
...sceneItem.taskJson,
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
task_context: state.current.task.overview
});
router('/qa-center/mope-chat')
}
console.log('sceneItem', sceneItem);
} else if (mode === 'task-search') {
window.open(`https://ch1.b.uat.4.cn/web/#/task/item-aicoh/step-info-search?id=${state.current.task.id}`)
}
};
const applyMockColumn = (column: ColumnType, pageIndex = 1) => {
const source = filteredMock[column];
const pageItems = paginateTasks(source, pageIndex, pageSizeRef.current);
// 处理卡片点击
const taskListSearch = async (taskType: string, pageNum: number = 1, pageSize: number = 10) => {
let taskStatus: number[] = []
if (taskType === 'unanalyzed') { // 未分析
taskStatus = [0, 10];
setUnanalyzedLoading(true);
} else if (taskType === 'running') { // 分析中
taskStatus = [20];
setRunningLoading(true);
} else if (taskType === 'completed') { // 已完成
taskStatus = [25, 30, 40, 60, 70, 80, 90, 100, 110];
setCompletedLoading(true);
}
const param = {
content: searchQuery,
pageNum: pageNum,
pageSize: pageSize,
taskStatus: taskStatus,
startTime: state.current.startTime,
endTime: state.current.endTime,
taskDirection: ''
}
const res = await service.post(`${zncmUrl}/three/cop-three-tasks`, param);
console.log('任务列表', res);
const result = res.data;
result.records.forEach((item: TaskListItem) => {
item.column = taskType;
if (!item.taskLevel) item.taskLevel = 1;
})
if (taskType === 'unanalyzed') {
if (column === 'unanalyzed') {
setUnanalyzedTotal(source.length);
setUnanalyzedList(pageItems);
setUnanalyzedLoading(false);
setUnanalyzedTotal(result.total);
setUnanalyzedList(result.records);
} else if (taskType === 'running') {
} else if (column === 'analyzing') {
setRunningTotal(source.length);
setRunningList(pageItems);
setRunningLoading(false);
setRunningTotal(result.total);
setRunningList(result.records);
} else if (taskType === 'completed') {
} else {
setCompletedTotal(source.length);
setCompletedList(pageItems);
setCompletedLoading(false);
setCompletedTotal(result.total);
setCompletedList(result.records);
}
}
};
/**
* 页码变化时的回调函数
* 当用户切换页码时触发,更新页码并重新获取数据
* @param value - 新的页码值
*/
const pageIndexChange = async (type: string, pageIndex: number) => {
// 更新 ref 中存储的页码值
// 更新页码状态,触发组件重新渲染
await taskListSearch(type, pageIndex);
}
const taskListSearch = async (
taskStatus: ColumnType,
pageIndex = 1,
pageSize = pageSizeRef.current,
) => {
pageSizeRef.current = pageSize;
const taskDataChange = async (pageNum: number = 1, pageSize: number = 10) => {
taskListSearch('unanalyzed', pageNum, pageSize);
taskListSearch('running', pageNum, pageSize);
taskListSearch('completed', pageNum, pageSize);
}
const setLoadingByType = (loading: boolean) => {
if (taskStatus === 'unanalyzed') setUnanalyzedLoading(loading);
if (taskStatus === 'analyzing') setRunningLoading(loading);
if (taskStatus === 'completed') setCompletedLoading(loading);
};
// 回车处理
const handleKeyDown = async (e) => {
if ((e.metaKey || e.ctrlKey || e.shiftKey) && e.key === "Enter") {
e.preventDefault();
} else if (e.keyCode === 13) {
e.preventDefault();
await taskDataChange();
} else if (e.key === "/") {
e.preventDefault();
setLoadingByType(true);
if (useDemoData) {
applyMockColumn(taskStatus, pageIndex);
return;
}
try {
const [startTime, endTime] = Array.isArray(dateRange) ? dateRange : ['', ''];
const payload = {
pageNum: pageIndex,
pageSize,
taskStatus,
taskCategoryName: category,
taskTypeName: subCategory,
searchKey: searchQuery.trim() || undefined,
startTime,
endTime,
};
const response = await service.post(`${zncmUrl}/three/cop-three-tasks`, payload);
const rows = response?.rows || response?.data?.rows || [];
const total = response?.total || response?.data?.total || 0;
const mappedList = rows.map((item: TaskListItem) => ({
...item,
column: taskStatus === 'analyzing' ? 'running' : taskStatus,
}));
if (taskStatus === 'unanalyzed') {
setUnanalyzedList(mappedList);
setUnanalyzedTotal(total);
} else if (taskStatus === 'analyzing') {
setRunningList(mappedList);
setRunningTotal(total);
} else {
setCompletedList(mappedList);
setCompletedTotal(total);
}
} catch (error) {
console.error('sentiment task list fetch failed', error);
applyMockColumn(taskStatus, pageIndex);
setUseDemoData(true);
} finally {
setLoadingByType(false);
}
};
const refreshAllColumns = async () => {
await Promise.all([
taskListSearch('unanalyzed', 1),
taskListSearch('analyzing', 1),
taskListSearch('completed', 1),
]);
};
useEffect(() => {
taskDataChange();
}, []);
refreshAllColumns();
}, [useDemoData]);
// 渲染看板列
useEffect(() => {
const timer = window.setTimeout(() => {
refreshAllColumns();
}, useDemoData ? 120 : 260);
return () => window.clearTimeout(timer);
}, [searchQuery, dateRange, useDemoData]);
const metrics = [
{
label: '全部任务',
value: unanalyzedTotal + runningTotal + completedTotal,
icon: Layers,
accent: 'from-[#4D8DFF] to-[#76C2FF]',
},
{
label: '未分析',
value: unanalyzedTotal,
icon: Clock3,
accent: 'from-[#38BDF8] to-[#7DD3FC]',
},
{
label: '分析中',
value: runningTotal,
icon: Sparkles,
accent: 'from-[#2DD4BF] to-[#67E8F9]',
},
{
label: '已完成',
value: completedTotal,
icon: Bot,
accent: 'from-[#60A5FA] to-[#A78BFA]',
},
];
const handleStartAnalysis = (task: TaskListItem) => {
setSelectedTaskForAnalysis(task);
setShowAnalysisModal(true);
};
const handleViewTaskDetail = () => {
if (!selectedTaskForAnalysis) return;
setStartConfig({
task_title: selectedTaskForAnalysis.taskName,
task_goal: selectedTaskForAnalysis.overview,
task_context: selectedTaskForAnalysis.overview,
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
});
setShowAnalysisModal(false);
navigate('/qa-center/mope-chat');
};
const handleMultiAgentDiscussion = () => {
if (!selectedTaskForAnalysis) return;
setStartConfig({
task_title: selectedTaskForAnalysis.taskName,
task_goal: selectedTaskForAnalysis.overview,
task_context: selectedTaskForAnalysis.overview,
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
});
setShowAnalysisModal(false);
navigate('/page/workspace/roundtable/planning');
};
return (
<div className="h-full flex flex-col">
{/* 顶部工具栏 */}
<div className={`rounded-xl p-4 flex-shrink-0 mb-4 ${cardBg} border ${cardBorder}`}>
<div className="flex items-center justify-between mb-4">
<div>
<div className="flex items-center gap-2 mb-1">
<span className={`px-2 py-0.5 rounded text-xs ${isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600'}`}>
{category}
</span>
<span className={`text-xs ${textMuted}`}>/</span>
<span className={`px-2 py-0.5 rounded text-xs ${isDark ? 'bg-cyan-500/20 text-cyan-400' : 'bg-red-100 text-red-600'}`}>
{subCategory}
</span>
</div>
<h1 className={`text-xl font-bold ${textPrimary}`}>任务中心</h1>
</div>
{/* <button
onClick={() => onNavigate('task-create')}
className={`flex items-center gap-2 px-4 py-2 text-white rounded-lg text-sm font-medium transition-colors ${isDark
? 'bg-blue-600 hover:bg-blue-500'
: 'bg-red-600 hover:bg-red-500 shadow-md'
}`}
>
<Plus className="w-4 h-4" />
新建任务
</button> */}
</div>
{/* 搜索和筛选 */}
<div className="flex items-center gap-4">
<div className="relative flex-1 max-w-md">
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${textMuted}`} />
<input
type="text"
placeholder="搜索任务名称"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => handleKeyDown(e)}
className={`w-full pl-10 pr-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-red-500/20 transition-colors ${inputBg} ${cardBorder} ${textPrimary} placeholder:${textMuted}`}
/>
</div>
{/* <select
value={modeFilter}
onChange={(e) => setModeFilter(e.target.value)}
className={`px-4 py-2 border rounded-lg text-sm focus:outline-none cursor-pointer ${inputBg} ${cardBorder} ${textSecondary}`}
>
<option value="all">全部模式</option>
<option value="single">单智能体</option>
<option value="multi-agent">多智能体协同</option>
<option value="consultation">多智能体会商</option>
<option value="scene">场景执行</option>
</select> */}
{/* <button className={`flex items-center gap-2 px-4 py-2 border rounded-lg text-sm transition-colors ${inputBg} ${cardBorder} ${textSecondary}`}>
<Clock className="w-4 h-4" />
时间范围
</button> */}
<DateRangePicker value={range} presets={presets} onChange={(val) => setRangeChange(val)} enableTimePicker={true} clearable />
</div>
</div>
{/* 统计概览栏 */}
<div className="grid grid-cols-4 gap-4 mb-4 flex-shrink-0">
<button
onClick={() => { setModeFilter('all'); }}
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
>
<p className={`text-2xl font-bold ${textPrimary}`}>{unanalyzedTotal + runningTotal + completedTotal}</p>
<p className={`text-sm ${textSecondary}`}>全部任务</p>
</button>
<button
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
>
<p className={`text-2xl font-bold ${textPrimary}`}>{unanalyzedTotal}</p>
<p className={`text-sm ${textSecondary}`}>未分析</p>
</button>
<button
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
>
<p className={`text-2xl font-bold ${isDark ? 'text-blue-400' : 'text-red-600'}`}>{runningTotal}</p>
<p className={`text-sm ${textSecondary}`}>分析中</p>
</button>
<button
className={`p-4 rounded-xl border transition-all ${cardBg} ${cardBorder} hover:border-red-400`}
>
<p className={`text-2xl font-bold ${isDark ? 'text-green-400' : 'text-green-600'}`}>{completedTotal}</p>
<p className={`text-sm ${textSecondary}`}>已完成</p>
</button>
</div>
{/* 看板区 */}
<div className="flex-1 grid grid-cols-3 gap-4 min-h-0 overflow-hidden">
<Loading loading={unanalyzedLoading}>
<KanbanColumn title="未分析" taskType={"unanalyzed"} TaskDataType={'sentiment'} count={unanalyzedTotal} color={isDark ? 'bg-yellow-400' : 'bg-yellow-400'} tasks={unanalyzedList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
</Loading>
<Loading loading={runningLoading}>
<KanbanColumn title="分析中" taskType="running" TaskDataType={'sentiment'} count={runningTotal} color={isDark ? 'bg-blue-400' : 'bg-red-400'} tasks={runningList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
</Loading>
<Loading loading={completedLoading}>
<KanbanColumn title="已完成" taskType="completed" TaskDataType={'sentiment'} count={completedTotal} color={isDark ? 'bg-green-400' : 'bg-green-400'} tasks={completedList} pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
</Loading>
</div>
{/* 选择分析模式弹窗 */}
{showAnalysisModal && selectedTaskForAnalysis && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick={() => setShowAnalysisModal(false)}>
<div
className={`w-full max-w-lg rounded-2xl p-6 ${cardBg} border ${cardBorder} shadow-xl`}
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between mb-6">
<div className={shellClass}>
<div className={`relative mx-auto flex h-full max-w-[1680px] flex-col gap-5 px-6 py-6 lg:px-8 ${panelGlowClass}`}>
<div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between">
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="flex h-11 w-11 items-center justify-center rounded-2xl border border-[rgba(96,165,250,0.28)] bg-[linear-gradient(180deg,rgba(22,119,255,0.28),rgba(14,63,135,0.18))] shadow-[0_10px_30px_rgba(14,63,135,0.24)]">
<Bot className="h-5 w-5 text-[#7CC9FF]" />
</div>
<div>
<h2 className={`text-xl font-bold ${textPrimary}`}>选择分析模式</h2>
<p className={`text-sm ${textMuted} mt-1`}>为任务「{selectedTaskForAnalysis.taskName}」选择分析方式</p>
<div className="flex items-center gap-2">
<h1 className="text-[26px] font-semibold tracking-[0.02em] text-slate-50">{title}</h1>
{useDemoData ? (
<span className="rounded-full border border-[rgba(125,211,252,0.28)] bg-[rgba(56,189,248,0.14)] px-2.5 py-1 text-xs text-[#8EDCFF]">
演示数据
</span>
) : null}
</div>
<p className={`mt-1 text-sm ${isDark ? 'text-slate-300/80' : 'text-slate-500'}`}>
面向规划任务的情绪与倾向分析看板,支持快速检索、会商启动与结果回看。
</p>
</div>
</div>
<div className={`inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs ${isDark ? 'border-[rgba(110,136,176,0.28)] bg-[rgba(9,25,46,0.72)] text-slate-300' : 'border-slate-200 bg-white text-slate-600'}`}>
<span>{category}</span>
<ArrowRight className="h-3.5 w-3.5 opacity-70" />
<span>{subCategory}</span>
</div>
</div>
<div className="flex flex-col gap-3 xl:items-end">
<div className="flex flex-wrap items-center gap-3">
<label className={`flex h-11 min-w-[320px] items-center gap-3 rounded-2xl border px-4 ${inputClass}`}>
<Search className="h-4 w-4 text-[#7CC9FF]" />
<input
value={searchQuery}
onChange={(event) => setSearchQuery(event.target.value)}
className="w-full bg-transparent text-sm outline-none"
placeholder="搜索任务名称、摘要或负责人"
/>
</label>
<DateRangePicker
className="sentiment-date-range"
value={dateRange}
enableTimePicker={false}
clearable={false}
popupProps={{ overlayClassName: 'sentiment-date-range-popup' }}
rangeInputProps={{
inputProps: [
{ inputClass: 'sentiment-date-range-input' },
{ inputClass: 'sentiment-date-range-input' },
],
}}
onChange={(value) => setDateRange(value)}
/>
</div>
<div className="flex flex-wrap items-center gap-2">
<button
type="button"
onClick={() => setUseDemoData(true)}
className={`rounded-xl px-4 py-2 text-sm transition ${useDemoData ? 'bg-[#1677FF] text-white shadow-[0_8px_22px_rgba(22,119,255,0.28)]' : isDark ? 'bg-[rgba(10,30,54,0.9)] text-slate-300 hover:bg-[rgba(13,42,78,0.95)]' : 'bg-white text-slate-700 hover:bg-slate-50'}`}
>
演示数据
</button>
<button
type="button"
onClick={() => setUseDemoData(false)}
className={`rounded-xl px-4 py-2 text-sm transition ${!useDemoData ? 'bg-[#0EA5E9] text-white shadow-[0_8px_22px_rgba(14,165,233,0.24)]' : isDark ? 'bg-[rgba(10,30,54,0.9)] text-slate-300 hover:bg-[rgba(13,42,78,0.95)]' : 'bg-white text-slate-700 hover:bg-slate-50'}`}
>
真实数据
</button>
</div>
</div>
</div>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
{metrics.map(({ label, value, icon: Icon, accent }) => (
<div key={label} className={`rounded-3xl p-5 ${metricCardClass}`}>
<div className="flex items-center justify-between">
<div>
<p className={`text-sm ${isDark ? 'text-slate-300/78' : 'text-slate-500'}`}>{label}</p>
<p className="mt-3 text-3xl font-semibold tracking-[0.03em] text-white">{value}</p>
</div>
<div className={`flex h-12 w-12 items-center justify-center rounded-2xl bg-gradient-to-br ${accent} shadow-[inset_0_1px_0_rgba(255,255,255,0.18)]`}>
<Icon className="h-5 w-5 text-white" />
</div>
</div>
</div>
))}
</div>
<div className="grid min-h-0 flex-1 grid-cols-1 gap-4 xl:grid-cols-3">
<div className="min-h-[420px]">
<Loading loading={unanalyzedLoading} showOverlay={false}>
<KanbanColumn
title="未分析"
count={unanalyzedTotal}
color="bg-[#38BDF8]"
tasks={unanalyzedList}
taskType="unanalyzed"
TaskDataType="sentiment"
pageIndexSearch={(taskType, pageIndex) => taskListSearch(taskType as ColumnType, pageIndex)}
handleStartAnalysis={handleStartAnalysis}
/>
</Loading>
</div>
<div className="min-h-[420px]">
<Loading loading={runningLoading} showOverlay={false}>
<KanbanColumn
title="分析中"
count={runningTotal}
color="bg-[#2DD4BF]"
tasks={runningList}
taskType="analyzing"
TaskDataType="sentiment"
pageIndexSearch={(taskType, pageIndex) => taskListSearch(taskType as ColumnType, pageIndex)}
handleStartAnalysis={handleStartAnalysis}
/>
</Loading>
</div>
<div className="min-h-[420px]">
<Loading loading={completedLoading} showOverlay={false}>
<KanbanColumn
title="已完成"
count={completedTotal}
color="bg-[#60A5FA]"
tasks={completedList}
taskType="completed"
TaskDataType="sentiment"
pageIndexSearch={(taskType, pageIndex) => taskListSearch(taskType as ColumnType, pageIndex)}
handleStartAnalysis={handleStartAnalysis}
/>
</Loading>
</div>
</div>
</div>
{showAnalysisModal && selectedTaskForAnalysis ? (
<div className="fixed inset-0 z-[2000] flex items-center justify-center bg-[rgba(3,10,22,0.62)] px-4 backdrop-blur-sm">
<div className={`w-full max-w-[560px] rounded-[28px] border p-6 ${isDark ? 'border-[rgba(110,136,176,0.26)] bg-[linear-gradient(180deg,rgba(10,29,54,0.98),rgba(8,20,37,0.98))] text-slate-100 shadow-[0_28px_80px_rgba(2,8,20,0.42)]' : 'border-slate-200 bg-white text-slate-900 shadow-xl'}`}>
<div className="flex items-start justify-between gap-4">
<div>
<h3 className="text-xl font-semibold">选择分析模式</h3>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-300/78' : 'text-slate-500'}`}>
当前任务:{selectedTaskForAnalysis.taskName}
</p>
</div>
<button
type="button"
onClick={() => setShowAnalysisModal(false)}
className={`p-2 rounded-lg hover:bg-slate-700/50 ${textSecondary}`}
className={`rounded-xl p-2 transition ${isDark ? 'hover:bg-[rgba(15,41,74,0.88)]' : 'hover:bg-slate-100'}`}
>
<X className="w-5 h-5" />
<X className="h-4 w-4" />
</button>
</div>
<div className="space-y-3">
<div className="mt-6 grid gap-3">
<button
onClick={() => handleSelectAnalysisMode('task-search')}
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
? 'bg-slate-700/50 border-blue-800/30 hover:border-green-500/50'
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
}`}
type="button"
onClick={handleViewTaskDetail}
className={`group flex items-center justify-between rounded-2xl border px-5 py-4 text-left transition ${isDark ? 'border-[rgba(110,136,176,0.24)] bg-[rgba(10,30,54,0.9)] hover:border-[rgba(96,165,250,0.48)] hover:bg-[rgba(12,38,69,0.96)]' : 'border-slate-200 bg-slate-50 hover:border-sky-200 hover:bg-sky-50'}`}
>
<div className="flex items-center gap-4">
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-green-500/20' : 'bg-green-100'
}`}>
<Layers className={`w-6 h-6 ${isDark ? 'text-green-400' : 'text-green-600'}`} />
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[linear-gradient(180deg,#1677FF,#0EA5E9)]">
<Layers className="h-5 w-5 text-white" />
</div>
<div className="flex-1">
<h3 className={`font-semibold ${textPrimary}`}>查看任务详情</h3>
<p className={`text-sm ${textSecondary}`}>进入b1系统查看任务详情</p>
<div>
<div className="font-medium">查看任务详情</div>
<div className={`mt-1 text-sm ${isDark ? 'text-slate-300/72' : 'text-slate-500'}`}>进入单任务分析视图,查看步骤、结论和上下文信息。</div>
</div>
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
</div>
<ArrowRight className="h-4 w-4 opacity-70 transition group-hover:translate-x-0.5" />
</button>
{/* 多智能体会商 */}
<button
onClick={() => handleSelectAnalysisMode('consultation')}
className={`w-full p-4 rounded-xl border transition-all duration-200 text-left hover:shadow-md ${isDark
? 'bg-slate-700/50 border-blue-800/30 hover:border-orange-500/50'
: 'bg-white border-red-100 hover:border-red-300 hover:shadow-md'
}`}
type="button"
onClick={handleMultiAgentDiscussion}
className={`group flex items-center justify-between rounded-2xl border px-5 py-4 text-left transition ${isDark ? 'border-[rgba(110,136,176,0.24)] bg-[rgba(10,30,54,0.9)] hover:border-[rgba(45,212,191,0.48)] hover:bg-[rgba(10,42,58,0.96)]' : 'border-slate-200 bg-slate-50 hover:border-emerald-200 hover:bg-emerald-50'}`}
>
<div className="flex items-center gap-4">
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${isDark ? 'bg-orange-500/20' : 'bg-red-100'
}`}>
<Users className={`w-6 h-6 ${isDark ? 'text-orange-400' : 'text-red-500'}`} />
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[linear-gradient(180deg,#14B8A6,#22C55E)]">
<Users className="h-5 w-5 text-white" />
</div>
<div className="flex-1">
<h3 className={`font-semibold ${textPrimary}`}>多智能体会商</h3>
<p className={`text-sm ${textSecondary}`}>多个AI智能体从不同视角开展讨论并形成共识</p>
<div>
<div className="font-medium">多智能体会商</div>
<div className={`mt-1 text-sm ${isDark ? 'text-slate-300/72' : 'text-slate-500'}`}>启动会商模式,由多个智能体并行分析情绪走势与影响因素。</div>
</div>
<ArrowRight className={`w-5 h-5 ${textMuted}`} />
</div>
<ArrowRight className="h-4 w-4 opacity-70 transition group-hover:translate-x-0.5" />
</button>
</div>
</div>
</div>
)}
) : null}
</div>
);
};
@@ -1,10 +1,10 @@
import { useTheme } from '@/contexts/ThemeContext';
import { TaskListItem } from './TaskManagement';
import { Bot, ChevronRight, Download, Edit, Eye, FileText, Layers, MoreHorizontal, Play, User, Users, Workflow } from 'lucide-react';
import React from 'react';
import { format } from 'date-fns';
import { useNavigate } from 'react-router-dom';
import { Bot, Eye, Layers, Play, User, Users, Workflow } from 'lucide-react';
import { useTheme } from '@/contexts/ThemeContext';
import { useChatContext } from '@/contexts/ChatContext';
import { TaskListItem } from './TaskManagement';
interface TaskCardProps {
TaskType?: string;
@@ -12,27 +12,20 @@ interface TaskCardProps {
handleStartAnalysis: (task: TaskListItem) => void;
}
export const TaskCard: React.FC<TaskCardProps> = ({ task, TaskType = 'task', handleStartAnalysis }) => {
export const TaskCard: React.FC<TaskCardProps> = ({
task,
TaskType = 'task',
handleStartAnalysis,
}) => {
const { theme } = useTheme();
const { setStartConfig } = useChatContext();
const isDark = theme === 'dark';
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
const textMuted = isDark ? 'text-slate-500' : 'text-gray-400';
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
const navigate = useNavigate();
const isDark = theme === 'dark';
const handleStartChange = (task: TaskListItem) => {
// if (TaskType === 'task') {
// handleStartAnalysis(task);
// } else {
// sceneDataChange(task);
// }
handleStartAnalysis(task);
}
const sceneDataChange = (currentTask: TaskListItem) => {
if (!currentTask.scene) return;
const sceneDataChange = (task: TaskListItem) => {
if (!task.scene) return;
const sceneItem = JSON.parse(task.scene);
const sceneItem = JSON.parse(currentTask.scene);
if (sceneItem.taskJson) {
sceneItem.taskJson = JSON.parse(sceneItem.taskJson);
setStartConfig({
@@ -40,264 +33,198 @@ export const TaskCard: React.FC<TaskCardProps> = ({ task, TaskType = 'task', han
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
task_context: task.overview
task_context: currentTask.overview,
});
navigate('/qa-center/mope-chat')
navigate('/qa-center/mope-chat');
}
console.log('sceneItem', sceneItem);
}
// 处理卡片点击
const handleTaskClick = (task: TaskListItem) => {
};
const handleTaskClick = (currentTask: TaskListItem) => {
if (TaskType === 'sentiment') {
console.log('点击了', task)
sceneDataChange(task);
// window.open(`http://ch1.b.uat.4.cn/taskcop/#/situation-overview/action/sentiment?taskId=${task.id}`)
} else {
if (task.column === 'completed') {
// if (task.type === 'consultation') {
// onNavigate('task-consult-result');
// } else {
// onNavigate('strategy-result');
// }
navigate('/strategy-result')
} else if (task.column === 'running') {
// navigate('/task-management/task-consult')
sceneDataChange(task);
// window.open(`http://ch1.b.uat.4.cn/mopeweb/#/mope?taskOverview=${task.overview}`)
// if (task.type === 'scene') {
// onNavigate('task-scene');
// } else if (task.type === 'consultation') {
// onNavigate('task-consult');
// } else {
// onNavigate('task-single');
// }
}
sceneDataChange(currentTask);
return;
}
if (currentTask.column === 'completed') {
navigate('/strategy-result');
return;
}
if (currentTask.column === 'running' || currentTask.column === 'analyzing') {
sceneDataChange(currentTask);
}
};
// 获取任务类型配置
const handleStartChange = (currentTask: TaskListItem) => {
handleStartAnalysis(currentTask);
};
const getTypeConfig = (type: string) => {
if (isDark) {
switch (type) {
case 'single':
return { label: '单智能体', color: 'bg-cyan-500/20 text-cyan-400', icon: Bot };
case 'scene':
return { label: '场景执行', color: 'bg-purple-500/20 text-purple-400', icon: Workflow };
case 'consultation':
return { label: '多智能体会商', color: 'bg-orange-500/20 text-orange-400', icon: Users };
case 'multi-agent':
return { label: '多智能体协同', color: 'bg-green-500/20 text-green-400', icon: Layers };
}
} else {
switch (type) {
case 'single':
return { label: '单智能体', color: 'bg-blue-100 text-blue-600', icon: Bot };
case 'scene':
return { label: '场景执行', color: 'bg-purple-100 text-purple-600', icon: Workflow };
case 'consultation':
return { label: '多智能体会商', color: 'bg-orange-100 text-orange-600', icon: Users };
case 'multi-agent':
return { label: '多智能体协同', color: 'bg-green-100 text-green-600', icon: Layers };
}
}
};
const typeConfig = getTypeConfig('single');
// 获取任务等级
const getLevel = (type: number) => {
const fallback = { label: '一般', color: isDark ? 'bg-cyan-500/20 text-cyan-400' : 'bg-blue-100 text-blue-600', icon: Bot };
if (isDark) {
switch (type) {
case 1:
return { label: '一般', color: 'bg-cyan-500/20 text-cyan-400', icon: Bot };
case 2:
return { label: '紧急', color: 'bg-purple-500/20 text-purple-400', icon: Workflow };
case 3:
return { label: '加急', color: 'bg-orange-500/20 text-orange-400', icon: Users };
default:
return fallback;
}
} else {
switch (type) {
case 1:
return { label: '一般', color: 'bg-blue-100 text-blue-600', icon: Bot };
case 2:
return { label: '紧急', color: 'bg-purple-100 text-purple-600', icon: Workflow };
case 3:
return { label: '加急', color: 'bg-orange-100 text-orange-600', icon: Users };
default:
return fallback;
}
const common = isDark ? 'border border-white/10 text-slate-200' : 'border border-slate-200 text-slate-700';
switch (type) {
case 'single':
return {
label: '单任务',
icon: Layers,
className: `${common} ${isDark ? 'bg-[rgba(22,119,255,0.14)]' : 'bg-sky-50'}`,
};
case 'consultation':
return {
label: '会商',
icon: Users,
className: `${common} ${isDark ? 'bg-[rgba(20,184,166,0.14)]' : 'bg-emerald-50'}`,
};
case 'scene':
return {
label: '场景',
icon: Workflow,
className: `${common} ${isDark ? 'bg-[rgba(168,85,247,0.14)]' : 'bg-violet-50'}`,
};
case 'multi-agent':
return {
label: '多智能体',
icon: Bot,
className: `${common} ${isDark ? 'bg-[rgba(99,102,241,0.16)]' : 'bg-indigo-50'}`,
};
default:
return {
label: '检索',
icon: Layers,
className: `${common} ${isDark ? 'bg-[rgba(59,130,246,0.12)]' : 'bg-slate-100'}`,
};
}
};
const levelConfig = getLevel(task.taskLevel);
const LevelIcon = levelConfig.icon;
// 获取状态配置
const getLevel = (priority: number = 1) => {
if (priority >= 3) return '加急';
if (priority === 2) return '紧急';
return '一般';
};
const getStatusConfig = (column: string) => {
if (column === 'unanalyzed') {
return { label: '待分析', color: isDark ? 'bg-yellow-500/20 text-yellow-400' : 'bg-yellow-100 text-yellow-600' };
} else if (column === 'running') {
return { label: '运行中', color: isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600' };
} else {
return { label: '已完成', color: isDark ? 'bg-green-500/20 text-green-400' : 'bg-green-100 text-green-600' };
switch (column) {
case 'completed':
return {
label: '已完成',
className: isDark
? 'bg-[rgba(34,197,94,0.16)] text-emerald-300 border border-emerald-400/20'
: 'bg-emerald-50 text-emerald-600 border border-emerald-100',
};
case 'running':
case 'analyzing':
return {
label: '分析中',
className: isDark
? 'bg-[rgba(14,165,233,0.16)] text-sky-300 border border-sky-400/20'
: 'bg-sky-50 text-sky-600 border border-sky-100',
};
default:
return {
label: '待分析',
className: isDark
? 'bg-[rgba(96,165,250,0.14)] text-blue-200 border border-blue-300/15'
: 'bg-blue-50 text-blue-600 border border-blue-100',
};
}
};
const statusConfig = getStatusConfig(task.column);
const typeConfig = getTypeConfig(task.type || 'single');
const statusConfig = getStatusConfig(task.column || 'unanalyzed');
const TypeIcon = typeConfig.icon;
const progressValue = Math.max(0, Math.min(task.progress || 0, 100));
return (
<div
className={`rounded-xl p-4 hover:shadow-md transition-all duration-200 cursor-pointer group theme-card border ${isDark
? 'bg-slate-800/40 backdrop-blur-xl border-blue-800/30 hover:border-blue-500/50'
: 'bg-white border-red-100 hover:shadow-md shadow-red-100'
}`}
onClick={() => handleTaskClick(task)}
className={`theme-card theme-card-hover theme-transition rounded-[24px] border p-4 ${
isDark
? 'border-[rgba(110,136,176,0.2)] shadow-[0_12px_30px_rgba(2,8,20,0.20)]'
: 'border-slate-200 bg-white shadow-sm'
}`}
>
{/* 卡片头部:模式标签 + 状态标签 */}
<div className="flex items-center justify-between mb-2">
<span className={`inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-xs font-medium ${levelConfig.color}`}>
{LevelIcon && <LevelIcon className="w-3 h-3" />}
{levelConfig.label}
</span>
<span></span>
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${statusConfig.color}`}>
<div className="flex items-start justify-between gap-3">
<div className={`inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs ${typeConfig.className}`}>
<TypeIcon className="h-3.5 w-3.5" />
<span>{typeConfig.label}</span>
</div>
<span className={`inline-flex items-center rounded-full px-2.5 py-1 text-xs ${statusConfig.className}`}>
{statusConfig.label}
</span>
</div>
{/* 任务名称 */}
<h3 className={`font-semibold text-sm mb-1 line-clamp-2 group-hover:text-red-500 transition-colors ${textPrimary}`}>
{task.taskName}
</h3>
{/* 任务编号 */}
<p className={`text-xs mb-2 ${textMuted}`}>TASK-{task.id}</p>
{/* 任务摘要 */}
<p className={`text-xs mb-3 line-clamp-2 ${textSecondary}`}>{task.overview}</p>
{/* 补充信息区 */}
{/* <div className="space-y-1 mb-3">
{task.type === 'single' && task.agent && (
<div className="flex items-center gap-2 text-xs">
<Bot className={`w-3 h-3 ${isDark ? 'text-cyan-400' : 'text-red-500'}`} />
<span className={textSecondary}>执行智能体:</span>
<span className={isDark ? 'text-cyan-400' : 'text-red-600'}>{task.agent}</span>
</div>
)}
{task.type === 'scene' && task.scene && (
<div className="flex items-center gap-2 text-xs">
<Workflow className={`w-3 h-3 ${isDark ? 'text-purple-400' : 'text-red-500'}`} />
<span className={textSecondary}>关联场景:</span>
<span className={isDark ? 'text-purple-400' : 'text-red-600'}>{task.scene}</span>
</div>
)}
{task.type === 'consultation' && (
<>
<div className="flex items-center gap-2 text-xs">
<Users className={`w-3 h-3 ${isDark ? 'text-orange-400' : 'text-red-500'}`} />
<span className={textSecondary}>参与智能体:</span>
<span className={isDark ? 'text-orange-400' : 'text-red-600'}>{task.participants?.length || 0}个</span>
</div>
{task.currentRound && (
<div className="flex items-center gap-2 text-xs">
<Clock className={`w-3 h-3 ${textMuted}`} />
<span className={textSecondary}>当前轮次:</span>
<span className={textPrimary}>第{task.currentRound}轮</span>
</div>
)}
</>
)}
{task.column === 'analyzing' && task.currentPhase && (
<div className="flex items-center gap-2 text-xs">
<Target className={`w-3 h-3 ${textMuted}`} />
<span className={textSecondary}>当前阶段:</span>
<span className={textPrimary}>{task.currentPhase}</span>
</div>
)}
</div> */}
{/* 时间信息 */}
<div className={`flex items-center justify-between text-xs pt-3 border-t ${isDark ? 'border-blue-800/30 text-slate-500' : 'border-red-100 text-gray-400'
}`}>
<div className="flex items-center gap-1.5">
<User className="w-3 h-3" />
<span>{task.taskDirection}</span>
<button
type="button"
onClick={() => handleTaskClick(task)}
className="mt-4 block w-full text-left"
>
<div className={`line-clamp-2 text-[15px] font-semibold leading-6 ${isDark ? 'text-slate-50' : 'text-slate-900'}`}>
{task.taskName}
</div>
<p className={`mt-2 line-clamp-3 text-sm leading-6 ${isDark ? 'text-slate-300/78' : 'text-slate-500'}`}>
{task.overview || '暂无任务摘要'}
</p>
</button>
<div className="mt-4 space-y-3">
<div className="flex items-center justify-between text-xs">
<div className={`flex items-center gap-1.5 ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>
<User className="h-3.5 w-3.5" />
<span>{task.owner || task.creator || '未分配'}</span>
</div>
<div className={`${isDark ? 'text-slate-500' : 'text-slate-400'}`}>优先级:{getLevel(task.priority)}</div>
</div>
<div>
<div className="mb-1.5 flex items-center justify-between text-xs">
<span className={isDark ? 'text-slate-400' : 'text-slate-500'}>分析进度</span>
<span className={isDark ? 'text-slate-300' : 'text-slate-700'}>{progressValue}%</span>
</div>
<div className={`h-2 overflow-hidden rounded-full ${isDark ? 'bg-[rgba(255,255,255,0.08)]' : 'bg-slate-100'}`}>
<div
className="h-full rounded-full bg-[linear-gradient(90deg,#1677FF,#5EEAD4)]"
style={{ width: `${progressValue}%` }}
/>
</div>
</div>
<div className="flex items-center justify-between text-xs">
<span className={isDark ? 'text-slate-500' : 'text-slate-400'}>
创建于 {task.createTime ? format(new Date(task.createTime), 'MM-dd HH:mm') : '--'}
</span>
<span className={isDark ? 'text-slate-500' : 'text-slate-400'}>
更新于 {task.updateTime ? format(new Date(task.updateTime), 'MM-dd HH:mm') : '--'}
</span>
</div>
<span>{format(new Date(task.updateTime), 'yyyy-MM-dd HH:mm:ss')}</span>
</div>
{/* 操作按钮区 */}
<div className={`flex items-center gap-2 mt-3 pt-3 border-t ${isDark ? 'border-blue-800/30' : 'border-red-100'
}`}>
{task.column === 'unanalyzed' && (
<>
<button
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
className={`flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-white rounded-lg text-xs font-medium transition-colors ${isDark
? 'bg-blue-600 hover:bg-blue-500'
: 'bg-red-600 hover:bg-red-500 shadow-md'
}`}
>
<Play className="w-3 h-3" />
<div className="mt-4 flex gap-2">
{task.column === 'completed' ? (
<button
type="button"
onClick={() => handleTaskClick(task)}
className={`flex-1 rounded-xl px-3 py-2 text-sm transition ${
isDark
? 'bg-[rgba(11,40,71,0.95)] text-slate-100 hover:bg-[rgba(15,49,87,0.98)]'
: 'bg-slate-900 text-white hover:bg-slate-800'
}`}
>
<span className="inline-flex items-center gap-2">
<Eye className="h-4 w-4" />
查看结果
</span>
</button>
) : (
<button
type="button"
onClick={() => handleStartChange(task)}
className="flex-1 rounded-xl bg-[linear-gradient(90deg,#1677FF,#0EA5E9)] px-3 py-2 text-sm text-white shadow-[0_10px_24px_rgba(22,119,255,0.28)] transition hover:brightness-110"
>
<span className="inline-flex items-center gap-2">
<Play className="h-4 w-4" />
开始分析
</button>
{/* <button
onClick={(e) => { e.stopPropagation(); onNavigate('task-create'); }}
className={`p-1.5 rounded-lg transition-colors ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-red-50'
}`}
title="编辑"
>
<Edit className={`w-4 h-4 ${isDark ? 'text-slate-400' : 'text-red-400'}`} />
</button> */}
</>
</span>
</button>
)}
{task.column === 'running' && (
<>
<button
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
className={`flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-white rounded-lg text-xs font-medium transition-colors ${isDark
? 'bg-blue-600 hover:bg-blue-500'
: 'bg-red-600 hover:bg-red-500 shadow-md'
}`}
>
<Play className="w-3 h-3" />
开始分析
</button>
{/* <button
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
className={`px-2 py-1.5 rounded-lg text-xs transition-colors ${isDark
? 'hover:bg-slate-700/50 border border-blue-800/30 text-slate-300'
: 'hover:bg-red-50 border border-red-200 text-red-600'
}`}
>
查看进展
</button> */}
</>
)}
{task.column === 'completed' && (
<>
<button
onClick={(e) => { e.stopPropagation(); handleStartChange(task); }}
className={`flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-white rounded-lg text-xs font-medium transition-colors ${isDark
? 'bg-green-600 hover:bg-green-500'
: 'bg-green-600 hover:bg-green-500 shadow-md'
}`}
>
<Eye className="w-3 h-3" />
开始分析
</button>
{/* <button className={`px-2 py-1.5 rounded-lg text-xs transition-colors ${isDark
? 'hover:bg-slate-700/50 border border-blue-800/30 text-slate-300'
: 'hover:bg-red-50 border border-red-200 text-red-600'
}`}>
<Download className="w-4 h-4" />
</button> */}
</>
)}
{/* <button className={`p-1.5 rounded-lg transition-colors ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-red-50'
}`} title="更多">
<MoreHorizontal className={`w-4 h-4 ${isDark ? 'text-slate-400' : 'text-red-400'}`} />
</button> */}
</div>
</div>
);
}
};
@@ -1,3 +1,5 @@
@import "../../../../../docs/ui规范-深蓝/tokens.css";
/**
* Strategy task components styles migrated from web-code/index.css
* Used by TaskAnalyzes, SentimentAnalyzes, ActionAnalyzes and related children.
@@ -20,17 +22,17 @@
/* ── CSS variables (dark) ────────────────────────────────────────────── */
.dark .strategy-data {
--color-main: #0062d9;
--td-brand-color: #ce2d39;
--bg-color: #0062d966;
--card-border: #fa9196;
--text-color: #ffffff;
--color-main: var(--color-primary);
--td-brand-color: var(--color-primary);
--bg-color: rgba(22, 119, 255, 0.12);
--card-border: rgba(110, 136, 176, 0.32);
--text-color: var(--color-text-strong);
--card-bg: 217.2 32.6% 17.5%;
--card-hover: 215 20.2% 25%;
--border-color: 217.2 32.6% 17.5%;
--input-bg: 217.2 32.6% 17.5%;
--input-border: 217.2 32.6% 17.5%;
--card-bg: 220 59% 16%;
--card-hover: 219 55% 22%;
--border-color: 220 54% 29%;
--input-bg: 220 71% 10%;
--input-border: 220 35% 38%;
}
/* ── Theme card (TaskCard, KanbanColumn) ─────────────────────────────── */
@@ -75,10 +77,51 @@
border-radius: 3px;
}
.strategy-data::-webkit-scrollbar,
.strategy-data *::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.strategy-data::-webkit-scrollbar-thumb,
.strategy-data *::-webkit-scrollbar-thumb {
background: rgba(203, 213, 225, 0.75);
border-radius: 999px;
}
.strategy-data::-webkit-scrollbar-track,
.strategy-data *::-webkit-scrollbar-track {
background: rgba(241, 245, 249, 0.9);
border-radius: 999px;
}
.dark .strategy-data .scrollbar-thin {
scrollbar-color: rgba(148, 163, 184, 0.45) transparent;
}
.dark .strategy-data,
.dark .strategy-data * {
scrollbar-width: thin;
scrollbar-color: rgba(84, 145, 229, 0.58) rgba(8, 23, 42, 0.24);
}
.dark .strategy-data::-webkit-scrollbar-thumb,
.dark .strategy-data *::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, rgba(96, 165, 250, 0.78), rgba(45, 212, 191, 0.72));
border: 1px solid rgba(8, 23, 42, 0.3);
}
.dark .strategy-data::-webkit-scrollbar-thumb:hover,
.dark .strategy-data *::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, rgba(125, 211, 252, 0.9), rgba(94, 234, 212, 0.82));
}
.dark .strategy-data::-webkit-scrollbar-track,
.dark .strategy-data *::-webkit-scrollbar-track {
background: rgba(8, 23, 42, 0.68);
border: 1px solid rgba(110, 136, 176, 0.12);
}
.dark .strategy-data .scrollbar-thin::-webkit-scrollbar-thumb {
background: rgba(148, 163, 184, 0.45);
}
@@ -91,6 +134,158 @@
background: transparent;
}
.dark .strategy-data .t-input__wrap,
.dark .strategy-data .t-input-adornment,
.dark .strategy-data .t-date-range-picker__cell {
background: rgba(8, 23, 42, 0.92);
border-color: rgba(110, 136, 176, 0.3);
color: var(--color-text-strong);
}
.dark .strategy-data .sentiment-date-range,
.dark .strategy-data .sentiment-date-range .t-range-input,
.dark .strategy-data .sentiment-date-range .t-input__wrap,
.dark .strategy-data .sentiment-date-range .t-range-input__inner,
.dark .strategy-data .sentiment-date-range .sentiment-date-range-input {
background: rgba(8, 23, 42, 0.92) !important;
color: #e6f4ff !important;
border-color: rgba(110, 136, 176, 0.3) !important;
}
.dark .strategy-data .sentiment-date-range .t-range-input {
border-radius: 14px;
box-shadow: inset 0 0 0 1px rgba(110, 136, 176, 0.22);
}
.dark .strategy-data .sentiment-date-range .t-range-input__inner::placeholder,
.dark .strategy-data .sentiment-date-range .t-input__inner::placeholder {
color: rgba(148, 163, 184, 0.82) !important;
}
.dark .strategy-data .sentiment-date-range .t-input__suffix,
.dark .strategy-data .sentiment-date-range .t-input-adornment,
.dark .strategy-data .sentiment-date-range .t-range-input__suffix {
color: #8edcff !important;
}
.dark .strategy-data .t-input__wrap:hover,
.dark .strategy-data .t-date-range-picker__cell:hover {
border-color: rgba(96, 165, 250, 0.45);
}
.dark .strategy-data .t-input__inner,
.dark .strategy-data .t-input-adornment__text,
.dark .strategy-data .t-date-range-picker__cell-text {
color: var(--color-text-strong);
}
.dark .strategy-data .t-input__inner::placeholder {
color: rgba(148, 163, 184, 0.82);
}
.dark .strategy-data .t-pagination .t-button,
.dark .strategy-data .t-pagination .t-pagination__btn {
color: var(--color-text-strong);
background: rgba(10, 30, 54, 0.86);
border-color: rgba(110, 136, 176, 0.24);
}
.dark .strategy-data .sentiment-pagination,
.dark .strategy-data .sentiment-pagination .t-pagination-mini,
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current,
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current-input,
.dark .strategy-data .sentiment-pagination .t-input,
.dark .strategy-data .sentiment-pagination .t-input__wrap,
.dark .strategy-data .sentiment-pagination .t-input__inner {
background: transparent !important;
color: #e6f4ff !important;
}
.dark .strategy-data .t-pagination .t-input,
.dark .strategy-data .t-pagination .t-input__wrap,
.dark .strategy-data .t-pagination .t-input-adornment,
.dark .strategy-data .t-pagination .t-pagination__jump-number-input,
.dark .strategy-data .t-pagination .t-pagination__jump-number-input .t-input__wrap,
.dark .strategy-data .t-pagination .t-pagination__jump-number-input .t-input__inner {
background: rgba(10, 30, 54, 0.92) !important;
color: var(--color-text-strong) !important;
border-color: rgba(110, 136, 176, 0.3) !important;
box-shadow: none !important;
}
.dark .strategy-data .t-pagination .t-pagination__jump-number-input {
border-radius: 10px;
}
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current-input {
border-bottom: 1px solid rgba(148, 163, 184, 0.75);
}
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current-input:hover,
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current-input:focus-within {
border-bottom-color: #8edcff;
}
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current,
.dark .strategy-data .sentiment-pagination .t-pagination-mini__current-input,
.dark .strategy-data .sentiment-pagination .t-pagination__btn-prev,
.dark .strategy-data .sentiment-pagination .t-pagination__btn-next {
color: #e6f4ff !important;
}
.dark .strategy-data .sentiment-pagination .t-button,
.dark .strategy-data .sentiment-pagination .t-pagination__btn {
background: rgba(10, 30, 54, 0.86) !important;
}
.dark .strategy-data .t-pagination .t-pagination__jump-number-input .t-input__inner,
.dark .strategy-data .t-pagination .t-input__inner {
color: #e6f4ff !important;
caret-color: #8edcff;
}
.dark .strategy-data .t-pagination .t-pagination__jump-number-input .t-input__inner::placeholder,
.dark .strategy-data .t-pagination .t-input__inner::placeholder {
color: rgba(148, 163, 184, 0.82) !important;
}
.dark .strategy-data .t-pagination .t-button:hover,
.dark .strategy-data .t-pagination .t-pagination__btn:hover {
background: rgba(13, 42, 78, 0.95);
}
.dark .strategy-data .t-pagination .t-pagination__current {
color: #8edcff;
}
.dark .strategy-data .t-date-range-picker {
min-width: 320px;
}
.sentiment-date-range-popup .t-date-picker__panel,
.sentiment-date-range-popup .t-date-range-picker__panel,
.sentiment-date-range-popup .t-date-picker__panel-body,
.sentiment-date-range-popup .t-date-picker__panel-content,
.sentiment-date-range-popup .t-date-picker__table,
.sentiment-date-range-popup .t-time-picker__panel {
background: #0d1d33 !important;
color: #e6f4ff !important;
}
.sentiment-date-range-popup .t-popup__content {
background: #0d1d33 !important;
border: 1px solid rgba(110, 136, 176, 0.24);
box-shadow: 0 24px 60px rgba(2, 8, 20, 0.42);
}
.sentiment-date-range-popup .t-date-picker__cell,
.sentiment-date-range-popup .t-date-picker__cell-inner,
.sentiment-date-range-popup .t-date-picker__header,
.sentiment-date-range-popup .t-date-picker__text,
.sentiment-date-range-popup .t-button {
color: #e6f4ff !important;
}
/* ── ActionAnalyzes task sidebar ─────────────────────────────────────── */
.strategy-data .task-item-active:hover {
border: 1px solid var(--color-main);