From c17c80a5d4b4ceb8f4347e43da14c0c31c0615f7 Mon Sep 17 00:00:00 2001
From: tony.cheng <chengmingwei_1984122@126.com>
Date: Thu, 05 Feb 2026 18:27:19 +0800
Subject: [PATCH] feat: 完成法律条文查询API对接及优化

---
 web-app/src/components/tools/LawSearchContent.jsx |  458 ++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 308 insertions(+), 150 deletions(-)

diff --git a/web-app/src/components/tools/LawSearchContent.jsx b/web-app/src/components/tools/LawSearchContent.jsx
index 24c9420..04ab057 100644
--- a/web-app/src/components/tools/LawSearchContent.jsx
+++ b/web-app/src/components/tools/LawSearchContent.jsx
@@ -1,123 +1,196 @@
-import React, { useState } from 'react';
-import { Input, DatePicker, Button, Pagination, Spin } from 'antd';
+import React, { useState, useEffect } from 'react';
+import { Input, DatePicker, Button, Pagination, Spin, Modal, message } from 'antd';
 import { SearchOutlined, RedoOutlined } from '@ant-design/icons';
+import LawAPIService from '../../services/LawAPIService';
+import LawDetailContent from './LawDetailContent';
 import './LawSearchContent.css';
 
-// Mock数据 - 按原型格式
-const mockLawList = [
-  {
-    id: 'law-001',
-    lawName: '中华人民共和国民法典',
-    effectLevel: '法律',
-    status: '有效',
-    org: '全国人民代表大会',
-    publishDate: '2020-05-28',
-    effectiveDate: '2021-01-01',
-    articles: [
-      { number: '第五条', content: '民事主体从事民事活动,应当遵循自愿原则,按照自己的意思设立、变更、终止民事法律关系。' },
-      { number: '第六条', content: '民事主体从事民事活动,应当遵循公平原则,合理确定各方的权利和义务。' },
-    ],
-  },
-  {
-    id: 'law-002',
-    lawName: '中华人民共和国劳动法',
-    effectLevel: '法律',
-    status: '有效',
-    org: '全国人民代表大会',
-    publishDate: '2018-12-29',
-    effectiveDate: '2019-01-01',
-    articles: [
-      { number: '第五十条', content: '工资应当以货币形式按月支付给劳动者本人。不得克扣或者无故拖欠劳动者的工资。' },
-      { number: '第九十一条', content: '用人单位有下列侵害劳动者合法权益情形之一的,由劳动行政部门责令支付劳动者的工资报酬、经济补偿,并可以责令支付赔偿金。' },
-    ],
-  },
-  {
-    id: 'law-003',
-    lawName: '中华人民共和国劳动合同法',
-    effectLevel: '法律',
-    status: '有效',
-    org: '全国人民代表大会常务委员会',
-    publishDate: '2012-12-28',
-    effectiveDate: '2013-07-01',
-    articles: [
-      { number: '第三十条', content: '用人单位应当按照劳动合同约定和国家规定,向劳动者及时足额支付劳动报酬。' },
-    ],
-  },
-  {
-    id: 'law-004',
-    lawName: '中华人民共和国社会保险法',
-    effectLevel: '法律',
-    status: '有效',
-    org: '全国人民代表大会常务委员会',
-    publishDate: '2018-12-29',
-    effectiveDate: '2019-01-01',
-    articles: [],
-  },
-  {
-    id: 'law-005',
-    lawName: '最高人民法院关于审理劳动争议案件适用法律问题的解释(一)',
-    effectLevel: '司法解释',
-    status: '有效',
-    org: '最高人民法院',
-    publishDate: '2020-12-29',
-    effectiveDate: '2021-01-01',
-    articles: [],
-  },
-];
-
-// 筛选器配置
-const filterConfig = {
-  lawNature: [
-    { label: '法律', count: 256, checked: true },
-    { label: '法律解释', count: 33, checked: false },
-    { label: '有关法律问题和重大问题的决定', count: 12, checked: false },
-    { label: '修改、废止的决定', count: 8, checked: false },
-  ],
-  org: [
-    { label: '全国人民代表大会', count: 256, checked: true },
-    { label: '全国人民代表大会常务委员会', count: 256, checked: false },
-    { label: '国务院', count: 21, checked: false },
-    { label: '地方各级人民代表大会', count: 8, checked: false },
-    { label: '人民法院', count: 21, checked: false },
-    { label: '人民检察院', count: 8, checked: false },
-  ],
-  validity: [
-    { label: '有效', count: 33, checked: true },
-    { label: '尚未生效', count: 33, checked: false },
-    { label: '已废止', count: 12, checked: false },
-    { label: '已修改', count: 21, checked: false },
-    { label: '其他', count: 8, checked: false },
-  ],
+/**
+ * 格式化日期为 YYYY年MM月DD日 格式
+ * @param {string} dateStr - 日期字符串 YYYY-MM-DD
+ * @returns {string} 格式化后的日期
+ */
+const formatDate = (dateStr) => {
+  if (!dateStr) return '-';
+  try {
+    const date = new Date(dateStr);
+    const year = date.getFullYear();
+    const month = date.getMonth() + 1;
+    const day = date.getDate();
+    return `${year}年${month}月${day}日`;
+  } catch (error) {
+    return dateStr;
+  }
 };
 
 /**
  * 法条搜索内容组件(用于弹窗内显示)- 与原型 law_search.html 保持一致
+ * 已对接API:getHotLaws, getCategoryStatistics, searchLaws, getLawProvisions
  */
 const LawSearchContent = () => {
+  // 基本状态
   const [loading, setLoading] = useState(false);
-  const [keyword, setKeyword] = useState('民事主体从事民事活动');
-  const [list, setList] = useState(mockLawList);
-  const [activeId, setActiveId] = useState('law-001');
-  const [filters, setFilters] = useState(filterConfig);
+  const [keyword, setKeyword] = useState('');
+  const [publishStart, setPublishStart] = useState(null);
+  const [publishEnd, setPublishEnd] = useState(null);
+  
+  // 列表数据
+  const [list, setList] = useState([]);
+  const [total, setTotal] = useState(0);
+  const [pageSize, setPageSize] = useState(10);
   const [currentPage, setCurrentPage] = useState(1);
-  const pageSize = 10;
-  const total = 256;
+  
+  // 筛选器数据
+  const [filters, setFilters] = useState({
+    lawNature: [],
+    org: [],
+    validity: []
+  });
+  
+  // 展开与详情
+  const [activeId, setActiveId] = useState(null);
+  const [expandedProvisions, setExpandedProvisions] = useState({}); // 缓存已加载的法条内容
+  const [detailVisible, setDetailVisible] = useState(false);
+  const [selectedLawId, setSelectedLawId] = useState(null);
 
-  const handleSearch = () => {
-    setLoading(true);
-    setTimeout(() => {
-      setList(mockLawList);
-      setLoading(false);
-    }, 300);
+  // 分类统计数据处理函数
+  const processCategory = (data, code) => {
+    return data
+      .filter(item => Number(item.code) === code)  // 统一转为数字比较
+      .sort((a, b) => b.count - a.count)
+      .map((item, index) => ({
+        label: item.name,
+        value: item.value,
+        count: item.count,
+        checked: index === 0  // 默认勾选第一项
+      }));
   };
 
+  // 初始化数据加载
+  useEffect(() => {
+    const loadInitialData = async () => {
+      setLoading(true);
+      try {
+        const [hotLawsRes, categoryRes] = await Promise.all([
+          LawAPIService.getHotLaws(10),
+          LawAPIService.getCategoryStatistics()
+        ]);
+        
+        // 处理法律列表
+        setList(hotLawsRes.data?.data || []);
+        setTotal(hotLawsRes.data?.total || 0);
+        setPageSize(hotLawsRes.data?.size || 10);
+        
+        // 处理分类统计
+        const categories = categoryRes.data || [];
+        console.log('分类统计原始数据:', categories);
+        
+        const processedFilters = {
+          lawNature: processCategory(categories, 102),
+          org: processCategory(categories, 101),
+          validity: processCategory(categories, 103)
+        };
+        
+        console.log('处理后的筛选器数据:', processedFilters);
+        setFilters(processedFilters);
+      } catch (error) {
+        message.error('数据加载失败,请刷新重试');
+        console.error('初始化数据加载失败:', error);
+      } finally {
+        setLoading(false);
+      }
+    };
+    
+    loadInitialData();
+  }, []);
+
+  // 构建查询参数
+  const buildSearchParams = () => {
+    const params = {
+      page: currentPage,
+      size: pageSize
+    };
+    
+    // 关键词(非空才传)
+    if (keyword.trim()) {
+      params.keyword = keyword.trim();
+    }
+    
+    // 公布日期(两个都有值才传)
+    if (publishStart && publishEnd) {
+      params.publishStart = publishStart.format('YYYY-MM-DD');
+      params.publishEnd = publishEnd.format('YYYY-MM-DD');
+    }
+    
+    // 法律性质(多个用逗号拼接,传名称name)
+    const selectedNatures = filters.lawNature
+      .filter(item => item.checked)
+      .map(item => item.label)
+      .join(',');
+    if (selectedNatures) {
+      params.lawNatures = selectedNatures;
+    }
+    
+    // 制定机关(多个用逗号拼接,传名称name)
+    const selectedAuthorities = filters.org
+      .filter(item => item.checked)
+      .map(item => item.label)
+      .join(',');
+    if (selectedAuthorities) {
+      params.authorities = selectedAuthorities;
+    }
+    
+    // 时效性(多个用逗号拼接,传名称name)
+    const selectedValidities = filters.validity
+      .filter(item => item.checked)
+      .map(item => item.label)
+      .join(',');
+    if (selectedValidities) {
+      params.validities = selectedValidities;
+    }
+    
+    return params;
+  };
+
+  // 查询功能
+  const handleSearch = async () => {
+    setLoading(true);
+    try {
+      const params = buildSearchParams();
+      const response = await LawAPIService.searchLaws(params);
+      
+      setList(response.data?.data || []);
+      setTotal(response.data?.total || 0);
+      setPageSize(response.data?.size || 10);
+      
+      // 清空已展开的法条缓存
+      setExpandedProvisions({});
+      setActiveId(null);
+    } catch (error) {
+      message.error('查询失败,请重试');
+      console.error('查询失败:', error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 重置条件
   const handleReset = () => {
     setKeyword('');
-    setFilters(filterConfig);
+    setPublishStart(null);
+    setPublishEnd(null);
+    // 重新加载初始化筛选器(每个第一项默认勾选)
+    setFilters(prev => ({
+      lawNature: prev.lawNature.map((item, index) => ({ ...item, checked: index === 0 })),
+      org: prev.org.map((item, index) => ({ ...item, checked: index === 0 })),
+      validity: prev.validity.map((item, index) => ({ ...item, checked: index === 0 }))
+    }));
+    setCurrentPage(1);
   };
 
+  // 切换筛选器
   const toggleFilter = (category, index) => {
-    setFilters(prev => {
+    setFilters((prev) => {
       const newFilters = { ...prev };
       newFilters[category] = [...prev[category]];
       newFilters[category][index] = {
@@ -126,6 +199,48 @@
       };
       return newFilters;
     });
+  };
+
+  // 点击法律卡片
+  const handleLawItemClick = async (law) => {
+    const lawInfoId = law.law_info_id;
+    
+    if (activeId === lawInfoId) {
+      // 已展开状态,弹出详情
+      setSelectedLawId(law.law_original_info_id);
+      setDetailVisible(true);
+    } else {
+      // 首次展开,加载法条内容
+      setActiveId(lawInfoId);
+      
+      if (!expandedProvisions[lawInfoId]) {
+        try {
+          const response = await LawAPIService.getLawProvisions(lawInfoId);
+          const provisions = response.data || [];
+          
+          setExpandedProvisions(prev => ({
+            ...prev,
+            [lawInfoId]: provisions
+          }));
+          
+          // 如果法条数据为空,直接打开详情弹窗
+          if (!provisions || provisions.length === 0) {
+            setSelectedLawId(law.law_original_info_id);
+            setDetailVisible(true);
+          }
+        } catch (error) {
+          message.error('法条内容加载失败');
+          console.error('法条加载失败:', error);
+        }
+      }
+    }
+  };
+
+  // 分页切换
+  const handlePageChange = (page) => {
+    setCurrentPage(page);
+    // 重新查询,保持当前筛选条件
+    handleSearch();
   };
 
   return (
@@ -148,9 +263,19 @@
           <div className="law-search-form-group">
             <label className="law-search-form-label">公布日期</label>
             <div className="law-search-date-input-group">
-              <DatePicker placeholder="开始日期" style={{ flex: 1 }} />
+              <DatePicker 
+                value={publishStart}
+                onChange={(date) => setPublishStart(date)}
+                placeholder="开始日期" 
+                style={{ flex: 1 }} 
+              />
               <span className="law-search-date-separator">至</span>
-              <DatePicker placeholder="结束日期" style={{ flex: 1 }} />
+              <DatePicker 
+                value={publishEnd}
+                onChange={(date) => setPublishEnd(date)}
+                placeholder="结束日期" 
+                style={{ flex: 1 }} 
+              />
             </div>
           </div>
           <div className="law-search-form-group full-width">
@@ -173,18 +298,24 @@
           <div className="law-search-filter-category">
             <h3 className="law-search-filter-title">法律性质</h3>
             <div className="law-search-filter-list">
-              {filters.lawNature.map((item, index) => (
-                <div
-                  key={index}
-                  className="law-search-filter-item"
-                  onClick={() => toggleFilter('lawNature', index)}
-                >
-                  <div className={`law-search-filter-checkbox ${item.checked ? 'checked' : ''}`}>
-                    {item.checked && <i className="fas fa-check"></i>}
+              {filters.lawNature && filters.lawNature.length > 0 ? (
+                filters.lawNature.map((item, index) => (
+                  <div
+                    key={index}
+                    className="law-search-filter-item"
+                    onClick={() => toggleFilter('lawNature', index)}
+                  >
+                    <div className={`law-search-filter-checkbox ${item.checked ? 'checked' : ''}`}>
+                      {item.checked && <i className="fas fa-check"></i>}
+                    </div>
+                    <span>
+                      {item.label} ({item.count})
+                    </span>
                   </div>
-                  <span>{item.label} ({item.count})</span>
-                </div>
-              ))}
+                ))
+              ) : (
+                <div className="law-search-filter-empty">暂无数据</div>
+              )}
             </div>
           </div>
 
@@ -192,18 +323,20 @@
           <div className="law-search-filter-category">
             <h3 className="law-search-filter-title">制定机关</h3>
             <div className="law-search-filter-list">
-              {filters.org.map((item, index) => (
-                <div
-                  key={index}
-                  className="law-search-filter-item"
-                  onClick={() => toggleFilter('org', index)}
-                >
-                  <div className={`law-search-filter-checkbox ${item.checked ? 'checked' : ''}`}>
-                    {item.checked && <i className="fas fa-check"></i>}
+              {filters.org && filters.org.length > 0 ? (
+                filters.org.map((item, index) => (
+                  <div key={index} className="law-search-filter-item" onClick={() => toggleFilter('org', index)}>
+                    <div className={`law-search-filter-checkbox ${item.checked ? 'checked' : ''}`}>
+                      {item.checked && <i className="fas fa-check"></i>}
+                    </div>
+                    <span>
+                      {item.label} ({item.count})
+                    </span>
                   </div>
-                  <span>{item.label} ({item.count})</span>
-                </div>
-              ))}
+                ))
+              ) : (
+                <div className="law-search-filter-empty">暂无数据</div>
+              )}
             </div>
           </div>
 
@@ -211,18 +344,24 @@
           <div className="law-search-filter-category">
             <h3 className="law-search-filter-title">时效性</h3>
             <div className="law-search-filter-list">
-              {filters.validity.map((item, index) => (
-                <div
-                  key={index}
-                  className="law-search-filter-item"
-                  onClick={() => toggleFilter('validity', index)}
-                >
-                  <div className={`law-search-filter-checkbox ${item.checked ? 'checked' : ''}`}>
-                    {item.checked && <i className="fas fa-check"></i>}
+              {filters.validity && filters.validity.length > 0 ? (
+                filters.validity.map((item, index) => (
+                  <div
+                    key={index}
+                    className="law-search-filter-item"
+                    onClick={() => toggleFilter('validity', index)}
+                  >
+                    <div className={`law-search-filter-checkbox ${item.checked ? 'checked' : ''}`}>
+                      {item.checked && <i className="fas fa-check"></i>}
+                    </div>
+                    <span>
+                      {item.label} ({item.count})
+                    </span>
                   </div>
-                  <span>{item.label} ({item.count})</span>
-                </div>
-              ))}
+                ))
+              ) : (
+                <div className="law-search-filter-empty">暂无数据</div>
+              )}
             </div>
           </div>
         </div>
@@ -239,40 +378,40 @@
           <div className="law-search-laws-list">
             {list.map((law) => (
               <div
-                key={law.id}
-                className={`law-search-law-item ${activeId === law.id ? 'active' : ''}`}
-                onClick={() => setActiveId(law.id)}
+                key={law.law_info_id}
+                className={`law-search-law-item ${activeId === law.law_info_id ? 'active' : ''}`}
+                onClick={() => handleLawItemClick(law)}
               >
-                <h3 className="law-search-law-title">{law.lawName}</h3>
+                <h3 className="law-search-law-title">{law.title}</h3>
                 <div className="law-search-law-meta">
                   <div className="law-search-law-meta-item">
                     <i className="fas fa-check-circle status-valid"></i>
-                    <span>时效性:{law.status}</span>
+                    <span>时效性:{law.validity_name || '-'}</span>
                   </div>
                   <div className="law-search-law-meta-item">
                     <i className="fas fa-layer-group"></i>
-                    <span>法律效力位阶:{law.effectLevel}</span>
+                    <span>法律效力位阶:{law.law_nature_name || '-'}</span>
                   </div>
                   <div className="law-search-law-meta-item">
                     <i className="fas fa-landmark"></i>
-                    <span>制定机关:{law.org}</span>
+                    <span>制定机关:{law.authority_name || '-'}</span>
                   </div>
                   <div className="law-search-law-meta-item">
                     <i className="far fa-calendar-alt"></i>
-                    <span>公布日期:{law.publishDate}</span>
+                    <span>公布日期:{formatDate(law.publish_time)}</span>
                   </div>
                   <div className="law-search-law-meta-item">
                     <i className="far fa-calendar-check"></i>
-                    <span>实施日期:{law.effectiveDate}</span>
+                    <span>实施日期:{formatDate(law.implementation_time)}</span>
                   </div>
                 </div>
                 {/* 条文内容 - 仅在选中状态显示 */}
-                {activeId === law.id && law.articles && law.articles.length > 0 && (
+                {activeId === law.law_info_id && expandedProvisions[law.law_info_id] && expandedProvisions[law.law_info_id].length > 0 && (
                   <div className="law-search-law-content">
-                    {law.articles.map((article, index) => (
+                    {expandedProvisions[law.law_info_id].map((article, index) => (
                       <div className="law-search-law-article" key={index}>
-                        <span className="law-search-article-number">{article.number}</span>
-                        <span>{article.content}</span>
+                        <span className="law-search-article-number">{article.provision_index || ''}</span>
+                        <span>{article.provision_text || ''}</span>
                       </div>
                     ))}
                   </div>
@@ -288,13 +427,32 @@
             current={currentPage}
             pageSize={pageSize}
             total={total}
-            onChange={(page) => setCurrentPage(page)}
+            onChange={handlePageChange}
             showSizeChanger={false}
             showQuickJumper
             showTotal={(total, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${total} 条`}
           />
         </div>
       </div>
+
+      {/* 详情弹窗 */}
+      <Modal
+        title={
+          <div style={{ fontSize: '1.2rem', fontWeight: 600 }}>
+            <i className="fas fa-book" style={{ marginRight: 10, color: '#1a6fb8' }}></i>
+            法律条文详情
+          </div>
+        }
+        visible={detailVisible}
+        onCancel={() => setDetailVisible(false)}
+        footer={null}
+        width={1000}
+        bodyStyle={{ padding: 0, height: '80vh', overflow: 'hidden' }}
+        centered
+        destroyOnClose
+      >
+        <LawDetailContent lawId={selectedLawId} />
+      </Modal>
     </div>
   );
 };

--
Gitblit v1.8.0