| | |
| | | import React, { useState, useEffect } from 'react'; |
| | | import { mockLawDetail } from '../../mocks/lawMocks'; |
| | | import { Spin, message } from 'antd'; |
| | | import LawAPIService from '../../services/LawAPIService'; |
| | | import './LawDetailContent.css'; |
| | | |
| | | /** |
| | | * 格式化日期为 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; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 从 provision_text 中提取章节列表 |
| | | * @param {string} provisionText - 法律原文纯文本 |
| | | * @returns {Array} 章节对象数组 [{id, title}, ...] |
| | | */ |
| | | const extractChapters = (provisionText) => { |
| | | if (!provisionText) return []; |
| | | |
| | | const startMarker = "\n\n目 录\n"; |
| | | const endMarker = "\n\n第一章 总则\n\n"; |
| | | |
| | | const startIndex = provisionText.indexOf(startMarker); |
| | | const endIndex = provisionText.indexOf(endMarker); |
| | | |
| | | if (startIndex === -1 || endIndex === -1) { |
| | | console.warn('未找到章节目录标记,跳过章节提取'); |
| | | return []; |
| | | } |
| | | |
| | | const tocContent = provisionText.substring( |
| | | startIndex + startMarker.length, |
| | | endIndex |
| | | ); |
| | | |
| | | const lines = tocContent.split('\n').filter(line => line.trim()); |
| | | |
| | | return lines.map((line, index) => ({ |
| | | id: `chapter-${index + 1}`, |
| | | title: line.trim() |
| | | })); |
| | | }; |
| | | |
| | | const LawDetailContent = ({ lawId }) => { |
| | | const [activeChapter, setActiveChapter] = useState('chapter1'); |
| | | const [loading, setLoading] = useState(false); |
| | | const [activeChapter, setActiveChapter] = useState(null); |
| | | const [lawDetail, setLawDetail] = useState(null); |
| | | const [chapters, setChapters] = useState([]); |
| | | |
| | | useEffect(() => { |
| | | // 模拟根据 lawId 获取详情 |
| | | setLawDetail(mockLawDetail); |
| | | const loadDetail = async () => { |
| | | if (!lawId) return; |
| | | |
| | | setLoading(true); |
| | | try { |
| | | const response = await LawAPIService.getLawOriginalDetail(lawId); |
| | | const detailData = response.data; |
| | | |
| | | // 从 provision_text 提取章节 |
| | | const extractedChapters = extractChapters(detailData.provision_text || ''); |
| | | |
| | | setLawDetail(detailData); |
| | | setChapters(extractedChapters); |
| | | |
| | | // 设置默认激活第一个章节 |
| | | if (extractedChapters.length > 0) { |
| | | setActiveChapter(extractedChapters[0].id); |
| | | } |
| | | } catch (error) { |
| | | message.error('详情加载失败'); |
| | | console.error('详情加载失败:', error); |
| | | } finally { |
| | | setLoading(false); |
| | | } |
| | | }; |
| | | |
| | | loadDetail(); |
| | | }, [lawId]); |
| | | |
| | | if (!lawDetail) return <div className="law-detail-loading">加载中...</div>; |
| | | if (loading) { |
| | | return ( |
| | | <div className="law-detail-loading" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '400px' }}> |
| | | <Spin size="large" tip="加载中..." /> |
| | | </div> |
| | | ); |
| | | } |
| | | |
| | | if (!lawDetail) { |
| | | return <div className="law-detail-loading">暂无数据</div>; |
| | | } |
| | | |
| | | const handleChapterClick = (chapterId) => { |
| | | setActiveChapter(chapterId); |
| | |
| | | return ( |
| | | <div className="law-detail-modal-body"> |
| | | {/* 章节导航 */} |
| | | {chapters.length > 0 && ( |
| | | <div className="law-detail-chapter-nav"> |
| | | <h3 className="law-detail-chapter-nav-title"> |
| | | <i className="fas fa-list-ol"></i> |
| | | 章节导航 |
| | | </h3> |
| | | <div className="law-detail-chapter-list"> |
| | | {lawDetail.chapters.map((chapter) => ( |
| | | {chapters.map((chapter) => ( |
| | | <a |
| | | key={chapter.id} |
| | | href={`#${chapter.id}`} |
| | |
| | | ))} |
| | | </div> |
| | | </div> |
| | | )} |
| | | |
| | | {/* 法律详情容器 */} |
| | | <div className="law-detail-main-container"> |
| | | <div className="law-detail-header"> |
| | | <h2 className="law-detail-title">{lawDetail.lawName}</h2> |
| | | <h2 className="law-detail-title">{lawDetail.title || '未命名法律'}</h2> |
| | | <div className="law-detail-meta-grid"> |
| | | <div className="law-detail-meta-item"> |
| | | <span className="law-detail-meta-label">时效性:</span> |
| | | <span className="law-detail-meta-value status-effective">{lawDetail.status}</span> |
| | | <span className="law-detail-meta-value status-effective">{lawDetail.validity_name || '其他'}</span> |
| | | </div> |
| | | <div className="law-detail-meta-item"> |
| | | <span className="law-detail-meta-label">法律效力位阶:</span> |
| | | <span className="law-detail-meta-value">{lawDetail.effectLevel}</span> |
| | | <span className="law-detail-meta-value">{lawDetail.law_nature_name || '-'}</span> |
| | | </div> |
| | | <div className="law-detail-meta-item"> |
| | | <span className="law-detail-meta-label">制定机关:</span> |
| | | <span className="law-detail-meta-value">{lawDetail.org}</span> |
| | | <span className="law-detail-meta-value">{lawDetail.authority_name || '-'}</span> |
| | | </div> |
| | | <div className="law-detail-meta-item"> |
| | | <span className="law-detail-meta-label">公布日期:</span> |
| | | <span className="law-detail-meta-value">{lawDetail.publishDate}</span> |
| | | <span className="law-detail-meta-value">{formatDate(lawDetail.publish_time)}</span> |
| | | </div> |
| | | <div className="law-detail-meta-item"> |
| | | <span className="law-detail-meta-label">实施日期:</span> |
| | | <span className="law-detail-meta-value">{lawDetail.effectiveDate}</span> |
| | | <span className="law-detail-meta-value">{formatDate(lawDetail.implementation_time)}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div className="law-detail-content-area"> |
| | | {lawDetail.chapters.map((chapter) => ( |
| | | <div className="law-detail-chapter-section" id={chapter.id} key={chapter.id}> |
| | | <h3 className="law-detail-chapter-title"> |
| | | <i className="fas fa-bookmark"></i> |
| | | {chapter.title} |
| | | </h3> |
| | | <div className="law-detail-articles-container"> |
| | | {chapter.articles.map((article, index) => ( |
| | | <div className="law-detail-article-item" key={index}> |
| | | <div className="law-detail-article-number">{article.number}</div> |
| | | <div className="law-detail-article-content"> |
| | | {article.content.split('\n').map((line, i) => ( |
| | | <React.Fragment key={i}> |
| | | {lawDetail.provision_text ? ( |
| | | <div className="law-detail-provision-text"> |
| | | {lawDetail.provision_text.split('\n').map((line, index) => ( |
| | | <React.Fragment key={index}> |
| | | {line} |
| | | {i < article.content.split('\n').length - 1 && <br />} |
| | | {index < lawDetail.provision_text.split('\n').length - 1 && <br />} |
| | | </React.Fragment> |
| | | ))} |
| | | </div> |
| | | </div> |
| | | ))} |
| | | </div> |
| | | </div> |
| | | ))} |
| | | ) : ( |
| | | <div className="law-detail-no-content">暂无详细内容</div> |
| | | )} |
| | | </div> |
| | | </div> |
| | | </div> |