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 5608ff14..d9e34a20 100644 --- a/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx +++ b/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx @@ -15,175 +15,16 @@ interface TaskListPageProps { subCategory: string; } +interface StateItem { + startTime: string; + endTime: string; + task: TaskListItem | null; +} + 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: number, - taskName: string, - overview: string, - column: ColumnType, - taskType: number, - taskLevel: number, - taskDirection: string, - createTime: string, - updateTime: string, -): TaskListItem => ({ - createBy: 0, - createTime, - 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: 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( - 1001, - '新能源价格舆情波动分析', - '聚焦近 7 日新能源整车与动力电池价格讨论热度,识别社媒与媒体端异常扩散信号。', - 'unanalyzed', - 1, - 2, - '王晓楠', - '2026-06-05 09:10:00', - '2026-06-05 09:10:00', - ), - buildMockTask( - 1002, - '重点项目负面反馈聚类', - '从任务评论、审批意见和业务群消息中抽取高频情绪主题,辅助判断风险聚焦点。', - 'unanalyzed', - 3, - 3, - '刘志诚', - '2026-06-05 08:40:00', - '2026-06-05 08:40:00', - ), - buildMockTask( - 1003, - '竞品发布会用户情绪追踪', - '围绕发布会直播、短视频评论与论坛帖子,建立情绪转折时间线并识别主要触发事件。', - 'unanalyzed', - 2, - 1, - '陈思雨', - '2026-06-04 18:20:00', - '2026-06-04 18:20:00', - ), - ], - running: [ - buildMockTask( - 2001, - '新车上市口碑监测会商', - '正在联合多个智能体汇总平台评价、媒体测评与用户问答,生成阶段性情绪画像。', - 'running', - 4, - 3, - '张雨桐', - '2026-06-05 07:50:00', - '2026-06-05 10:05:00', - ), - buildMockTask( - 2002, - '投诉热点归因拆解', - '拆分售后投诉中关于续航、交付和门店服务的负面占比,分析主要归因与蔓延路径。', - 'running', - 3, - 2, - '何嘉成', - '2026-06-04 20:10:00', - '2026-06-05 09:48:00', - ), - ], - completed: [ - buildMockTask( - 3001, - '车展传播效果情绪复盘', - '已输出传播节点热度变化、核心评论主题以及 KOL 内容对情绪曲线的影响分析。', - 'completed', - 1, - 1, - '孙怡宁', - '2026-06-03 14:20:00', - '2026-06-04 17:30:00', - ), - buildMockTask( - 3002, - '智能驾驶争议议题扫描', - '已完成争议议题聚类、情绪倾向分层和外部平台扩散链路分析,可直接查看结论。', - 'completed', - 2, - 3, - '李承泽', - '2026-06-03 09:00:00', - '2026-06-04 11:45:00', - ), - buildMockTask( - 3003, - '品牌联名活动舆情总结', - '已完成用户态度分布统计与正负面声音对比,附带评论样本摘要和建议动作。', - 'completed', - 5, - 2, - '周清越', - '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 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(); @@ -192,9 +33,8 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa const isDark = theme === 'dark'; const [searchQuery, setSearchQuery] = useState(''); - const [useDemoData, setUseDemoData] = useState(ENABLE_SENTIMENT_DEMO_BY_DEFAULT); - const [selectedTask, setSelectedTask] = useState(null); const [showAnalysisModal, setShowAnalysisModal] = useState(false); + const [selectedTask, setSelectedTask] = useState(null); const [unanalyzedTotal, setUnanalyzedTotal] = useState(0); const [runningTotal, setRunningTotal] = useState(0); const [completedTotal, setCompletedTotal] = useState(0); @@ -205,7 +45,12 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa const [runningLoading, setRunningLoading] = useState(false); const [completedLoading, setCompletedLoading] = useState(false); const [range, setRange] = useState([]); - const pageSizeRef = useRef(10); + + const stateRef = useRef({ + startTime: '', + endTime: '', + task: null, + }); const presets = useMemo( () => ({ @@ -216,164 +61,132 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa [], ); - const filteredMock = useMemo(() => { - const keyword = searchQuery.trim().toLowerCase(); - const filterList = (list: TaskListItem[]) => { - if (!keyword) return list; - return list.filter((item) => - `${item.taskName} ${item.overview} ${item.taskDirection}`.toLowerCase().includes(keyword), - ); - }; + const taskStatusMap: Record = { + unanalyzed: [0, 10], + running: [20], + completed: [25, 30, 40, 60, 70, 80, 90, 100, 110], + }; - return { - unanalyzed: filterList(MOCK_TASKS.unanalyzed), - running: filterList(MOCK_TASKS.running), - completed: filterList(MOCK_TASKS.completed), - }; - }, [searchQuery]); + const setRangeChange = async (value: DateRangeValue) => { + setRange(value); + stateRef.current.startTime = ''; + stateRef.current.endTime = ''; - const applyMockColumn = (column: ColumnType, pageIndex = 1) => { - const source = filteredMock[column]; - const pageList = paginateTasks(source, pageIndex, pageSizeRef.current); - - if (column === 'unanalyzed') { - setUnanalyzedTotal(source.length); - setUnanalyzedList(pageList); - setUnanalyzedLoading(false); - return; + if (Array.isArray(value) && value.length > 1) { + stateRef.current.startTime = String(value[0] || ''); + stateRef.current.endTime = String(value[1] || ''); } - if (column === 'running') { - setRunningTotal(source.length); - setRunningList(pageList); - setRunningLoading(false); - return; - } - - setCompletedTotal(source.length); - setCompletedList(pageList); - setCompletedLoading(false); + await taskDataChange(); }; const taskListSearch = async ( - column: ColumnType, - pageIndex = 1, - pageSize = pageSizeRef.current, + taskType: ColumnType, + pageNum = 1, + pageSize = 10, ) => { - pageSizeRef.current = pageSize; - - if (column === 'unanalyzed') setUnanalyzedLoading(true); - if (column === 'running') setRunningLoading(true); - if (column === 'completed') setCompletedLoading(true); - - if (useDemoData) { - applyMockColumn(column, pageIndex); - return; - } - - const taskStatusMap: Record = { - unanalyzed: [0, 10], - running: [20], - completed: [25, 30, 40, 60, 70, 80, 90, 100, 110], - }; + if (taskType === 'unanalyzed') setUnanalyzedLoading(true); + if (taskType === 'running') setRunningLoading(true); + if (taskType === 'completed') setCompletedLoading(true); try { - const { startTime, endTime } = normalizeRangeValue(range); const response = await service.post(`${zncmUrl}/three/cop-three-tasks`, { - content: searchQuery.trim(), - pageNum: pageIndex, + content: searchQuery, + pageNum, pageSize, - taskStatus: taskStatusMap[column], - startTime, - endTime, + taskStatus: taskStatusMap[taskType], + startTime: stateRef.current.startTime, + endTime: stateRef.current.endTime, taskDirection: '', }); - const result = response?.data ?? response ?? {}; + const result = response?.data ?? {}; const records = Array.isArray(result.records) ? result.records : []; - const total = Number(result.total ?? records.length); - const mapped = records.map((item: TaskListItem) => ({ + const normalized = records.map((item: TaskListItem) => ({ ...item, - column, + column: taskType, taskLevel: item.taskLevel || 1, })); + const total = Number(result.total ?? normalized.length); - if (column === 'unanalyzed') { - setUnanalyzedList(mapped); + if (taskType === 'unanalyzed') { setUnanalyzedTotal(total); - } else if (column === 'running') { - setRunningList(mapped); + setUnanalyzedList(normalized); + } else if (taskType === 'running') { setRunningTotal(total); + setRunningList(normalized); } else { - setCompletedList(mapped); setCompletedTotal(total); + setCompletedList(normalized); } - } catch (error) { - console.error('sentiment task list fetch failed', error); - setUseDemoData(true); - applyMockColumn(column, pageIndex); } finally { - if (column === 'unanalyzed') setUnanalyzedLoading(false); - if (column === 'running') setRunningLoading(false); - if (column === 'completed') setCompletedLoading(false); + if (taskType === 'unanalyzed') setUnanalyzedLoading(false); + if (taskType === 'running') setRunningLoading(false); + if (taskType === 'completed') setCompletedLoading(false); } }; - const refreshColumns = () => { - taskListSearch('unanalyzed', 1); - taskListSearch('running', 1); - taskListSearch('completed', 1); + const pageIndexChange = async (type: string, pageIndex: number) => { + await taskListSearch(type as ColumnType, pageIndex); }; - useEffect(() => { - refreshColumns(); - }, [useDemoData]); - - useEffect(() => { - const timer = window.setTimeout(() => { - refreshColumns(); - }, 200); - - return () => window.clearTimeout(timer); - }, [searchQuery, range]); + const taskDataChange = async (pageNum = 1, pageSize = 10) => { + await Promise.all([ + taskListSearch('unanalyzed', pageNum, pageSize), + taskListSearch('running', pageNum, pageSize), + taskListSearch('completed', pageNum, pageSize), + ]); + }; const handleStartAnalysis = (task: TaskListItem) => { setSelectedTask(task); + stateRef.current.task = task; setShowAnalysisModal(true); }; const handleOpenTaskDetail = () => { - if (!selectedTask) return; - - setStartConfig({ - task_title: selectedTask.taskName, - task_goal: selectedTask.overview, - task_context: selectedTask.overview, - llm_preset_id: 'BAILING', - consistency_mode: 'soft', - phase_config: { diverge_rounds: 3 }, - }); - + if (!stateRef.current.task) return; + window.open(`https://ch1.b.uat.4.cn/web/#/task/item-aicoh/step-info-search?id=${stateRef.current.task.id}`); setShowAnalysisModal(false); - navigate('/qa-center/mope-chat'); }; - const handleOpenRoundtable = () => { - if (!selectedTask) return; + const handleOpenConsultation = () => { + const task = stateRef.current.task; + if (!task) return; - setStartConfig({ - 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'); + try { + const sceneItem = JSON.parse(task.scene || '{}'); + if (sceneItem.taskJson) { + const taskJson = JSON.parse(sceneItem.taskJson); + setStartConfig({ + ...taskJson, + llm_preset_id: 'BAILING', + consistency_mode: 'soft', + phase_config: { diverge_rounds: 3 }, + task_context: task.overview, + }); + navigate('/qa-center/mope-chat'); + } + } finally { + setShowAnalysisModal(false); + } }; + const handleKeyDown = async (event: React.KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey || event.shiftKey) && event.key === 'Enter') { + event.preventDefault(); + } else if (event.key === 'Enter') { + event.preventDefault(); + await taskDataChange(); + } else if (event.key === '/') { + event.preventDefault(); + } + }; + + useEffect(() => { + taskDataChange(); + }, []); + 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%)]'; @@ -390,9 +203,8 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa ? '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 + runningTotal + completedTotal }, { label: '未分析', value: unanalyzedTotal }, { label: '分析中', value: runningTotal }, { label: '已完成', value: completedTotal }, @@ -405,38 +217,17 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa
- + {category} - + {subCategory}
-
-

