From 6f45735adfdcd973a19f638f9ced9629f79cd6de Mon Sep 17 00:00:00 2001
From: shimai <shimai@example.com>
Date: Wed, 15 Apr 2026 16:12:01 +0800
Subject: [PATCH] v2.0: 提交当前所有变更,准备创建v2.0标签

---
 web-app/src/components/dashboard/TabContainer.jsx |   84 ++++++++++++++++++++++++++++++++----------
 1 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index 4e8c3a6..9cf5070 100644
--- a/web-app/src/components/dashboard/TabContainer.jsx
+++ b/web-app/src/components/dashboard/TabContainer.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
+import React, { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
 import { useCaseData } from '../../contexts/CaseDataContext';
 import { formatDuration, formatSuccessRate } from '../../utils/stateTranslator';
 import ProcessAPIService from '../../services/ProcessAPIService';
@@ -6,7 +6,7 @@
 import MediationAgreementAPIService from '../../services/MediationAgreementAPIService';
 import { getMergedParams } from '../../utils/urlParams';
 import { message, Spin, Tag, Modal, Button, Input, Image } from 'antd';
-import { PhoneOutlined, ArrowUpOutlined } from '@ant-design/icons';
+import { PhoneOutlined } from '@ant-design/icons';
 import { CallRecordModal } from '../call-record';
 
 // 新增组件导入
@@ -129,6 +129,9 @@
   const updateTime = formatDuration(timeline.before_duration);
   const successRate = formatSuccessRate(mediation.success_rate);
   
+  // 获取成功率数值(用于进度条)
+  const successRateValue = (mediation.success_rate || 0) * 100;
+  
   // 获取同比数据
   const yoyData = getSuccessRateYoY(mediation);
   const yoyRate = yoyData.rate >= 0 ? `+${yoyData.rate.toFixed(0)}%` : `${yoyData.rate.toFixed(0)}%`;
@@ -166,11 +169,18 @@
         {/* 预计调解成功率 */}
         <div className="success-rate-section">
           <div className="success-rate-label">预计调解成功率</div>
-          <div className="success-rate-value">{successRate}</div>
-          <div className="success-rate-yoy">
-            <ArrowUpOutlined className="yoy-icon" />
-            <span className="yoy-rate">{yoyRate}</span>
-            <span className="yoy-time">较{yoyHours}小时前</span>
+          <div className="success-rate-row">
+            <span className="success-rate-value">{successRate}</span>
+            <div className="success-rate-yoy">
+              <img src="/mom.png" alt="" className="yoy-icon-img" />
+              <span className="yoy-rate">{yoyRate}</span>
+              <span className="yoy-time">较{yoyHours}小时前</span>
+            </div>
+          </div>
+          <div className="success-rate-progress">
+            <div className="progress-bar-bg">
+              <div className="progress-bar-fill" style={{ width: `${successRateValue}%` }}></div>
+            </div>
           </div>
         </div>
         
@@ -263,16 +273,23 @@
     }));
   };
   
-  // 获取调解记录数据
-  const loadMediationRecords = async () => {
-    setLoading(true);
+  // 看板轮询间隔(毫秒)
+  const BOARD_POLL_INTERVAL = 5000; // 5秒
+  const isBoardMountedRef = useRef(true);
+
+  // 获取调解记录数据(首次加载带 loading,后续静默刷新)
+  const loadMediationRecords = useCallback(async (silent = false) => {
+    if (!silent) {
+      setLoading(true);
+    }
     setError(null);
     
     try {
       // 从timeline中获取mediation_id
       const mediationId = timeline.mediation?.id;
       if (!mediationId) {
-        throw new Error('未找到调解ID');
+        if (!silent) throw new Error('未找到调解ID');
+        return;
       }
       
       // 调用API获取记录列表
@@ -282,23 +299,50 @@
       
       // 格式化数据
       const formattedRecords = formatRecordData(response.data || []);
-      setRecords(formattedRecords);
+      if (isBoardMountedRef.current) {
+        setRecords(formattedRecords);
+      }
       
     } catch (err) {
-      setError(err.message);
-      console.error('获取调解记录失败:', err);
-      message.error(`获取调解记录失败: ${err.message}`);
+      if (!silent) {
+        setError(err.message);
+        console.error('获取调解记录失败:', err);
+        message.error(`获取调解记录失败: ${err.message}`);
+      } else {
+        console.warn('[MediationBoard] 静默刷新失败:', err.message);
+      }
     } finally {
-      setLoading(false);
+      if (!silent) {
+        setLoading(false);
+      }
     }
-  };
+  }, [timeline.mediation?.id]);
   
-  // 监听Tab切换
+  // Tab激活时:首次加载 + 启动周期性轮询
   useEffect(() => {
+    isBoardMountedRef.current = true;
+
     if (activeTab === 'mediation-board') {
-      loadMediationRecords();
+      // 首次加载(带 loading 效果)
+      loadMediationRecords(false);
+
+      // 周期性静默刷新(不显示 loading)
+      const pollTimer = setInterval(() => {
+        if (isBoardMountedRef.current) {
+          loadMediationRecords(true);
+        }
+      }, BOARD_POLL_INTERVAL);
+
+      console.log('[MediationBoard] 启动周期轮询,间隔:', BOARD_POLL_INTERVAL, 'ms');
+
+      return () => {
+        clearInterval(pollTimer);
+        isBoardMountedRef.current = false;
+      };
     }
-  }, [activeTab]);
+
+    return () => { isBoardMountedRef.current = false; };
+  }, [activeTab, loadMediationRecords]);
   
   // 如果还在加载中,显示Loading状态
   if (loading) {

--
Gitblit v1.8.0