6a2adec4956d5f5eef09c2e6e16d70460dc27772..6b14010e1765842acc437ab130e5815bc623788d
2026-04-02 chengmw
feat: 优化警告提示和调解流程 UI,新增 AI 背景和图片资源
6b1401 diff | tree
2026-04-02 chengmw
feat: 优化 AI 调解实时看板功能并集成新组件
f6b958 diff | tree
7 files added
16 files modified
447 ■■■■ changed files
openspec/changes/upgrade-homepage-layout/proposal.md 8 ●●●● patch | view | raw | blame | history
openspec/changes/upgrade-homepage-layout/specs/homepage-layout/spec.md 8 ●●●● patch | view | raw | blame | history
openspec/changes/upgrade-homepage-layout/tasks.md 102 ●●●● patch | view | raw | blame | history
web-app/public/ai-bg.png patch | view | raw | blame | history
web-app/public/app_logo.png patch | view | raw | blame | history
web-app/public/in_person.png patch | view | raw | blame | history
web-app/public/join.png patch | view | raw | blame | history
web-app/public/mom.png patch | view | raw | blame | history
web-app/public/to_person.png patch | view | raw | blame | history
web-app/public/warning.png patch | view | raw | blame | history
web-app/src/App.css 88 ●●●●● patch | view | raw | blame | history
web-app/src/App.js 10 ●●●●● patch | view | raw | blame | history
web-app/src/components/common/AppHeader.jsx 2 ●●● patch | view | raw | blame | history
web-app/src/components/common/WarningAlert.css 14 ●●●● patch | view | raw | blame | history
web-app/src/components/common/WarningAlert.jsx 9 ●●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/AISuggestionCard.css 39 ●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/AISuggestionCard.jsx 3 ●●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/NegotiationProgress.css 20 ●●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/PartyInfoCard.css 26 ●●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/PartyInfoCard.jsx 28 ●●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/TabContainer.jsx 84 ●●●● patch | view | raw | blame | history
web-app/src/components/dashboard/TopSection.jsx 2 ●●● patch | view | raw | blame | history
web-app/src/services/MediationTimelineAPIService.js 4 ●●●● patch | view | raw | blame | history
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:
{
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** 返回当事人数组
---
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 整体视觉与原型图对比验收
web-app/public/ai-bg.png
web-app/public/app_logo.png
web-app/public/in_person.png
web-app/public/join.png
web-app/public/mom.png
web-app/public/to_person.png
web-app/public/warning.png
web-app/src/App.css
@@ -715,6 +715,94 @@
  font-weight: 600;
}
/* 预计调解成功率 - 新版样式 */
.success-rate-section {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 10px;
  text-align: left;
}
.success-rate-label {
  font-size: 0.85rem;
  color: #6c757d;
  margin-bottom: 8px;
}
.success-rate-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}
.success-rate-value {
  font-size: 2.2rem;
  font-weight: 700;
  color: #1a6fb8;
  line-height: 1;
}
.success-rate-yoy {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.8rem;
}
.yoy-icon-img {
  width: 12px;
  height: 7px;
}
.yoy-rate {
  color: #52c41a;
  font-weight: 600;
}
.yoy-time {
  color: #52c41a;
}
/* 进度条 */
.success-rate-progress {
  width: 100%;
}
.progress-bar-bg {
  width: 100%;
  height: 6px;
  background: #e8e8e8;
  border-radius: 3px;
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%;
  background: #1a6fb8;
  border-radius: 3px;
  transition: width 0.3s ease;
}
/* 调解数据看板 - 左右分栏布局 */
.metric-card.left-column {
  display: flex;
  flex-direction: column;
}
.metric-card.right-column {
  display: flex;
  flex-direction: column;
  padding: 0;
  background: transparent;
  gap: 0px;
}
.metric-card.right-column > *:last-child {
  margin-bottom: 0;
}
/* 模态窗口样式 */
.modal-overlay {
  display: none;
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>
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参数获取调解员信息
web-app/src/components/common/WarningAlert.css
@@ -23,10 +23,18 @@
  min-width: 0;
}
.warning-icon {
  color: #faad14;
  font-size: 1rem;
.warning-icon-img {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-right: 6px;
}
.warning-modal-icon {
  width: 16px;
  height: 16px;
  margin-right: 8px;
  vertical-align: middle;
}
.warning-label {
web-app/src/components/common/WarningAlert.jsx
@@ -5,17 +5,20 @@
import React, { useState, useEffect, useCallback } from 'react';
import { Modal, List } from 'antd';
import { WarningOutlined, RightOutlined } from '@ant-design/icons';
import { RightOutlined } from '@ant-design/icons';
import { useCaseData } from '../../contexts/CaseDataContext';
import MediationTimelineAPIService from '../../services/MediationTimelineAPIService';
import './WarningAlert.css';
// 警告图标图片
const WARNING_ICON = '/warning.png';
/**
 * 预警消息列表弹窗
 */
const WarningModal = ({ visible, warnings, onClose }) => (
  <Modal
    title={<span><WarningOutlined style={{ color: '#faad14', marginRight: 8 }} />预警消息列表</span>}
    title={<span><img src={WARNING_ICON} alt="warning" className="warning-modal-icon" />预警消息列表</span>}
    open={visible}
    onCancel={onClose}
    footer={null}
@@ -90,7 +93,7 @@
  return (
    <div className="warning-alert">
      <div className="warning-alert-content">
        <WarningOutlined className="warning-icon" />
        <img src={WARNING_ICON} alt="warning" className="warning-icon-img" />
        <span className="warning-label">预警:</span>
        <span className="warning-text">{firstWarning.content}</span>
      </div>
web-app/src/components/dashboard/AISuggestionCard.css
@@ -4,11 +4,23 @@
 */
.ai-suggestion-card {
  background: linear-gradient(135deg, #e6f4ff, #f0f7ff);
  border-radius: 8px;
  background: #1a6fb8;
  border-radius: 12px;
  padding: 14px 16px;
  margin-top: 12px;
  border-left: 4px solid #1a6fb8;
  box-shadow: 0px 4px 6px -4px rgba(0,0,0,0.10), 0px 10px 15px -3px rgba(0,0,0,0.10);
  position: relative;
  overflow: hidden;
}
.ai-suggestion-bg {
  position: absolute;
  top: 15px;
  right: 15px;
  width: 48px;
  height: 50px;
  opacity: 0.1;
  pointer-events: none;
}
.ai-suggestion-header {
@@ -20,19 +32,19 @@
.ai-suggestion-icon {
  font-size: 1rem;
  color: #1a6fb8;
  color: #fff;
}
.ai-suggestion-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: #0d4a8a;
  color: #fff;
}
.ai-suggestion-content {
  font-size: 0.85rem;
  line-height: 1.6;
  color: #333;
  color: #fff;
  max-height: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
@@ -47,11 +59,22 @@
}
.ai-suggestion-btn {
  padding: 0;
  padding: 8px 24px;
  font-size: 0.85rem;
  color: #1a6fb8;
  color: #FFFFFF;
  background: rgba(255, 255, 255, 0.9);
  border-radius: 20px;
  border: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  height: auto;
  line-height: 1.5;
  margin: 10px;
  width: 90%;
  background: rgba(255, 255, 255, 0.20);
  border-radius: 8px;
}
.ai-suggestion-btn:hover {
  color: #0d4a8a;
  background: rgba(255, 255, 255, 1);
}
web-app/src/components/dashboard/AISuggestionCard.jsx
@@ -22,6 +22,9 @@
  return (
    <div className="ai-suggestion-card">
      {/* 背景图标 */}
      <img src="/ai-bg.png" alt="" className="ai-suggestion-bg" />
      {/* 标题 */}
      <div className="ai-suggestion-header">
        <BulbOutlined className="ai-suggestion-icon" />
web-app/src/components/dashboard/NegotiationProgress.css
@@ -7,7 +7,6 @@
  background: #f8f9fa;
  border-radius: 8px;
  padding: 12px 16px;
  border-top: 3px solid #1a6fb8;
}
.negotiation-header {
@@ -40,21 +39,26 @@
.progress-dot-wrapper {
  display: flex;
  align-items: center;
  flex: 1;
  justify-content: flex-start;
}
.progress-dot-wrapper.last {
  flex: 0;
}
/* 进度点 */
/* 进度短横线 */
.progress-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  width: 16px;
  height: 4px;
  border-radius: 2px;
  background: #d9d9d9;
  flex-shrink: 0;
  transition: background 0.3s;
  margin-left: 5px;
}
.progress-dot-wrapper:first-child .progress-dot {
  margin-left: 0;
}
.progress-dot.active {
@@ -64,9 +68,9 @@
/* 连接线 */
.progress-line {
  flex: 1;
  height: 3px;
  height: 2px;
  background: #d9d9d9;
  margin: 0 2px;
  margin: 0 4px;
  transition: background 0.3s;
}
web-app/src/components/dashboard/PartyInfoCard.css
@@ -7,7 +7,7 @@
  background: #f8f9fa;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 16px;
  margin-bottom: 10px;
}
.party-info-title {
@@ -34,21 +34,28 @@
  padding: 8px;
}
/* 情绪标签 */
/* 情绪标签 - 实心红背景白字 */
.party-tag {
  position: absolute;
  top: -4px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.7rem;
  padding: 0 6px;
  line-height: 18px;
  border-radius: 4px;
  padding: 2px 8px;
  line-height: 16px;
  border-radius: 10px;
  background-color: #ff4d4f !important;
  color: white !important;
  border: none !important;
}
.party-avatar {
.party-avatar-img {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  margin-top: 12px;
  margin-bottom: 8px;
  object-fit: cover;
}
.party-role {
@@ -76,7 +83,8 @@
  padding: 0 8px;
}
.vs-icon {
  font-size: 1.5rem;
  color: #d9d9d9;
.vs-icon-img {
  width: 24px;
  height: 24px;
  object-fit: contain;
}
web-app/src/components/dashboard/PartyInfoCard.jsx
@@ -4,14 +4,17 @@
 */
import React, { useState, useEffect, useCallback } from 'react';
import { Avatar, Tag } from 'antd';
import { UserOutlined, TeamOutlined } from '@ant-design/icons';
import { Tag } from 'antd';
import { useCaseData } from '../../contexts/CaseDataContext';
import MediationTimelineAPIService from '../../services/MediationTimelineAPIService';
import './PartyInfoCard.css';
// 默认头像
const DEFAULT_AVATAR_PERSON = 'http://gz.hugeinfo.com.cn/dyh/wx414ae04ac3f10b4e/images/pngAI_logo.png';
// 申请人头像图片
const APPLICANT_AVATAR = '/in_person.png';
// 被申请人头像图片
const RESPONDENT_AVATAR = '/to_person.png';
/**
 * 根据per_type判断是申请方还是被申请方
@@ -42,8 +45,7 @@
 * 当事人信息卡片
 */
const PartyCard = ({ person, isApplicantSide }) => {
  const avatarIcon = isApplicantSide ? <UserOutlined /> : <TeamOutlined />;
  const avatarBg = isApplicantSide ? '#1a6fb8' : '#faad14';
  const avatarSrc = isApplicantSide ? APPLICANT_AVATAR : RESPONDENT_AVATAR;
  return (
    <div className={`party-card ${isApplicantSide ? 'applicant' : 'respondent'}`}>
@@ -55,11 +57,10 @@
      )}
      
      {/* 头像 */}
      <Avatar
        size={48}
        icon={avatarIcon}
        style={{ backgroundColor: avatarBg }}
        className="party-avatar"
      <img
        src={avatarSrc}
        alt={isApplicantSide ? '申请人' : '被申请人'}
        className="party-avatar-img"
      />
      
      {/* 角色标签 */}
@@ -73,12 +74,15 @@
  );
};
// VS分隔符图片
const VS_ICON = '/join.png';
/**
 * VS分隔符
 */
const VSSeparator = () => (
  <div className="vs-separator">
    <span className="vs-icon">⚖</span>
    <img src={VS_ICON} alt="VS" className="vs-icon-img" />
  </div>
);
web-app/src/components/dashboard/TabContainer.jsx
@@ -1,6 +1,6 @@
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';
@@ -8,6 +8,11 @@
import { message, Spin, Tag, Modal, Button, Input, Image } from 'antd';
import { PhoneOutlined } 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,53 @@
});
/**
 * 获取成功率同比数据
 */
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 successRateValue = (mediation.success_rate || 0) * 100;
  // 获取同比数据
  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 +156,36 @@
              {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 className="metric-card right-column">
        {/* 申请双方信息 */}
        <PartyInfoCard />
        {/* 预计调解成功率 */}
        <div className="success-rate-section">
          <div className="success-rate-label">预计调解成功率</div>
          <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 style={{ marginTop: 15, fontSize: '0.9rem', color: 'var(--gray-color)' }}>
              协商沟通:<span style={{ color: 'var(--dark-color)', fontWeight: 600 }}>{roundCount}</span>
          </div>
          <div className="success-rate-progress">
            <div className="progress-bar-bg">
              <div className="progress-bar-fill" style={{ width: `${successRateValue}%` }}></div>
            </div>
          </div>
        </div>
        {/* 协商沟通进度 */}
        <NegotiationProgress />
      </div>
    </div>
  );
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>
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}`);
  }
}