From f6b958eedcfd1ec955a8638ec8f70d01a9c05ae6 Mon Sep 17 00:00:00 2001
From: chengmw <chengmingwei_1984122@126.com>
Date: Thu, 02 Apr 2026 11:10:14 +0800
Subject: [PATCH] feat: 优化 AI 调解实时看板功能并集成新组件

---
 web-app/src/App.js                                                     |   10 ++
 web-app/src/components/common/AppHeader.jsx                            |    2 
 web-app/src/components/dashboard/PartyInfoCard.jsx                     |    3 
 web-app/src/services/MediationTimelineAPIService.js                    |    4 
 web-app/public/app_logo.png                                            |    0 
 web-app/src/components/dashboard/TopSection.jsx                        |    2 
 web-app/src/components/dashboard/TabContainer.jsx                      |   80 +++++++++++----
 openspec/changes/upgrade-homepage-layout/tasks.md                      |  102 ++++++++++----------
 openspec/changes/upgrade-homepage-layout/specs/homepage-layout/spec.md |    8 
 openspec/changes/upgrade-homepage-layout/proposal.md                   |    8 
 web-app/src/App.css                                                    |   63 ++++++++++++
 11 files changed, 194 insertions(+), 88 deletions(-)

diff --git a/openspec/changes/upgrade-homepage-layout/proposal.md b/openspec/changes/upgrade-homepage-layout/proposal.md
index e562710..238b8d7 100644
--- a/openspec/changes/upgrade-homepage-layout/proposal.md
+++ b/openspec/changes/upgrade-homepage-layout/proposal.md
@@ -10,8 +10,8 @@
 - **修改**: 预计调解成功率组件,增加同比数据展示(如 +8% 较3小时前)
 - **新增**: 协商沟通面板组件,点线式可视化展示沟通轮次
 - **新增**: AI调解建议面板,展示调解建议内容和详情按钮
-- **新增**: 预警消息API服务 (`/api/v1/mediation-timeline/warning-notify-list/{mediation_id}`)
-- **新增**: 当事人列表API服务 (`/api/v1/mediation-timeline/person-list/{case_id}`)
+- **新增**: 预警消息API服务 (`/api/v1/mediation-timeline/v2/warning-notify-list/{mediation_id}`)
+- **新增**: 当事人列表API服务 (`/api/v1/mediation-timeline/v2/person-list/{case_id}`)
 
 ## Impact
 - **Affected specs**: homepage-layout
@@ -39,7 +39,7 @@
 
 ### 2. 预警消息API
 ```
-GET /api/v1/mediation-timeline/warning-notify-list/{mediation_id}
+GET /api/v1/mediation-timeline/v2/warning-notify-list/{mediation_id}
 
 Response:
 {
@@ -57,7 +57,7 @@
 
 ### 3. 当事人列表API
 ```
