From 823cf3819f2f91adeada3707435d40b3dac8f7b4 Mon Sep 17 00:00:00 2001
From: tony.cheng <chengmingwei_1984122@126.com>
Date: Fri, 06 Feb 2026 18:15:05 +0800
Subject: [PATCH] feat: 实现证据材料审查弹窗功能

---
 web-app/src/components/tools/TypicalCaseDetailContent.jsx |  312 ++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 221 insertions(+), 91 deletions(-)

diff --git a/web-app/src/components/tools/TypicalCaseDetailContent.jsx b/web-app/src/components/tools/TypicalCaseDetailContent.jsx
index dbc8c9d..d2f94e1 100644
--- a/web-app/src/components/tools/TypicalCaseDetailContent.jsx
+++ b/web-app/src/components/tools/TypicalCaseDetailContent.jsx
@@ -1,158 +1,288 @@
-import React from 'react';
+import React, { useState, useEffect, useRef } from 'react';
+import { Button } from 'antd';
+import { UpOutlined } from '@ant-design/icons';
 import './TypicalCaseDetailContent.css';
 
 /**
- * 典型案例详情内容组件 - 与原型 case_search_detail.html 保持一致
+ * 典型案例详情内容组件 - 根据案例类型展示不同内容
+ * @param {Object} caseData - 案例数据
+ * @param {string} caseType - 案例类型 'judgment' | 'mediation'
  */
-const TypicalCaseDetailContent = ({ caseData }) => {
+const TypicalCaseDetailContent = ({ caseData, caseType }) => {
+  const [showBackToTop, setShowBackToTop] = useState(false);
+  const containerRef = useRef(null);
+
+  // 监听滚动事件 - Hooks必须在所有条件语句之前调用
+  useEffect(() => {
+    let modalBody = null;
+    
+    const handleScroll = () => {
+      if (modalBody) {
+        // 当滚动超过300px时显示返回顶部按钮
+        setShowBackToTop(modalBody.scrollTop > 300);
+      }
+    };
+
+    // 延迟获取DOM元素,确保Modal完全渲染
+    const timer = setTimeout(() => {
+      modalBody = document.querySelector('.case-detail-antd-modal .ant-modal-body');
+      if (modalBody) {
+        modalBody.addEventListener('scroll', handleScroll);
+        // 初始检查滚动位置
+        handleScroll();
+      }
+    }, 100);
+
+    return () => {
+      clearTimeout(timer);
+      if (modalBody) {
+        modalBody.removeEventListener('scroll', handleScroll);
+      }
+    };
+  }, [caseData]); // 当caseData变化时重新绑定
+
+  // 提前返回条件必须在所有Hooks之后
   if (!caseData) return <div className="case-detail-loading">加载中...</div>;
 
-  const { caseTitle, caseType, disputeType, caseNumber, court, judgmentDate, region, disputeTime, content } = caseData;
+  // 返回顶部函数
+  const scrollToTop = () => {
+    const modalBody = document.querySelector('.case-detail-antd-modal .ant-modal-body');
+    if (modalBody) {
+      modalBody.scrollTo({
+        top: 0,
+        behavior: 'smooth'
+      });
+    }
+  };
 
+  // 日期格式化函数
+  const formatDate = (dateStr) => {
+    if (!dateStr) return '';
+    const date = new Date(dateStr);
+    if (isNaN(date.getTime())) return dateStr;
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, '0');
+    const day = String(date.getDate()).padStart(2, '0');
+    return `${year}年${month}月${day}日`;
+  };
+
+  // 调解案例详情展示
+  if (caseType === 'mediation') {
+    return (
+      <div className="case-detail-container">
+        {/* 案件基本信息 */}
+        <div className="case-detail-info-section">
+          <h2 className="case-detail-info-title">案件基本信息</h2>
+          
+          {/* 第一行:案件标题 */}
+          {caseData.case_title && (
+            <div className="case-detail-info-full">
+              <span className="case-detail-info-label">案件标题:</span>
+              <span className="case-detail-info-value">{caseData.case_title}</span>
+            </div>
+          )}
+          
+          {/* 第二行:其他信息 */}
+          <div className="case-detail-info-grid">
+            {caseData.occur_time && (
+              <div className="case-detail-info-item">
+                <span className="case-detail-info-label">纠纷发生时间:</span>
+                <span className="case-detail-info-value">{formatDate(caseData.occur_time)}</span>
+              </div>
+            )}
+            {(caseData.que_prov_name || caseData.que_city_name) && (
+              <div className="case-detail-info-item">
+                <span className="case-detail-info-label">发生地点:</span>
+                <span className="case-detail-info-value">
+                  {caseData.que_prov_name || ''}/{caseData.que_city_name || ''}
+                </span>
+              </div>
+            )}
+            {caseData.case_type_first_name && (
+              <div className="case-detail-info-item">
+                <span className="case-detail-info-label">纠纷类型:</span>
+                <span className="case-detail-info-value">{caseData.case_type_first_name}</span>
+              </div>
+            )}
+          </div>
+        </div>
+
+        {/* 案件内容 */}
+        <div className="case-detail-body">
+          {/* 案例概述 */}
+          {caseData.case_des && (
+            <div className="case-detail-section">
+              <h3 className="case-detail-section-title">案例概述</h3>
+              <div className="case-detail-section-content">
+                <p>{caseData.case_des}</p>
+              </div>
+            </div>
+          )}
+
+          {/* 原告诉讼请求 */}
+          {caseData.case_claim && (
+            <div className="case-detail-section">
+              <h3 className="case-detail-section-title">原告诉讼请求</h3>
+              <div className="case-detail-section-content case-detail-plaintiff-demand">
+                <p>{caseData.case_claim}</p>
+              </div>
+            </div>
+          )}
+
+          {/* 调解结果 */}
+          {caseData.agree_content && (
+            <div className="case-detail-section">
+              <h3 className="case-detail-section-title">调解结果</h3>
+              <div className="case-detail-section-content case-detail-mediation-result">
+                <p>{caseData.agree_content}</p>
+              </div>
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  }
+
+  // 判决文书详情展示
   return (
     <div className="case-detail-container">
       {/* 案件基本信息 */}
       <div className="case-detail-info-section">
         <h2 className="case-detail-info-title">案件基本信息</h2>
+        
+        {/* 第一行:案件标题和案号 */}
+        <div className="case-detail-info-full">
+          {caseData.case_name && (
+            <>
+              <span className="case-detail-info-label">案件标题:</span>
+              <span className="case-detail-info-value">{caseData.case_name}</span>
+            </>
+          )}
+          {caseData.case_number && (
+            <>
+              <span className="case-detail-info-label" style={{ marginLeft: '30px' }}>案号:</span>
+              <span className="case-detail-info-value">{caseData.case_number}</span>
+            </>
+          )}
+        </div>
+        
+        {/* 第二行:其他信息 */}
         <div className="case-detail-info-grid">
-          <div className="case-detail-info-item">
-            <span className="case-detail-info-label">纠纷发生时间:</span>
-            <span className="case-detail-info-value">{disputeTime || '2024-4-12 12:00'}</span>
-          </div>
-          <div className="case-detail-info-item">
-            <span className="case-detail-info-label">发生地点:</span>
-            <span className="case-detail-info-value">{region || '广东省/广州市'}</span>
-          </div>
-          <div className="case-detail-info-item">
-            <span className="case-detail-info-label">纠纷类型:</span>
-            <span className="case-detail-info-value">{disputeType}</span>
-          </div>
-          <div className="case-detail-info-item">
-            <span className="case-detail-info-label">调解组织:</span>
-            <span className="case-detail-info-value">{court || '白云区新市街综治中心'}</span>
-          </div>
+          {caseData.judgment_date && (
+            <div className="case-detail-info-item">
+              <span className="case-detail-info-label">判决日期:</span>
+              <span className="case-detail-info-value">{formatDate(caseData.judgment_date)}</span>
+            </div>
+          )}
+          {caseData.court && (
+            <div className="case-detail-info-item">
+              <span className="case-detail-info-label">发生地点:</span>
+              <span className="case-detail-info-value">{caseData.court}</span>
+            </div>
+          )}
+          {caseData.case_reason && (
+            <div className="case-detail-info-item">
+              <span className="case-detail-info-label">纠纷类型:</span>
+              <span className="case-detail-info-value">{caseData.case_reason}</span>
+            </div>
+          )}
         </div>
       </div>
 
       {/* 案件内容 */}
       <div className="case-detail-body">
-        {content?.overview && (
+        {/* 案例概述 */}
+        {caseData.basic_case_info && (
           <div className="case-detail-section">
             <h3 className="case-detail-section-title">案例概述</h3>
             <div className="case-detail-section-content">
-              {content.overview.split('\n').map((para, i) => (
-                <p key={i}>{para}</p>
-              ))}
+              <p>{caseData.basic_case_info}</p>
             </div>
           </div>
         )}
 
-        {content?.plaintiffDemand && content.plaintiffDemand.length > 0 && (
+        {/* 原告介绍 */}
+        {caseData.plaintiff && (
           <div className="case-detail-section">
-            <h3 className="case-detail-section-title">原告诉讼请求</h3>
+            <h3 className="case-detail-section-title">原告介绍</h3>
             <div className="case-detail-section-content case-detail-plaintiff-demand">
-              <div className="case-detail-inner-content">
-                <p>原告{caseData.plaintiff || '黄某'}起诉请求:</p>
-                <ol>
-                  {content.plaintiffDemand.map((item, i) => (
-                    <li key={i}>{item}</li>
-                  ))}
-                </ol>
-              </div>
+              <p>{caseData.plaintiff}</p>
             </div>
           </div>
         )}
 
-        {content?.courtDecision && (
+        {/* 被告介绍 */}
+        {caseData.defendant && (
+          <div className="case-detail-section">
+            <h3 className="case-detail-section-title">被告介绍</h3>
+            <div className="case-detail-section-content">
+              <p>{caseData.defendant}</p>
+            </div>
+          </div>
+        )}
+
+        {/* 法院审理与判决 */}
+        {caseData.trial_finding && (
           <div className="case-detail-section">
             <h3 className="case-detail-section-title">法院审理与判决</h3>
             <div className="case-detail-section-content case-detail-court-decision">
-              <div className="case-detail-inner-content">
-                {content.courtDecision.split('\n').map((para, i) => (
-                  <p key={i}>{para}</p>
-                ))}
-              </div>
+              <p>{caseData.trial_finding}</p>
             </div>
           </div>
         )}
 
-        {content?.mediationBackground && (
+        {/* 审理经过 */}
+        {caseData.trial_process && (
           <div className="case-detail-section">
-            <h3 className="case-detail-section-title">调解背景</h3>
-            <div className="case-detail-section-content">
-              <p>{content.mediationBackground}</p>
-            </div>
-          </div>
-        )}
-
-        {content?.partiesPosition && (
-          <div className="case-detail-section">
-            <h3 className="case-detail-section-title">双方立场</h3>
-            <div className="case-detail-section-content">
-              <p><strong>{caseData.plaintiff || '黄某'}表示:</strong>{content.partiesPosition.plaintiff}</p>
-              <p><strong>{caseData.defendant || '郭某'}认为:</strong>{content.partiesPosition.defendant}</p>
-            </div>
-          </div>
-        )}
-
-        {content?.mediationProcess && (
-          <div className="case-detail-section">
-            <h3 className="case-detail-section-title">调解过程</h3>
+            <h3 className="case-detail-section-title">审理经过</h3>
             <div className="case-detail-section-content case-detail-mediation-process">
-              <p>{content.mediationProcess}</p>
+              <p>{caseData.trial_process}</p>
             </div>
           </div>
         )}
 
-        {content?.mediationScheme && content.mediationScheme.length > 0 && (
+        {/* 审理程序 */}
+        {caseData.trial_procedure && (
           <div className="case-detail-section">
-            <h3 className="case-detail-section-title">调解方案</h3>
+            <h3 className="case-detail-section-title">审理程序</h3>
             <div className="case-detail-section-content case-detail-mediation-scheme">
-              <p>法官提出的调解方案:</p>
-              <ul>
-                {content.mediationScheme.map((item, i) => (
-                  <li key={i}>{item}</li>
-                ))}
-              </ul>
+              <p>{caseData.trial_procedure}</p>
             </div>
           </div>
         )}
 
-        {content?.mediationResult && (
+        {/* 调解结果 */}
+        {caseData.judgment && (
           <div className="case-detail-section">
             <h3 className="case-detail-section-title">调解结果</h3>
             <div className="case-detail-section-content case-detail-mediation-result">
-              <div className="case-detail-inner-content">
-                <p>经过进一步的协商,双方最终接受了法官提出的调解方案。</p>
-                <ol>
-                  {content.mediationResult.items.map((item, i) => (
-                    <li key={i}>{item}</li>
-                  ))}
-                </ol>
-                {content.mediationResult.note && (
-                  <div className="case-detail-note">
-                    <p>{content.mediationResult.note}</p>
-                  </div>
-                )}
-              </div>
+              <p>{caseData.judgment}</p>
             </div>
           </div>
         )}
 
-        {content?.legalArticles && content.legalArticles.length > 0 && (
+        {/* 案例相关法律条文 */}
+        {caseData.legal_basis && (
           <div className="case-detail-section">
             <h3 className="case-detail-section-title">案例相关法律条文</h3>
             <div className="case-detail-section-content case-detail-legal-articles">
-              {content.legalArticles.map((article, i) => (
-                <div className="case-detail-article" key={i}>
-                  <div className="case-detail-article-title">{article.title}</div>
-                  <p>{article.content}</p>
-                </div>
-              ))}
+              <p>{caseData.legal_basis}</p>
             </div>
           </div>
         )}
       </div>
+
+      {/* 返回顶部按钮 */}
+      {showBackToTop && (
+        <Button
+          type="primary"
+          shape="circle"
+          icon={<UpOutlined />}
+          size="large"
+          className="back-to-top-btn"
+          onClick={scrollToTop}
+        />
+      )}
     </div>
   );
 };

--
Gitblit v1.8.0