- {tasks.length === 0 ? (
-
-
+
+
+
+
+
-
-
-
暂无任务
-
- 可以尝试调整筛选条件或切换数据模式。
-
+ {title}
+
+
+ {count}
+
+
+
+
+
handlePageChange(Number(page))}
+ />
+
+
+
+ 按时间倒序展示,支持分页切换。
+
+
+
+ {tasks.length === 0 ? (
+
) : (
tasks.map((task) => (
))
)}
-
+
);
};
diff --git a/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx b/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx
index b3f3fb56..5608ff14 100644
--- a/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx
+++ b/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx
@@ -1,7 +1,7 @@
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 { ArrowRight, Layers, Plus, Search, Users, X } from 'lucide-react';
import dayjs from 'dayjs';
+import { DateRangePicker, DateRangePickerProps, DateRangeValue, Loading } from 'tdesign-react';
import { useNavigate } from 'react-router-dom';
import { useTheme } from '@/contexts/ThemeContext';
import { useChatContext } from '@/contexts/ChatContext';
@@ -15,158 +15,156 @@ interface TaskListPageProps {
subCategory: string;
}
-type TaskType = 'single' | 'consultation' | 'scene' | 'multi-agent' | 'task-search';
-type ColumnType = 'unanalyzed' | 'analyzing' | 'completed';
+type ColumnType = 'unanalyzed' | 'running' | 'completed';
const service = requestCrudService;
const zncmUrl = import.meta.env.VITE_API_BASE_URL || '';
const ENABLE_SENTIMENT_DEMO_BY_DEFAULT = import.meta.env.DEV;
const buildMockTask = (
- id: string,
- name: string,
+ id: number,
+ taskName: string,
overview: string,
column: ColumnType,
- type: TaskType,
- priority: 1 | 2 | 3,
- progress: number,
+ taskType: number,
+ taskLevel: number,
+ taskDirection: string,
createTime: string,
updateTime: string,
- owner: string,
): TaskListItem => ({
- id,
- taskName: name,
- overview,
- column,
- type,
- priority,
- progress,
+ createBy: 0,
createTime,
- updateTime,
- owner,
- creator: owner,
- executor: owner,
+ deptId: null,
+ id,
+ isTop: 0,
+ issuingDept: '',
+ keywords: null,
+ overview,
+ planTaskTop: 0,
+ platform: null,
+ ptaskId: null,
+ recommendedBasis: null,
scene: JSON.stringify({
taskJson: JSON.stringify({
- task_title: name,
+ task_title: taskName,
task_goal: overview,
}),
}),
+ sourceType: 0,
+ step: null,
+ taskContent: overview,
+ taskDirection,
+ taskEndDate: '',
+ taskLevel,
+ taskName,
+ taskNo: `TASK-${id}`,
+ taskStartDate: '',
+ taskStatus: 0,
+ taskSuperiorFlag: 0,
+ taskTimeLimit: null,
+ taskType,
+ column,
+ updateBy: 0,
+ updateTime,
+ endCount: 0,
+ sumAll: 0,
+ progress: 0,
+ taskStatusData: null,
});
const MOCK_TASKS: Record
= {
unanalyzed: [
buildMockTask(
- 'demo-u-1',
+ 1001,
'新能源价格舆情波动分析',
'聚焦近 7 日新能源整车与动力电池价格讨论热度,识别社媒与媒体端异常扩散信号。',
'unanalyzed',
- 'single',
+ 1,
2,
- 0,
- '2026-06-05 09:10:00',
- '2026-06-05 09:10:00',
'王晓楠',
+ '2026-06-05 09:10:00',
+ '2026-06-05 09:10:00',
),
buildMockTask(
- 'demo-u-2',
+ 1002,
'重点项目负面反馈聚类',
'从任务评论、审批意见和业务群消息中抽取高频情绪主题,辅助判断风险聚焦点。',
'unanalyzed',
- 'consultation',
3,
- 0,
- '2026-06-05 08:40:00',
- '2026-06-05 08:40:00',
+ 3,
'刘志诚',
+ '2026-06-05 08:40:00',
+ '2026-06-05 08:40:00',
),
buildMockTask(
- 'demo-u-3',
+ 1003,
'竞品发布会用户情绪追踪',
'围绕发布会直播、短视频评论与论坛帖子,建立情绪转折时间线并识别主要触发事件。',
'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',
- '赵明宇',
+ 1,
+ '陈思雨',
+ '2026-06-04 18:20:00',
+ '2026-06-04 18:20:00',
),
],
- analyzing: [
+ running: [
buildMockTask(
- 'demo-a-1',
+ 2001,
'新车上市口碑监测会商',
'正在联合多个智能体汇总平台评价、媒体测评与用户问答,生成阶段性情绪画像。',
- 'analyzing',
- 'multi-agent',
+ 'running',
+ 4,
3,
- 56,
+ '张雨桐',
'2026-06-05 07:50:00',
'2026-06-05 10:05:00',
- '张雨桐',
),
buildMockTask(
- 'demo-a-2',
+ 2002,
'投诉热点归因拆解',
'拆分售后投诉中关于续航、交付和门店服务的负面占比,分析主要归因与蔓延路径。',
- 'analyzing',
- 'consultation',
+ 'running',
+ 3,
2,
- 78,
+ '何嘉成',
'2026-06-04 20:10:00',
'2026-06-05 09:48:00',
- '何嘉成',
),
],
completed: [
buildMockTask(
- 'demo-c-1',
+ 3001,
'车展传播效果情绪复盘',
'已输出传播节点热度变化、核心评论主题以及 KOL 内容对情绪曲线的影响分析。',
'completed',
- 'single',
1,
- 100,
+ 1,
+ '孙怡宁',
'2026-06-03 14:20:00',
'2026-06-04 17:30:00',
- '孙怡宁',
),
buildMockTask(
- 'demo-c-2',
+ 3002,
'智能驾驶争议议题扫描',
'已完成争议议题聚类、情绪倾向分层和外部平台扩散链路分析,可直接查看结论。',
'completed',
- 'scene',
+ 2,
3,
- 100,
+ '李承泽',
'2026-06-03 09:00:00',
'2026-06-04 11:45:00',
- '李承泽',
),
buildMockTask(
- 'demo-c-3',
+ 3003,
'品牌联名活动舆情总结',
'已完成用户态度分布统计与正负面声音对比,附带评论样本摘要和建议动作。',
'completed',
- 'task-search',
+ 5,
2,
- 100,
+ '周清越',
'2026-06-02 16:50:00',
'2026-06-03 19:12:00',
- '周清越',
),
],
};
@@ -176,28 +174,27 @@ const paginateTasks = (tasks: TaskListItem[], pageIndex: number, pageSize: numbe
return tasks.slice(start, start + pageSize);
};
+const normalizeRangeValue = (range: DateRangeValue) => {
+ if (!Array.isArray(range) || range.length < 2) {
+ return { startTime: '', endTime: '' };
+ }
+
+ return {
+ startTime: dayjs(range[0]).format('YYYY-MM-DD HH:mm:ss'),
+ endTime: dayjs(range[1]).format('YYYY-MM-DD HH:mm:ss'),
+ };
+};
+
const SentimentAnalyzes: React.FC = ({ title, category, subCategory }) => {
const { theme } = useTheme();
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 [showAnalysisModal, setShowAnalysisModal] = useState(false);
- const [selectedTaskForAnalysis, setSelectedTaskForAnalysis] = useState(null);
const [useDemoData, setUseDemoData] = useState(ENABLE_SENTIMENT_DEMO_BY_DEFAULT);
+ const [selectedTask, setSelectedTask] = useState(null);
+ const [showAnalysisModal, setShowAnalysisModal] = useState(false);
const [unanalyzedTotal, setUnanalyzedTotal] = useState(0);
const [runningTotal, setRunningTotal] = useState(0);
const [completedTotal, setCompletedTotal] = useState(0);
@@ -207,378 +204,465 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa
const [unanalyzedLoading, setUnanalyzedLoading] = useState(false);
const [runningLoading, setRunningLoading] = useState(false);
const [completedLoading, setCompletedLoading] = useState(false);
- const [dateRange, setDateRange] = useState([
- dayjs().startOf('month').format('YYYY-MM-DD'),
- dayjs().endOf('day').format('YYYY-MM-DD'),
- ]);
-
+ const [range, setRange] = useState([]);
const pageSizeRef = useRef(10);
+ const presets = useMemo(
+ () => ({
+ 最近7天: [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()],
+ 最近3天: [dayjs().subtract(2, 'day').toDate(), dayjs().toDate()],
+ 今天: [dayjs().toDate(), dayjs().toDate()],
+ }),
+ [],
+ );
+
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 filterList = (list: TaskListItem[]) => {
+ if (!keyword) return list;
+ return list.filter((item) =>
+ `${item.taskName} ${item.overview} ${item.taskDirection}`.toLowerCase().includes(keyword),
+ );
};
return {
- unanalyzed: filterItems(MOCK_TASKS.unanalyzed),
- analyzing: filterItems(MOCK_TASKS.analyzing),
- completed: filterItems(MOCK_TASKS.completed),
+ unanalyzed: filterList(MOCK_TASKS.unanalyzed),
+ running: filterList(MOCK_TASKS.running),
+ completed: filterList(MOCK_TASKS.completed),
};
}, [searchQuery]);
const applyMockColumn = (column: ColumnType, pageIndex = 1) => {
const source = filteredMock[column];
- const pageItems = paginateTasks(source, pageIndex, pageSizeRef.current);
+ const pageList = paginateTasks(source, pageIndex, pageSizeRef.current);
if (column === 'unanalyzed') {
setUnanalyzedTotal(source.length);
- setUnanalyzedList(pageItems);
+ setUnanalyzedList(pageList);
setUnanalyzedLoading(false);
- } else if (column === 'analyzing') {
- setRunningTotal(source.length);
- setRunningList(pageItems);
- setRunningLoading(false);
- } else {
- setCompletedTotal(source.length);
- setCompletedList(pageItems);
- setCompletedLoading(false);
+ return;
}
+
+ if (column === 'running') {
+ setRunningTotal(source.length);
+ setRunningList(pageList);
+ setRunningLoading(false);
+ return;
+ }
+
+ setCompletedTotal(source.length);
+ setCompletedList(pageList);
+ setCompletedLoading(false);
};
const taskListSearch = async (
- taskStatus: ColumnType,
+ column: ColumnType,
pageIndex = 1,
pageSize = pageSizeRef.current,
) => {
pageSizeRef.current = pageSize;
- const setLoadingByType = (loading: boolean) => {
- if (taskStatus === 'unanalyzed') setUnanalyzedLoading(loading);
- if (taskStatus === 'analyzing') setRunningLoading(loading);
- if (taskStatus === 'completed') setCompletedLoading(loading);
- };
-
- setLoadingByType(true);
+ if (column === 'unanalyzed') setUnanalyzedLoading(true);
+ if (column === 'running') setRunningLoading(true);
+ if (column === 'completed') setCompletedLoading(true);
if (useDemoData) {
- applyMockColumn(taskStatus, pageIndex);
+ applyMockColumn(column, pageIndex);
return;
}
+ const taskStatusMap: Record = {
+ unanalyzed: [0, 10],
+ running: [20],
+ completed: [25, 30, 40, 60, 70, 80, 90, 100, 110],
+ };
+
try {
- const [startTime, endTime] = Array.isArray(dateRange) ? dateRange : ['', ''];
- const payload = {
+ const { startTime, endTime } = normalizeRangeValue(range);
+ const response = await service.post(`${zncmUrl}/three/cop-three-tasks`, {
+ content: searchQuery.trim(),
pageNum: pageIndex,
pageSize,
- taskStatus,
- taskCategoryName: category,
- taskTypeName: subCategory,
- searchKey: searchQuery.trim() || undefined,
+ taskStatus: taskStatusMap[column],
startTime,
endTime,
- };
+ taskDirection: '',
+ });
- 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) => ({
+ const result = response?.data ?? response ?? {};
+ const records = Array.isArray(result.records) ? result.records : [];
+ const total = Number(result.total ?? records.length);
+ const mapped = records.map((item: TaskListItem) => ({
...item,
- column: taskStatus === 'analyzing' ? 'running' : taskStatus,
+ column,
+ taskLevel: item.taskLevel || 1,
}));
- if (taskStatus === 'unanalyzed') {
- setUnanalyzedList(mappedList);
+ if (column === 'unanalyzed') {
+ setUnanalyzedList(mapped);
setUnanalyzedTotal(total);
- } else if (taskStatus === 'analyzing') {
- setRunningList(mappedList);
+ } else if (column === 'running') {
+ setRunningList(mapped);
setRunningTotal(total);
} else {
- setCompletedList(mappedList);
+ setCompletedList(mapped);
setCompletedTotal(total);
}
} catch (error) {
console.error('sentiment task list fetch failed', error);
- applyMockColumn(taskStatus, pageIndex);
setUseDemoData(true);
+ applyMockColumn(column, pageIndex);
} finally {
- setLoadingByType(false);
+ if (column === 'unanalyzed') setUnanalyzedLoading(false);
+ if (column === 'running') setRunningLoading(false);
+ if (column === 'completed') setCompletedLoading(false);
}
};
- const refreshAllColumns = async () => {
- await Promise.all([
- taskListSearch('unanalyzed', 1),
- taskListSearch('analyzing', 1),
- taskListSearch('completed', 1),
- ]);
+ const refreshColumns = () => {
+ taskListSearch('unanalyzed', 1);
+ taskListSearch('running', 1);
+ taskListSearch('completed', 1);
};
useEffect(() => {
- refreshAllColumns();
+ refreshColumns();
}, [useDemoData]);
useEffect(() => {
const timer = window.setTimeout(() => {
- refreshAllColumns();
- }, useDemoData ? 120 : 260);
+ refreshColumns();
+ }, 200);
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]',
- },
- ];
+ }, [searchQuery, range]);
const handleStartAnalysis = (task: TaskListItem) => {
- setSelectedTaskForAnalysis(task);
+ setSelectedTask(task);
setShowAnalysisModal(true);
};
- const handleViewTaskDetail = () => {
- if (!selectedTaskForAnalysis) return;
+ const handleOpenTaskDetail = () => {
+ if (!selectedTask) return;
+
setStartConfig({
- task_title: selectedTaskForAnalysis.taskName,
- task_goal: selectedTaskForAnalysis.overview,
- task_context: selectedTaskForAnalysis.overview,
+ task_title: selectedTask.taskName,
+ task_goal: selectedTask.overview,
+ task_context: selectedTask.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;
+ const handleOpenRoundtable = () => {
+ if (!selectedTask) return;
+
setStartConfig({
- task_title: selectedTaskForAnalysis.taskName,
- task_goal: selectedTaskForAnalysis.overview,
- task_context: selectedTaskForAnalysis.overview,
+ task_title: selectedTask.taskName,
+ task_goal: selectedTask.overview,
+ task_context: selectedTask.overview,
llm_preset_id: 'BAILING',
consistency_mode: 'soft',
phase_config: { diverge_rounds: 3 },
});
+
setShowAnalysisModal(false);
navigate('/page/workspace/roundtable/planning');
};
+ const shellClass = isDark
+ ? 'strategy-data h-full overflow-y-auto bg-[linear-gradient(180deg,#081322_0%,#0d1d33_42%,#0a1628_100%)]'
+ : 'strategy-data h-full overflow-y-auto bg-[linear-gradient(180deg,#FFFDFD_0%,#FFF7F7_46%,#FAFAFA_100%)]';
+
+ const toolbarClass = isDark
+ ? 'border border-[rgba(67,89,124,0.46)] bg-[rgba(10,23,42,0.94)] text-white shadow-[0_16px_36px_rgba(3,10,22,0.2)]'
+ : 'border border-[#E8E8E8] bg-white text-[#222222] shadow-[0_14px_30px_rgba(245,34,45,0.06)]';
+
+ const metricClass = isDark
+ ? 'border border-[rgba(67,89,124,0.46)] bg-[rgba(12,29,54,0.94)]'
+ : 'border border-[#E8E8E8] bg-white';
+
+ const inputClass = isDark
+ ? 'border-[rgba(67,89,124,0.46)] bg-[rgba(8,19,35,0.9)] text-white placeholder:text-slate-500'
+ : 'border-[#E8E8E8] bg-white text-[#222222] placeholder:text-[#A6A6A6]';
+
+ const totalCount = unanalyzedTotal + runningTotal + completedTotal;
+ const metrics = [
+ { label: '全部任务', value: totalCount },
+ { label: '未分析', value: unanalyzedTotal },
+ { label: '分析中', value: runningTotal },
+ { label: '已完成', value: completedTotal },
+ ];
+
return (
-
-
-
-
-
-
+
+
+
+
+
+
+ {category}
+
+
+
+ {subCategory}
+
+
-
-
{title}
+
+
+ {title}
+
{useDemoData ? (
-
+
演示数据
) : null}
-
+
面向规划任务的情绪与倾向分析看板,支持快速检索、会商启动与结果回看。
-
-
{category}
-
-
{subCategory}
-
-
-
-
-
-
+
-
- {metrics.map(({ label, value, icon: Icon, accent }) => (
-
+
+ {metrics.map((item, index) => (
+
-
{label}
-
{value}
+
{item.label}
+
+ {item.value}
+
-
))}
-
+
-
-
-
- taskListSearch(taskType as ColumnType, pageIndex)}
- handleStartAnalysis={handleStartAnalysis}
- />
+
+
-
-
- taskListSearch(taskType as ColumnType, pageIndex)}
- handleStartAnalysis={handleStartAnalysis}
- />
+
+
-
-
- taskListSearch(taskType as ColumnType, pageIndex)}
- handleStartAnalysis={handleStartAnalysis}
- />
+
+
-
+
- {showAnalysisModal && selectedTaskForAnalysis ? (
-
-
+ {showAnalysisModal && selectedTask ? (
+
+
选择分析模式
-
- 当前任务:{selectedTaskForAnalysis.taskName}
+
+ 为任务“{selectedTask.taskName}”选择分析方式
-
+