From 15332eb2a66375b0b2aff130737202682980a23d Mon Sep 17 00:00:00 2001
From: chengmw <chengmingwei_1984122@126.com>
Date: Tue, 31 Mar 2026 15:47:47 +0800
Subject: [PATCH] feat: 优化外呼组件和 Tab 容器功能

---
 web-app/src/components/common/OutboundCallWidget.jsx |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 57 insertions(+), 3 deletions(-)

diff --git a/web-app/src/components/common/OutboundCallWidget.jsx b/web-app/src/components/common/OutboundCallWidget.jsx
index df9ffe7..b540357 100644
--- a/web-app/src/components/common/OutboundCallWidget.jsx
+++ b/web-app/src/components/common/OutboundCallWidget.jsx
@@ -1,6 +1,7 @@
 import React, { useState, useEffect, useCallback, useRef } from 'react';
 import { useCaseData } from '../../contexts/CaseDataContext';
 import OutboundBotAPIService from '../../services/OutboundBotAPIService';
+import ProcessAPIService from '../../services/ProcessAPIService';
 import { message } from 'antd';
 
 const OUTBOUND_JOBS_KEY = 'outbound_call_jobs';
@@ -31,6 +32,7 @@
   const [isVisible, setIsVisible] = useState(false); // 默认隐藏
   const [isMinimized, setIsMinimized] = useState(false); // 默认展开(非最小化)
   const [calls, setCalls] = useState([]);
+  const [mediationRecords, setMediationRecords] = useState([]); // AI调解记录
   const isMountedRef = useRef(true);
   
   // 轮询间隔(毫秒)
@@ -41,6 +43,28 @@
 
   // 获取 caseId
   const caseId = caseData?.caseId || caseData?.case_id;
+  
+  // 获取 mediationId
+  const mediationId = caseData?.mediation?.id;
+
+  // 加载AI调解记录(无Loading效果)
+  const loadMediationRecords = useCallback(async () => {
+    if (!mediationId) return;
+    
+    try {
+      const response = await ProcessAPIService.getProcessRecords({
+        mediation_id: mediationId
+      });
+      
+      if (isMountedRef.current) {
+        setMediationRecords(response.data || []);
+        console.log('AI调解记录加载成功:', response.data?.length || 0, '条');
+      }
+    } catch (err) {
+      console.error('加载AI调解记录失败:', err);
+      // 不显示错误提示,不设置loading状态
+    }
+  }, [mediationId]);
 
   // 格式化通话时长
   const formatDuration = (seconds) => {
@@ -110,8 +134,14 @@
         }
       });
       
+      // 获取成功任务的 personId 集合
+      const successPersonIds = new Set(successJobs.map(job => job.personId));
+      
+      // 过滤掉已有成功任务的 personId 对应的失败任务(成功任务优先)
+      const filteredFailedJobs = uniqueFailedJobs.filter(job => !successPersonIds.has(job.personId));
+      
       // 合并所有任务
-      return [...successJobs, ...uniqueFailedJobs];
+      return [...successJobs, ...filteredFailedJobs];
     } catch (err) {
       console.error('读取外呼任务失败:', err);
       return [];
@@ -335,11 +365,18 @@
 
   // 定时轮询通话状态
   useEffect(() => {
+    // 组件挂载时设置为 true
+    isMountedRef.current = true;
+    
     // 初始加载
     fetchCallStatus();
+    loadMediationRecords(); // 初始加载调解记录
     
     // 设置轮询定时器(10秒间隔)
-    const interval = setInterval(fetchCallStatus, POLL_INTERVAL);
+    const interval = setInterval(() => {
+      fetchCallStatus();
+      loadMediationRecords(); // 每10秒加载一次AI调解记录
+    }, POLL_INTERVAL);
     
     // 监听外呼任务更新事件(立即刷新)
     const handleOutboundJobsUpdated = () => {
@@ -348,13 +385,25 @@
     };
     window.addEventListener('outbound-jobs-updated', handleOutboundJobsUpdated);
     
+    // 监听调解终止事件(关闭外呼气泡)
+    const handleMediationTerminated = () => {
+      console.log('收到调解终止事件,关闭外呼气泡');
+      setIsVisible(false);
+      setIsMinimized(true);
+      // 清空localStorage中的外呼任务
+      localStorage.removeItem(OUTBOUND_JOBS_KEY);
+      setCalls([]);
+    };
+    window.addEventListener('mediation-terminated', handleMediationTerminated);
+    
     // 清理函数
     return () => {
       clearInterval(interval);
       window.removeEventListener('outbound-jobs-updated', handleOutboundJobsUpdated);
+      window.removeEventListener('mediation-terminated', handleMediationTerminated);
       isMountedRef.current = false;
     };
-  }, [fetchCallStatus]);
+  }, [fetchCallStatus, loadMediationRecords]);
 
   // 关闭气泡
   const handleClose = (e) => {
@@ -393,6 +442,11 @@
     return true;
   });
 
+  // 如果没有活跃任务且不可见,不渲染任何内容
+  if (activeCalls.length === 0 && !isVisible) {
+    return null;
+  }
+
   // 如果最小化,显示AI客服图标
   if (isMinimized) {
     return (

--
Gitblit v1.8.0