-GET /api/v1/mediation-timeline/person-list/{case_id}
+GET /api/v1/mediation-timeline/v2/person-list/{case_id}
 
 Response:
 {
diff --git a/openspec/changes/upgrade-homepage-layout/specs/homepage-layout/spec.md b/openspec/changes/upgrade-homepage-layout/specs/homepage-layout/spec.md
index ecfb403..82629a1 100644
--- a/openspec/changes/upgrade-homepage-layout/specs/homepage-layout/spec.md
+++ b/openspec/changes/upgrade-homepage-layout/specs/homepage-layout/spec.md
@@ -44,7 +44,7 @@
 
 #### Scenario: Warning API call
 - **WHEN** 页面加载且mediationId存在
-- **THEN** 调用 `/api/v1/mediation-timeline/warning-notify-list/{mediation_id}` 获取预警数据
+- **THEN** 调用 `/api/v1/mediation-timeline/v2/warning-notify-list/{mediation_id}` 获取预警数据
 
 ---
 
@@ -69,7 +69,7 @@
 
 #### Scenario: Person list API call
 - **WHEN** 页面加载且caseId存在
-- **THEN** 调用 `/api/v1/mediation-timeline/person-list/{case_id}` 获取当事人数据
+- **THEN** 调用 `/api/v1/mediation-timeline/v2/person-list/{case_id}` 获取当事人数据
 
 ---
 
@@ -137,7 +137,7 @@
 
 #### Scenario: Get warning notify list
 - **WHEN** 调用 getWarningNotifyList(mediationId)
-- **THEN** 发送GET请求到 `/api/v1/mediation-timeline/warning-notify-list/{mediationId}`
+- **THEN** 发送GET请求到 `/api/v1/mediation-timeline/v2/warning-notify-list/{mediationId}`
 - **AND** 返回预警消息数组
 
 ---
@@ -147,7 +147,7 @@
 
 #### Scenario: Get person list
 - **WHEN** 调用 getPersonList(caseId)
-- **THEN** 发送GET请求到 `/api/v1/mediation-timeline/person-list/{caseId}`
+- **THEN** 发送GET请求到 `/api/v1/mediation-timeline/v2/person-list/{caseId}`
 - **AND** 返回当事人数组
 
 ---
diff --git a/openspec/changes/upgrade-homepage-layout/tasks.md b/openspec/changes/upgrade-homepage-layout/tasks.md
index 8496b29..41f11dd 100644
--- a/openspec/changes/upgrade-homepage-layout/tasks.md
+++ b/openspec/changes/upgrade-homepage-layout/tasks.md
@@ -2,78 +2,78 @@
 
 ## 1. API服务层
 
-- [ ] 1.1 创建 `MediationTimelineAPIService.js` 服务文件
-- [ ] 1.2 实现 `getWarningNotifyList(mediationId)` 方法 - 获取预警消息列表
-- [ ] 1.3 实现 `getPersonList(caseId)` 方法 - 获取当事人列表
+- [x] 1.1 创建 `MediationTimelineAPIService.js` 服务文件
+- [x] 1.2 实现 `getWarningNotifyList(mediationId)` 方法 - 获取预警消息列表
+- [x] 1.3 实现 `getPersonList(caseId)` 方法 - 获取当事人列表
 
 ## 2. 蓝色顶部Header组件
 
-- [ ] 2.1 创建 `AppHeader.jsx` 组件
-- [ ] 2.2 实现左侧系统名称"矛盾纠纷应用"展示
-- [ ] 2.3 实现右侧通知图标 + 红色气泡消息数量
-- [ ] 2.4 实现右侧调解员信息展示(从URL参数获取trueName/unit/roleName/avatar)
-- [ ] 2.5 实现通知列表弹窗(点击通知图标触发)
-- [ ] 2.6 添加Header相关CSS样式(蓝色背景渐变)
-- [ ] 2.7 在 `App.js` 中集成 AppHeader 组件
+- [x] 2.1 创建 `AppHeader.jsx` 组件
+- [x] 2.2 实现左侧系统名称"矛盾纠纷应用"展示
+- [x] 2.3 实现右侧通知图标 + 红色气泡消息数量
+- [x] 2.4 实现右侧调解员信息展示(从URL参数获取trueName/unit/roleName/avatar)
+- [x] 2.5 实现通知列表弹窗(点击通知图标触发)
+- [x] 2.6 添加Header相关CSS样式(蓝色背景渐变)
+- [x] 2.7 在 `App.js` 中集成 AppHeader 组件
 
 ## 3. 预警提示消息组件
 
-- [ ] 3.1 创建 `WarningAlert.jsx` 组件
-- [ ] 3.2 实现浅黄色预警消息条展示(单条时直接显示)
-- [ ] 3.3 实现多条消息时右侧显示数量 + "查看更多"按钮
-- [ ] 3.4 实现预警消息详情弹窗(Modal)
-- [ ] 3.5 添加预警提示相关CSS样式
-- [ ] 3.6 在 `MediationProgress` 组件下方集成预警提示
+- [x] 3.1 创建 `WarningAlert.jsx` 组件
+- [x] 3.2 实现浅黄色预警消息条展示(单条时直接显示)
+- [x] 3.3 实现多条消息时右侧显示数量 + "查看更多"按钮
+- [x] 3.4 实现预警消息详情弹窗(Modal)
+- [x] 3.5 添加预警提示相关CSS样式
+- [x] 3.6 在 `MediationProgress` 组件下方集成预警提示
 
 ## 4. 申请双方信息组件
 
-- [ ] 4.1 创建 `PartyInfoCard.jsx` 组件
-- [ ] 4.2 实现申请人信息展示(头像 + 姓名 + 情绪标签)
-- [ ] 4.3 实现中间VS分隔符展示
-- [ ] 4.4 实现被申请人信息展示(头像 + 公司名称 + 标签)
-- [ ] 4.5 实现情绪标签样式(根据tag_style显示不同颜色)
-- [ ] 4.6 添加申请双方信息相关CSS样式
-- [ ] 4.7 在 `MediationDataBoard` 右侧区域集成组件
+- [x] 4.1 创建 `PartyInfoCard.jsx` 组件
+- [x] 4.2 实现申请人信息展示(头像 + 姓名 + 情绪标签)
+- [x] 4.3 实现中间VS分隔符展示
+- [x] 4.4 实现被申请人信息展示(头像 + 公司名称 + 标签)
+- [x] 4.5 实现情绪标签样式(根据tag_style显示不同颜色)
+- [x] 4.6 添加申请双方信息相关CSS样式
+- [x] 4.7 在 `MediationDataBoard` 右侧区域集成组件
 
 ## 5. 预计调解成功率组件改进
 
-- [ ] 5.1 修改 `MediationDataBoard` 中的成功率展示
-- [ ] 5.2 实现同比数据获取逻辑(yoy_success_rate或计算差值)
-- [ ] 5.3 实现"较X小时前"时间展示(yoy_before_hours)
-- [ ] 5.4 添加成功率同比展示样式(绿色上升箭头 + 百分比)
+- [x] 5.1 修改 `MediationDataBoard` 中的成功率展示
+- [x] 5.2 实现同比数据获取逻辑(yoy_success_rate或计算差值)
+- [x] 5.3 实现"较X小时前"时间展示(yoy_before_hours)
+- [x] 5.4 添加成功率同比展示样式(绿色上升箭头 + 百分比)
 
 ## 6. 协商沟通组件
 
-- [ ] 6.1 创建 `NegotiationProgress.jsx` 组件
-- [ ] 6.2 实现"协商沟通"标题展示
-- [ ] 6.3 实现"第N轮"文字展示
-- [ ] 6.4 实现点线式沟通进度(默认6个点线块)
-- [ ] 6.5 实现进度着色逻辑(已完成蓝色/未完成灰色)
-- [ ] 6.6 实现总次数动态计算(基于流程节点和沟通次数)
-- [ ] 6.7 添加协商沟通相关CSS样式
-- [ ] 6.8 在 `MediationDataBoard` 右侧区域集成组件
+- [x] 6.1 创建 `NegotiationProgress.jsx` 组件
+- [x] 6.2 实现"协商沟通"标题展示
+- [x] 6.3 实现"第N轮"文字展示
+- [x] 6.4 实现点线式沟通进度(默认6个点线块)
+- [x] 6.5 实现进度着色逻辑(已完成蓝色/未完成灰色)
+- [x] 6.6 实现总次数动态计算(基于流程节点和沟通次数)
+- [x] 6.7 添加协商沟通相关CSS样式
+- [x] 6.8 在 `MediationDataBoard` 右侧区域集成组件
 
 ## 7. AI调解建议面板
 
-- [ ] 7.1 创建 `AISuggestionCard.jsx` 组件
-- [ ] 7.2 实现AI建议内容展示(Mock数据)
-- [ ] 7.3 实现"查看详细策略建议"按钮
-- [ ] 7.4 实现按钮点击提示("该功能正在升级中,敬请期待!")
-- [ ] 7.5 添加AI调解建议相关CSS样式(浅蓝色背景)
-- [ ] 7.6 在诉求差距分析下方集成组件
+- [x] 7.1 创建 `AISuggestionCard.jsx` 组件
+- [x] 7.2 实现AI建议内容展示(Mock数据)
+- [x] 7.3 实现"查看详细策略建议"按钮
+- [x] 7.4 实现按钮点击提示("该功能正在升级中,敬请期待!")
+- [x] 7.5 添加AI调解建议相关CSS样式(浅蓝色背景)
+- [x] 7.6 在诉求差距分析下方集成组件
 
 ## 8. 布局调整与整合
 
-- [ ] 8.1 调整 `App.css` 整体布局适配新Header
-- [ ] 8.2 调整 `MediationDataBoard` 布局为左右两栏
-- [ ] 8.3 确保各组件响应式适配
-- [ ] 8.4 验证1:1还原原型图效果
+- [x] 8.1 调整 `App.css` 整体布局适配新Header
+- [x] 8.2 调整 `MediationDataBoard` 布局为左右两栏
+- [x] 8.3 确保各组件响应式适配
+- [x] 8.4 验证1:1还原原型图效果
 
 ## 9. 验证与测试
 
-- [ ] 9.1 验证Header通知功能正常
-- [ ] 9.2 验证预警消息API集成正常
-- [ ] 9.3 验证当事人列表API集成正常
-- [ ] 9.4 验证成功率同比数据计算正确
-- [ ] 9.5 验证协商沟通进度显示正确
-- [ ] 9.6 整体视觉与原型图对比验收
+- [x] 9.1 验证Header通知功能正常
+- [x] 9.2 验证预警消息API集成正常
+- [x] 9.3 验证当事人列表API集成正常
+- [x] 9.4 验证成功率同比数据计算正确
+- [x] 9.5 验证协商沟通进度显示正确
+- [x] 9.6 整体视觉与原型图对比验收
diff --git a/web-app/public/app_logo.png b/web-app/public/app_logo.png
new file mode 100644
index 0000000..1cf3912
--- /dev/null
+++ b/web-app/public/app_logo.png
Binary files differ
diff --git a/web-app/src/App.css b/web-app/src/App.css
index 84b67ec..2afd4ed 100644
--- a/web-app/src/App.css
+++ b/web-app/src/App.css
@@ -715,6 +715,69 @@
   font-weight: 600;
 }
 
