| | |
| | | import React, { useState } from 'react'; |
| | | import { Input, DatePicker, Button, Pagination, Spin, Modal } 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 [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 pageSize = 10; |
| | | const total = 256; |
| | | |
| | | 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) => { |
| | | const newFilters = { ...prev }; |
| | |
| | | }); |
| | | }; |
| | | |
| | | const handleLawItemClick = (lawId) => { |
| | | if (activeId === lawId) { |
| | | // 如果已经是激活状态,再次点击弹出详情 |
| | | setSelectedLawId(lawId); |
| | | // 点击法律卡片 |
| | | const handleLawItemClick = async (law) => { |
| | | const lawInfoId = law.law_info_id; |
| | | |
| | | if (activeId === lawInfoId) { |
| | | // 已展开状态,弹出详情 |
| | | setSelectedLawId(law.law_original_info_id); |
| | | setDetailVisible(true); |
| | | } else { |
| | | setActiveId(lawId); |
| | | // 首次展开,加载法条内容 |
| | | 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 ( |
| | |
| | | <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"> |
| | |
| | | <div className="law-search-filter-category"> |
| | | <h3 className="law-search-filter-title">法律性质</h3> |
| | | <div className="law-search-filter-list"> |
| | | {filters.lawNature.map((item, index) => ( |
| | | {filters.lawNature && filters.lawNature.length > 0 ? ( |
| | | filters.lawNature.map((item, index) => ( |
| | | <div |
| | | key={index} |
| | | className="law-search-filter-item" |
| | |
| | | {item.label} ({item.count}) |
| | | </span> |
| | | </div> |
| | | ))} |
| | | )) |
| | | ) : ( |
| | | <div className="law-search-filter-empty">暂无数据</div> |
| | | )} |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | <div className="law-search-filter-category"> |
| | | <h3 className="law-search-filter-title">制定机关</h3> |
| | | <div className="law-search-filter-list"> |
| | | {filters.org.map((item, index) => ( |
| | | {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>} |
| | |
| | | {item.label} ({item.count}) |
| | | </span> |
| | | </div> |
| | | ))} |
| | | )) |
| | | ) : ( |
| | | <div className="law-search-filter-empty">暂无数据</div> |
| | | )} |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | <div className="law-search-filter-category"> |
| | | <h3 className="law-search-filter-title">时效性</h3> |
| | | <div className="law-search-filter-list"> |
| | | {filters.validity.map((item, index) => ( |
| | | {filters.validity && filters.validity.length > 0 ? ( |
| | | filters.validity.map((item, index) => ( |
| | | <div |
| | | key={index} |
| | | className="law-search-filter-item" |
| | |
| | | {item.label} ({item.count}) |
| | | </span> |
| | | </div> |
| | | ))} |
| | | )) |
| | | ) : ( |
| | | <div className="law-search-filter-empty">暂无数据</div> |
| | | )} |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div className="law-search-laws-list"> |
| | | {list.map((law) => ( |
| | | <div |
| | | key={law.id} |
| | | className={`law-search-law-item ${activeId === law.id ? 'active' : ''}`} |
| | | onClick={() => handleLawItemClick(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> |
| | |
| | | current={currentPage} |
| | | pageSize={pageSize} |
| | | total={total} |
| | | onChange={(page) => setCurrentPage(page)} |
| | | onChange={handlePageChange} |
| | | showSizeChanger={false} |
| | | showQuickJumper |
| | | showTotal={(total, range) => `第 ${range[0]}-${range[1]} 条 / 共 ${total} 条`} |