You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

605 lines
25 KiB
JavaScript

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

import React, { useEffect, useRef, useState } from 'react';
import { Card, Table, Tag, Space, Typography, Progress, Row, Col, Button, Input, Select } from 'antd';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './LicenseManagement.less';
import icon_echart from '@/assets/business_basic/icon_echart.svg';
const { Title } = Typography;
const { Search } = Input;
const { Option } = Select;
const LicenseManagement = () => {
const chartRef = useRef(null);
const [searchValue, setSearchValue] = useState('');
const [selectedType, setSelectedType] = useState('all');
// 图表数据
const chartData = [
{ name: '安全生产许可证', value: 35, itemStyle: { color: '#3C7DFF' } },
{ name: '安全评估报告', value: 25, itemStyle: { color: '#FF8800' } },
{ name: '安全三同时材料', value: 20, itemStyle: { color: '#FF3E48' } },
{ name: '施工资质证书', value: 15, itemStyle: { color: '#FFC403' } },
{ name: '应急预案', value: 10, itemStyle: { color: '#22C55E' } },
{ name: '其他', value: 5, itemStyle: { color: '#31BCFF' } }
];
// 初始化图表
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'horizontal',
bottom: 0,
left: 'center',
itemWidth: 14,
itemHeight: 4,
itemGap: 10,
textStyle: {
fontSize: 12,
color: '#333',
width: 100
},
formatter: function (name) {
return name;
},
data: (() => {
// 找到最长的名称长度
const maxLength = Math.max(...chartData.map(item => item.name.length));
// 将所有名称填充到相同长度
return chartData.map(item => {
const paddingLength = maxLength - item.name.length;
return item.name + ' '.repeat(paddingLength);
});
})()
},
series: [
{
name: '证件类型分布',
type: 'pie',
radius: ['20%', '65%'],
center: ['50%', '38%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
// color:"red",/
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '16',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: (() => {
// 找到最长的名称长度
const maxLength = Math.max(...chartData.map(item => item.name.length));
// 将所有名称填充到相同长度
return chartData.map(item => ({
...item,
name: item.name + ' '.repeat(maxLength - item.name.length)
}));
})()
}
]
};
chart.setOption(option);
// 响应式处理
const handleResize = () => {
chart.resize();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
chart.dispose();
};
}
}, []);
// 表格数据
const tableData = [
{
key: '1',
no: '01',
name: '安全生产许可证',
type: '资质证书',
id: 'HQ-XF-01-001',
authority: '应急管理部',
validUntil: '2025-09-10',
status: '已过期',
statusType: 'error'
},
{
key: '2',
no: '02',
name: '安全预评估报告',
type: '安全三同时',
id: 'HQ-XF-02-015',
authority: '第三方评估机构',
validUntil: '2025-09-10',
status: '有效',
statusType: 'warning'
},
{
key: '3',
no: '03',
name: '施工资质证书',
type: '资质证书',
id: 'HQ-XF-03-007',
authority: '3设计院',
validUntil: '2025-09-10',
status: '有效',
statusType: 'success'
},
{
key: '4',
no: '04',
name: '安全标准化证书',
type: '资质证书',
id: 'HQ-XF-03-007',
authority: '第三方评估机构',
validUntil: '2025-09-10',
status: '有效',
statusType: 'success'
},
{
key: '5',
no: '05',
name: '消防验收合格证',
type: '消防证书',
id: 'HQ-XF-05-012',
authority: '消防局',
validUntil: '2026-03-15',
status: '有效',
statusType: 'success'
},
{
key: '6',
no: '06',
name: '职业健康安全管理体系认证',
type: '管理体系认证',
id: 'HQ-XF-06-008',
authority: '认证机构',
validUntil: '2026-06-20',
status: '有效',
statusType: 'success'
},
{
key: '7',
no: '07',
name: '环境管理体系认证',
type: '管理体系认证',
id: 'HQ-XF-07-009',
authority: '认证机构',
validUntil: '2026-08-25',
status: '有效',
statusType: 'success'
},
{
key: '8',
no: '08',
name: '特种设备使用登记证',
type: '特种设备证书',
id: 'HQ-XF-08-011',
authority: '质量技术监督局',
validUntil: '2026-12-10',
status: '有效',
statusType: 'success'
},
{
key: '9',
no: '09',
name: '危险化学品经营许可证',
type: '经营许可证',
id: 'HQ-XF-09-013',
authority: '应急管理局',
validUntil: '2027-01-30',
status: '有效',
statusType: 'success'
},
{
key: '10',
no: '10',
name: '辐射安全许可证',
type: '辐射安全证书',
id: 'HQ-XF-10-014',
authority: '生态环境部',
validUntil: '2027-04-18',
status: '有效',
statusType: 'success'
}
];
// 表格列定义
const columns = [
{
title: '编号',
dataIndex: 'no',
key: 'no',
width: 80,
},
{
title: '证照名称',
dataIndex: 'name',
key: 'name',
width: 150,
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
width: 120,
},
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 150,
},
{
title: '发证机关',
dataIndex: 'authority',
key: 'authority',
width: 150,
},
{
title: '有效期至',
dataIndex: 'validUntil',
key: 'validUntil',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
render: (text, record) => {
const getStatusStyle = (status) => {
if (status === '有效') {
return {
color: '#44BB5F',
backgroundColor: '#D8F7DE',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
display: 'inline-block'
};
} else if (status === '即将到期') {
return {
color: '#FF8800',
backgroundColor: '#FFF3E9',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
display: 'inline-block'
};
} else if (status === '已过期') {
return {
color: '#FF3E48',
backgroundColor: '#FFE0E2',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
display: 'inline-block'
};
}
return {};
};
return (
<span style={getStatusStyle(text)}>
{text}
</span>
);
}
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
width: 120,
render: (text, record) => {
const handleEdit = (record) => {
console.log('编辑记录:', record);
};
const handleDelete = (record) => {
console.log('删除记录:', record);
};
return (
<div style={{
display: 'flex',
gap: '8px',
justifyContent: 'center',
alignItems: 'center'
}}>
<Button
onClick={() => handleEdit(record)}
style={{
color: '#2E4CD4',
backgroundColor: 'transparent',
// borderColor: '#E6E9FB',
fontSize: '12px',
height: '28px',
padding: '0 12px'
}}
>
更新
</Button>
<Button
onClick={() => handleDelete(record)}
style={{
color: '#2E4CD4',
backgroundColor: 'transparent',
// borderColor: '#E6E9FB',
fontSize: '12px',
height: '28px',
padding: '0 12px'
}}
>
查看
</Button>
</div>
);
}
},
];
return (
<div className={styles.licenseManagementContainer}>
<div className={styles.topSectionContainer}>
<div className={styles.firstBlock}>
<div className={styles.chartHeader}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>证件类型分布</span>
</div>
<div className={styles.chartContainer}>
<div ref={chartRef} className={styles.chart}></div>
</div>
</div>
<div className={styles.secondBlock}>
<div className={styles.chartHeader}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>证件状态概览</span>
</div>
<div className={styles.chartContainer}>
{/* 上半部分:进度条和百分比 */}
<div className={styles.progressSection}>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>有效证照</div>
<div className={styles.progressWrapper}>
<Progress
percent={50}
strokeColor="#3C7DFF"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>50%</span>
</div>
</div>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>即将到期</div>
<div className={styles.progressWrapper}>
<Progress
percent={15}
strokeColor="#FFC403"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>15%</span>
</div>
</div>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>已过期</div>
<div className={styles.progressWrapper}>
<Progress
percent={20}
strokeColor="#FF3E48"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>20%</span>
</div>
</div>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>待审核材料</div>
<div className={styles.progressWrapper}>
<Progress
percent={15}
strokeColor="#FF8800"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>15%</span>
</div>
</div>
</div>
{/* 下半部分:数字统计 */}
<div className={styles.statsSection}>
<Row gutter={[16, 16]}>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#3C7DFF' }}>42</div>
<div className={styles.statLabel}>总证照数</div>
</div>
</Col>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#FFC403' }}>8</div>
<div className={styles.statLabel}>即将过期</div>
</div>
</Col>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#FF3E48' }}>6</div>
<div className={styles.statLabel}>已过期</div>
</div>
</Col>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#FF8800' }}>6</div>
<div className={styles.statLabel}>待审核材料</div>
</div>
</Col>
</Row>
</div>
</div>
</div>
<div className={styles.thirdBlock}>
<div className={styles.chartHeader}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>临期预警</span>
</div>
<div className={styles.chartContainer}>
{/* 透明块容器 */}
<div className={styles.transparentBlock}>
{/* 四个垂直分布的卡片 */}
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>安全生产许可证</div>
<div className={styles.licenseNumber}>编号: AQXK-2023-0582</div>
</div>
<div className={styles.expiryTag}>
<span className={styles.expiryText}>15天后到期</span>
</div>
</div>
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>安全评估报告</div>
<div className={styles.licenseNumber}>编号: AQPG-2023-0125</div>
</div>
<div className={styles.expiryTag}>
<span className={styles.expiryText}>30天后到期</span>
</div>
</div>
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>施工资质证书</div>
<div className={styles.licenseNumber}>编号: SGZZ-2023-0089</div>
</div>
<div className={styles.expiryTag} style={{ backgroundColor: '#FFE0E2' }}>
<span className={styles.expiryText} style={{ color: '#FF2526' }}>7天后到期</span>
</div>
</div>
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>应急预案</div>
<div className={styles.licenseNumber}>编号: YJYA-2023-0045</div>
</div>
<div className={styles.expiryTag} style={{ backgroundColor: '#FFE0E2' }}>
<span className={styles.expiryText} style={{ color: '#FF2526' }}>4天后到期</span>
</div>
</div>
</div>
</div>
</div>
</div>
{/* 证照列表区域 */}
<div className={styles.listCard}>
<div className={styles.chartHeader}>
<div className={styles.headerLeft}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>证照列表</span>
</div>
<div className={styles.headerRight}>
<Search
placeholder="搜索证照名称或编号..."
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onSearch={(value) => console.log('搜索:', value)}
className={styles.searchInput}
/>
<Select
value={selectedType}
onChange={setSelectedType}
className={styles.typeSelector}
>
<Option value="all">全部类型</Option>
<Option value="safety">安全生产许可证</Option>
<Option value="assessment">安全评估报告</Option>
<Option value="construction">施工资质证书</Option>
<Option value="emergency">应急预案</Option>
<Option value="other">其他</Option>
</Select>
<Button
type="primary"
className={styles.addButton}
onClick={() => console.log('新增证照')}
>
新增证照
</Button>
</div>
</div>
<StandardTable
columns={columns}
data={{
list: tableData, // ======== 表格数据列表 ========
pagination: { // ======== 分页配置 ========
currentPage: 1, // ======== 当前页码 ========
pageSize: 5, // ======== 每页显示5条数据 ========
total: tableData.length, // ======== 总数据条数 ========
} // ======== 分页配置结束 ========
}} // ======== 数据对象结束 ========
selectedRows={[]} // ======== 选中的行数据,初始为空数组 ========
onSelectRow={() => { }} // ======== 行选择事件处理函数 ========
onChange={() => { }} // ======== 表格变化事件处理函数 ========
pagination={{
currentPage: 1,
pageSize: 5,
total: tableData.length,
showSizeChanger: false,
showQuickJumper: true,
showTotal: (total, range) =>
`${total}`,
locale: {
jump_to: '前往',
page: '页',
items_per_page: '条/页',
}
}}
/>
</div>
</div>
);
};
export default LicenseManagement;