+/* 预计调解成功率 - 新版样式 */
+.success-rate-section {
+  background: #f8f9fa;
+  border-radius: 8px;
+  padding: 14px 16px;
+  margin-bottom: 16px;
+  text-align: center;
+}
+
+.success-rate-label {
+  font-size: 0.85rem;
+  color: #6c757d;
+  margin-bottom: 8px;
+}
+
+.success-rate-value {
+  font-size: 2.2rem;
+  font-weight: 700;
+  color: #1a6fb8;
+  line-height: 1;
+  margin-bottom: 8px;
+}
+
+.success-rate-yoy {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 6px;
+  font-size: 0.8rem;
+}
+
+.yoy-icon {
+  color: #52c41a;
+  font-size: 0.75rem;
+}
+
+.yoy-rate {
+  color: #52c41a;
+  font-weight: 600;
+}
+
+.yoy-time {
+  color: #999;
+}
+
+/* 调解数据看板 - 左右分栏布局 */
+.metric-card.left-column {
+  display: flex;
+  flex-direction: column;
+}
+
+.metric-card.right-column {
+  display: flex;
+  flex-direction: column;
+  padding: 0;
+  background: transparent;
+  gap: 0;
+}
+
+.metric-card.right-column > * {
+  margin-bottom: 0;
+}
+
 /* 模态窗口样式 */
 .modal-overlay {
   display: none;
diff --git a/web-app/src/App.js b/web-app/src/App.js
index 813675c..0482c5c 100644
--- a/web-app/src/App.js
+++ b/web-app/src/App.js
@@ -16,6 +16,10 @@
 import ToolModal from './components/common/ToolModal';
 import OutboundCallWidget from './components/common/OutboundCallWidget';
 
+// 新增组件
+import AppHeader from './components/common/AppHeader';
+import WarningAlert from './components/common/WarningAlert';
+
 // 工具内容组件
 import WageCalculatorContent from './components/tools/WageCalculatorContent';
 import LawSearchContent from './components/tools/LawSearchContent';
@@ -103,6 +107,9 @@
           href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
         />
 
+        {/* 蓝色顶部Header */}
+        <AppHeader />
+
         {/* 顶部区域 */}
         <TopSection />
 
@@ -113,6 +120,9 @@
             {/* AI调解进度 */}
             <MediationProgress />
 
+            {/* 预警提示消息 */}
+            <WarningAlert />
+
             {/* 选项卡容器 */}
             <TabContainer ref={tabContainerRef} />
           </div>
diff --git a/web-app/src/components/common/AppHeader.jsx b/web-app/src/components/common/AppHeader.jsx
index abd1f39..d0fe896 100644
--- a/web-app/src/components/common/AppHeader.jsx
+++ b/web-app/src/components/common/AppHeader.jsx
@@ -10,7 +10,7 @@
 import './AppHeader.css';
 
 // 默认头像
-const DEFAULT_AVATAR = 'http://gz.hugeinfo.com.cn/dyh/wx414ae04ac3f10b4e/images/pngAI_logo.png';
+const DEFAULT_AVATAR = '/app_logo.png';
 
 /**
  * 从URL参数获取调解员信息
diff --git a/web-app/src/components/dashboard/PartyInfoCard.jsx b/web-app/src/components/dashboard/PartyInfoCard.jsx
index 495a5a7..c04a606 100644
--- a/web-app/src/components/dashboard/PartyInfoCard.jsx
+++ b/web-app/src/components/dashboard/PartyInfoCard.jsx
@@ -10,8 +10,7 @@
 import MediationTimelineAPIService from '../../services/MediationTimelineAPIService';
 import './PartyInfoCard.css';
 
-// 默认头像
-const DEFAULT_AVATAR_PERSON = 'http://gz.hugeinfo.com.cn/dyh/wx414ae04ac3f10b4e/images/pngAI_logo.png';
+
 
 /**
  * 根据per_type判断是申请方还是被申请方
diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index 90b1e5e..4e8c3a6 100644
--- a/web-app/src/components/dashboard/TabContainer.jsx
+++ b/web-app/src/components/dashboard/TabContainer.jsx
@@ -1,13 +1,18 @@
 import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
 import { useCaseData } from '../../contexts/CaseDataContext';
-import { formatDuration, formatSuccessRate, formatRoundCount } from '../../utils/stateTranslator';
+import { formatDuration, formatSuccessRate } from '../../utils/stateTranslator';
 import ProcessAPIService from '../../services/ProcessAPIService';
 import EvidenceAPIService from '../../services/EvidenceAPIService';
 import MediationAgreementAPIService from '../../services/MediationAgreementAPIService';
 import { getMergedParams } from '../../utils/urlParams';
 import { message, Spin, Tag, Modal, Button, Input, Image } from 'antd';
-import { PhoneOutlined } from '@ant-design/icons';
+import { PhoneOutlined, ArrowUpOutlined } from '@ant-design/icons';
 import { CallRecordModal } from '../call-record';
+
+// 新增组件导入
+import PartyInfoCard from './PartyInfoCard';
+import NegotiationProgress from './NegotiationProgress';
+import AISuggestionCard from './AISuggestionCard';
 
 const { TextArea } = Input;
 
@@ -89,22 +94,50 @@
 });
 
 /**
+ * 获取成功率同比数据
+ */
+const getSuccessRateYoY = (mediation) => {
+  // 优先使用API返回的同比值
+  if (mediation?.yoy_success_rate !== undefined && mediation?.yoy_success_rate !== null) {
+    return {
+      rate: mediation.yoy_success_rate,
+      hours: mediation.yoy_before_hours || 0
+    };
+  }
+  
+  // 计算同比值
+  const currentRate = mediation?.success_rate || 0;
+  const lastRate = mediation?.last_success_rate || 0;
+  const diff = (currentRate - lastRate) * 100;
+  
+  return {
+    rate: diff,
+    hours: mediation?.yoy_before_hours || 0
+  };
+};
+
+/**
  * 调解数据看板
  */
 const MediationDataBoard = () => {
   const { caseData } = useCaseData();
   const timeline = caseData || {};
+  const mediation = timeline.mediation || {};
   
   // 从 timeline 获取数据
   const gapContent = timeline.result || '暂无分歧分析';
   const updateTime = formatDuration(timeline.before_duration);
-  const successRate = formatSuccessRate(timeline.mediation?.success_rate);
-  const roundCount = formatRoundCount(timeline.mediation?.mediation_count);
+  const successRate = formatSuccessRate(mediation.success_rate);
+  
+  // 获取同比数据
+  const yoyData = getSuccessRateYoY(mediation);
+  const yoyRate = yoyData.rate >= 0 ? `+${yoyData.rate.toFixed(0)}%` : `${yoyData.rate.toFixed(0)}%`;
+  const yoyHours = yoyData.hours;
   
   return (
     <div className="mediation-metrics">
-      {/* 左侧:诉求差距分析 */}
-      <div className="metric-card">
+      {/* 左侧:诉求差距分析 + AI建议 */}
+      <div className="metric-card left-column">
         <div className="metric-title">
           <i className="fas fa-exclamation-circle"></i>
           <span>诉求差距分析</span>
@@ -120,28 +153,29 @@
               {gapContent}
             </div>
           </div>
+          {/* AI调解建议 */}
+          <AISuggestionCard />
         </div>
       </div>
 
-      {/* 右侧:调解数据 */}
-      <div className="metric-card">
-        <div className="metric-title">
-          <i className="fas fa-exchange-alt"></i>
-          <span>调解数据</span>
-        </div>
-        <div className="metric-content">
-          <div className="success-metric">
-            <div className="success-value">{successRate}</div>
-            <div className="success-label">预计调解成功概率</div>
-            <div className="success-change">
-              
-              <i className="fas fa-arrow-up"></i><span>较{updateTime} +8%</span>
-            </div>
-            <div style={{ marginTop: 15, fontSize: '0.9rem', color: 'var(--gray-color)' }}>
-              协商沟通:<span style={{ color: 'var(--dark-color)', fontWeight: 600 }}>{roundCount}</span>
-            </div>
+      {/* 右侧:申请双方 + 成功率 + 协商沟通 */}
+      <div className="metric-card right-column">
+        {/* 申请双方信息 */}
+        <PartyInfoCard />
+        
+        {/* 预计调解成功率 */}
+        <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>
         </div>
+        
+        {/* 协商沟通进度 */}
+        <NegotiationProgress />
       </div>
     </div>
   );
diff --git a/web-app/src/components/dashboard/TopSection.jsx b/web-app/src/components/dashboard/TopSection.jsx
index 984ebc1..dd03614 100644
--- a/web-app/src/components/dashboard/TopSection.jsx
+++ b/web-app/src/components/dashboard/TopSection.jsx
@@ -23,7 +23,7 @@
           <div className="title-icon">
             <img
               style={{ width: 36 }}
-              src="http://gz.hugeinfo.com.cn/dyh/wx414ae04ac3f10b4e/images/pngAI_logo.png"
+              src="/app_logo.png"
               // alt="云小调"
             />
           </div>
diff --git a/web-app/src/services/MediationTimelineAPIService.js b/web-app/src/services/MediationTimelineAPIService.js
index 40c73a9..e89f361 100644
--- a/web-app/src/services/MediationTimelineAPIService.js
+++ b/web-app/src/services/MediationTimelineAPIService.js
@@ -14,7 +14,7 @@
    * @returns {Promise<Array>} 预警消息列表
    */
   static getWarningNotifyList(mediationId) {
-    return request.get(`/api/v1/mediation-timeline/warning-notify-list/${mediationId}`);
+    return request.get(`/api/v1/mediation-timeline/v2/warning-notify-list/${mediationId}`);
   }
 
   /**
@@ -24,7 +24,7 @@
    * @returns {Promise<Array>} 当事人列表
    */
   static getPersonList(caseId) {
-    return request.get(`/api/v1/mediation-timeline/person-list/${caseId}`);
+    return request.get(`/api/v1/mediation-timeline/v2/person-list/${caseId}`);
   }
 }
 

--
Gitblit v1.8.0