- {title} -

- {useDemoData ? ( - - 演示数据 - - ) : null} -
+

{title}

面向规划任务的情绪与倾向分析看板,支持快速检索、会商启动与结果回看。

@@ -445,13 +236,12 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa
-
-
- - - -
+
@@ -518,27 +274,11 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa

{item.label}

-

+

{item.value}

-
+
@@ -557,7 +297,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa tasks={unanalyzedList} taskType="unanalyzed" taskDataType="sentiment" - pageIndexSearch={taskListSearch} + pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
@@ -574,7 +314,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa tasks={runningList} taskType="running" taskDataType="sentiment" - pageIndexSearch={taskListSearch} + pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
@@ -591,7 +331,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa tasks={completedList} taskType="completed" taskDataType="sentiment" - pageIndexSearch={taskListSearch} + pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
@@ -602,13 +342,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa {showAnalysisModal && selectedTask ? (
-
+

选择分析模式

@@ -629,16 +363,12 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa - -
+
@@ -481,11 +293,11 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor

{label}

-

{value}

+

+ {value} +

-
+
@@ -493,26 +305,6 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor ))}
-
-
-
-
分析工作台
-
- 当前为演示任务数据,便于预览深色模式下的多列分析工作流。 -
-
-
- 目标拆解 / 受众判断 / 会商收敛 -
-
-
-
@@ -523,9 +315,7 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor color="bg-[#7C9BFF]" tasks={unanalyzedList} taskType="unanalyzed" - pageIndexSearch={(taskType, pageIndex) => - taskListSearch(taskType as ColumnType, pageIndex) - } + pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
@@ -540,10 +330,8 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor count={runningTotal} color="bg-[#22C55E]" tasks={runningList} - taskType="analyzing" - pageIndexSearch={(taskType, pageIndex) => - taskListSearch(taskType as ColumnType, pageIndex) - } + taskType="running" + pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
@@ -559,9 +347,7 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor color="bg-[#38BDF8]" tasks={completedList} taskType="completed" - pageIndexSearch={(taskType, pageIndex) => - taskListSearch(taskType as ColumnType, pageIndex) - } + pageIndexSearch={pageIndexChange} handleStartAnalysis={handleStartAnalysis} />
@@ -570,28 +356,20 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor
- {showAnalysisModal && selectedTaskForAnalysis ? ( + {showAnalysisModal && selectedTask ? (
-
+

选择分析模式

- 当前任务:{selectedTaskForAnalysis.taskName} + 当前任务:{selectedTask.taskName}

@@ -600,12 +378,8 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor