| | |
| | | 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'; |
| | |
| | | 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'; |
| | | |
| | | // 新增组件导入 |
| | |
| | | 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)}%`; |
| | |
| | | {/* 预计调解成功率 */} |
| | | <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> |
| | | |
| | |
| | | })); |
| | | }; |
| | | |
| | | // 获取调解记录数据 |
| | | 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获取记录列表 |
| | |
| | | |
| | | // 格式化数据 |
| | | 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) { |