mh_jy_safe_web/src/layout/FullOther/HiddenSolve.js

579 lines
18 KiB
JavaScript
Raw Normal View History

2026-04-28 16:52:44 +08:00
// HiddenSolve.js - 隐患解决页面组件
import React from 'react';
import { Select } from 'antd';
import styles from './../fullinter.less';
import echarts from 'echarts';
const { Option } = Select;
class HiddenSolve extends React.Component {
constructor(props) {
super(props);
this.echartsInstances = {
hiddenBarChart: null, // 各公司隐患统计柱状图(使用 hiddenList
hiddenRectifyChart: null, // 隐患整改情况柱状图(使用 hiddenRectifyList
};
this.chartResizeHandlers = {};
this.isUnmounted = false;
}
// 获取公司选项(使用 props 传入的 companyData与 TrainingContent 一致)
getCompanyOptions = () => {
const { companyData } = this.props;
if (!companyData || companyData.length === 0) {
return [];
}
return companyData.map((company, index) => (
<Option key={company.ID || index} value={company.ID}>
{company.NAME}
</Option>
));
};
// 根据选中的公司ID获取公司名称用于筛选模拟数据
getSelectedCompanyName = () => {
const { companyData, selectedCompany } = this.props;
if (!selectedCompany || !companyData || companyData.length === 0) {
return null;
}
const selectedCompanyObj = companyData.find((company) => company.ID === selectedCompany);
return selectedCompanyObj ? selectedCompanyObj.NAME : null;
};
waitForElement = (elementId, maxRetries = 10) => {
return new Promise((resolve) => {
let retries = 0;
const checkInterval = setInterval(() => {
const element = document.getElementById(elementId);
if (element || retries >= maxRetries) {
clearInterval(checkInterval);
resolve(!!element);
}
retries++;
}, 50);
});
};
// 图表1: 各公司隐患统计柱状图(使用 hiddenList 数据)
renderHiddenBarChart = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('hiddenBarChart');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.hiddenBarChart) {
this.echartsInstances.hiddenBarChart.dispose();
this.echartsInstances.hiddenBarChart = null;
}
const chartDom = document.getElementById('hiddenBarChart');
if (!chartDom) return;
this.echartsInstances.hiddenBarChart = echarts.init(chartDom);
const { hiddenSubData } = this.props;
let hiddenList = hiddenSubData?.hiddenList || [];
if (hiddenList.length === 0) {
this.echartsInstances.hiddenBarChart.setOption({
title: {
2026-04-29 11:11:23 +08:00
text: '各公司累计隐患统计数据',
2026-04-28 16:52:44 +08:00
x: 'center',
y: '25%',
textStyle: { fontSize: 16, color: '#999' },
},
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: '暂无数据',
fill: '#999',
fontSize: 14,
},
},
});
return;
}
const companyNames = hiddenList.map((item) => item.companyName);
const majorCounts = hiddenList.map((item) => item.majorCount);
const generalCounts = hiddenList.map((item) => item.generalCount);
const option = {
title: {
2026-04-29 11:11:23 +08:00
text: '各公司累计隐患统计数据',
2026-04-28 16:52:44 +08:00
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
let result = `${params[0].axisValue}<br/>`;
params.forEach((param) => {
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
});
return result;
},
},
legend: {
data: ['重大隐患', '一般隐患'],
orient: 'vertical',
right: '3%',
top: '5%',
itemGap: 16,
itemWidth: 18,
itemHeight: 12,
textStyle: { color: '#000', fontSize: 14 },
},
grid: {
left: '8%',
right: '5%',
top: '18%',
bottom: '8%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: companyNames,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
textStyle: { color: '#000' },
rotate: companyNames.length > 4 ? 15 : 0,
interval: 0,
fontSize: 12,
},
},
],
yAxis: [
{
type: 'value',
show: true,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
show: true,
textStyle: { color: '#000' },
},
splitLine: { show: false },
name: '隐患数量',
nameTextStyle: { fontSize: 12 },
},
],
series: [
{
name: '重大隐患',
type: 'bar',
data: majorCounts,
itemStyle: {
normal: {
color: '#c92a2a', // 红色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#c92a2a', fontSize: 12 },
formatter: (params) => `${params.value}`,
},
barWidth: '35%',
},
{
name: '一般隐患',
type: 'bar',
data: generalCounts,
itemStyle: {
normal: {
color: '#4285F4', // 蓝色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#4285F4', fontSize: 12 },
formatter: (params) => `${params.value}`,
},
barWidth: '35%',
},
],
};
this.echartsInstances.hiddenBarChart.setOption(option);
this.setupResizeHandler('hiddenBarChart', this.renderHiddenBarChart);
};
// 图表2: 隐患整改情况柱状图(使用 hiddenRectifyList 数据)
renderHiddenRectifyChart = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('hiddenRectifyChart');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.hiddenRectifyChart) {
this.echartsInstances.hiddenRectifyChart.dispose();
this.echartsInstances.hiddenRectifyChart = null;
}
const chartDom = document.getElementById('hiddenRectifyChart');
if (!chartDom) return;
this.echartsInstances.hiddenRectifyChart = echarts.init(chartDom);
const { hiddenSubData } = this.props;
let hiddenRectifyList = hiddenSubData?.hiddenRectifyList || [];
if (hiddenRectifyList.length === 0) {
this.echartsInstances.hiddenRectifyChart.setOption({
title: {
2026-04-29 11:11:23 +08:00
text: '当月各公司隐患统计数据',
2026-04-28 16:52:44 +08:00
x: 'center',
y: '25%',
textStyle: { fontSize: 16, color: '#999' },
},
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: '暂无数据',
fill: '#999',
fontSize: 14,
},
},
});
return;
}
const companyNames = hiddenRectifyList.map((item) => item.companyName);
const majorTotal = hiddenRectifyList.map((item) => item.majorCount);
const majorRectified = hiddenRectifyList.map((item) => (item.majorCount || 0) - (item.majorCountNo || 0));
const generalTotal = hiddenRectifyList.map((item) => item.generalCount);
const generalRectified = hiddenRectifyList.map((item) => (item.generalCount || 0) - (item.generalCountNo || 0));
const option = {
title: {
2026-04-29 11:11:23 +08:00
text: '当月各公司隐患统计数据',
2026-04-28 16:52:44 +08:00
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
let result = `${params[0].axisValue}<br/>`;
params.forEach((param) => {
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
});
return result;
},
},
legend: {
2026-04-29 11:11:23 +08:00
data: ['重大隐患量', '重大隐患未整改量', '一般隐患量', '一般隐患未整改量'],
2026-04-28 16:52:44 +08:00
orient: 'vertical',
right: '3%',
top: '5%',
itemGap: 12,
itemWidth: 18,
itemHeight: 12,
textStyle: { color: '#000', fontSize: 12 },
},
grid: {
left: '8%',
right: '5%',
top: '18%',
bottom: '8%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: companyNames,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
textStyle: { color: '#000' },
rotate: companyNames.length > 4 ? 15 : 0,
interval: 0,
fontSize: 12,
},
},
],
yAxis: [
{
type: 'value',
show: true,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
show: true,
textStyle: { color: '#000' },
},
splitLine: { show: false },
name: '隐患数量',
nameTextStyle: { fontSize: 12 },
},
],
series: [
{
2026-04-29 11:11:23 +08:00
name: '重大隐患量',
2026-04-28 16:52:44 +08:00
type: 'bar',
data: majorTotal,
itemStyle: {
normal: {
color: '#c92a2a', // 红色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#c92a2a', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
{
2026-04-29 11:11:23 +08:00
name: '重大隐患未整改量',
2026-04-28 16:52:44 +08:00
type: 'bar',
data: majorRectified,
itemStyle: {
normal: {
color: '#ffa94d', // 橙色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#ffa94d', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
{
2026-04-29 11:11:23 +08:00
name: '一般隐患量',
2026-04-28 16:52:44 +08:00
type: 'bar',
data: generalTotal,
itemStyle: {
normal: {
color: '#4285F4', // 蓝色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#4285F4', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
{
2026-04-29 11:11:23 +08:00
name: '一般隐患未整改量',
2026-04-28 16:52:44 +08:00
type: 'bar',
data: generalRectified,
itemStyle: {
normal: {
color: '#ffe066', // 黄色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#d4a000', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
],
};
this.echartsInstances.hiddenRectifyChart.setOption(option);
this.setupResizeHandler('hiddenRectifyChart', this.renderHiddenRectifyChart);
};
// 排名列表组件(支持公司筛选)
renderRankingList = () => {
const { hiddenSubData, selectedCompany, onCompanyChange, companyData } = this.props;
let hiddenRanking = hiddenSubData?.hiddenRanking || [];
// 根据选中的公司筛选数据
const selectedCompanyName = this.getSelectedCompanyName();
if (selectedCompanyName) {
hiddenRanking = hiddenRanking.filter((item) => item.companyName === selectedCompanyName);
}
2026-04-29 11:11:23 +08:00
// if (hiddenRanking.length === 0) {
// return (
// <div style={{ textAlign: 'center', padding: '50px', color: '#999' }}>
// {selectedCompanyName ? `${selectedCompanyName} 暂无隐患排名数据` : '暂无隐患排名数据'}
// </div>
// );
// }
2026-04-28 16:52:44 +08:00
return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<div
style={{
textAlign: 'center',
padding: '20px 0 0 0',
fontWeight: 'bold',
fontSize: '17px',
color: '#000',
}}
>
2026-04-29 11:11:23 +08:00
隐患次数排名列表
2026-04-28 16:52:44 +08:00
</div>
<div style={{ textAlign: 'right', padding: '10px 20px' }}>
<span style={{ marginRight: '8px', fontSize: '14px', color: '#000' }}>选择公司</span>
<Select
value={selectedCompany || undefined}
onChange={onCompanyChange}
style={{ width: 150 }}
allowClear
placeholder="全部公司"
>
{this.getCompanyOptions()}
</Select>
</div>
2026-04-29 11:11:23 +08:00
{/* 公司筛选器 - 与 TrainingContent 样式保持一致 */}
2026-04-28 16:52:44 +08:00
<div style={{ flex: 1, overflow: 'auto', padding: '0 20px 20px 20px' }}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ backgroundColor: '#f5f5f5', borderBottom: '2px solid #e8e8e8' }}>
2026-04-29 11:11:23 +08:00
<th style={{ padding: '12px 8px', textAlign: 'center', fontWeight: 'bold', color: '#000' }}>序号</th>
2026-04-28 16:52:44 +08:00
<th style={{ padding: '12px 8px', textAlign: 'center', fontWeight: 'bold', color: '#000' }}>
隐患名称
</th>
<th style={{ padding: '12px 8px', textAlign: 'center', fontWeight: 'bold', color: '#000' }}>数量</th>
</tr>
</thead>
<tbody>
2026-04-29 11:11:23 +08:00
{hiddenRanking.length > 0 ? (
hiddenRanking.map((item, index) => (
<tr
key={index}
style={{
borderBottom: '1px solid #e8e8e8',
backgroundColor: index % 2 === 0 ? '#fafafa' : '#fff',
}}
>
<td style={{ padding: '10px 8px', textAlign: 'center', color: '#000' }}>
<span
style={{
padding: '10px 8px',
textAlign: 'center',
color: '#000',
fontWeight: 'bold',
}}
>
{index + 1}
</span>
</td>
<td style={{ padding: '10px 8px', textAlign: 'center', color: '#000' }}>{item.hiddenName}</td>
<td style={{ padding: '10px 8px', textAlign: 'center', color: '#000' }}>
<span style={{ color: '#4285F4', fontWeight: 'bold' }}>{item.qty}</span>
</td>
</tr>
))
) : (
<div style={{ textAlign: 'center', padding: '50px', color: '#999' }}>{'暂无隐患排名数据'}</div>
)}
2026-04-28 16:52:44 +08:00
</tbody>
</table>
</div>
</div>
);
};
setupResizeHandler = (chartName, renderMethod) => {
const resizeHandler = () => {
if (this.echartsInstances[chartName] && !this.isUnmounted) {
this.echartsInstances[chartName].resize();
}
};
this.chartResizeHandlers[chartName] = resizeHandler;
window.addEventListener('resize', resizeHandler);
};
initAllCharts = () => {
if (this.isUnmounted) return;
setTimeout(() => {
if (this.isUnmounted) return;
this.renderHiddenBarChart();
this.renderHiddenRectifyChart();
}, 100);
};
disposeAllCharts = () => {
Object.keys(this.echartsInstances).forEach((key) => {
if (this.echartsInstances[key]) {
try {
this.echartsInstances[key].dispose();
} catch (e) {
console.warn(`Dispose chart ${key} error:`, e);
}
this.echartsInstances[key] = null;
}
});
Object.keys(this.chartResizeHandlers).forEach((key) => {
if (this.chartResizeHandlers[key]) {
window.removeEventListener('resize', this.chartResizeHandlers[key]);
}
});
this.chartResizeHandlers = {};
};
componentDidMount() {
this.isUnmounted = false;
this.initAllCharts();
}
componentDidUpdate(prevProps) {
// 当 hiddenSubData 或 selectedCompany 变化时重新渲染图表
if (
prevProps.hiddenSubData !== this.props.hiddenSubData ||
prevProps.selectedCompany !== this.props.selectedCompany
) {
this.renderHiddenBarChart();
this.renderHiddenRectifyChart();
}
}
componentWillUnmount() {
this.isUnmounted = true;
this.disposeAllCharts();
}
render() {
const { selectedCompany, onCompanyChange } = this.props;
return (
<div className={styles.trainingContentWrapper}>
<div className={styles.trainingGrid}>
{/* 第一行 - 柱状图left+ 排名列表right */}
<div className={styles.trainingRow}>
<div className={styles.trainingCard} style={{ flex: '0 0 70%' }}>
{/* 添加公司筛选器(左上角,与 TrainingContent 样式一致) */}
<div id="hiddenBarChart" className={styles.trainingChartContainer}></div>
</div>
<div className={styles.trainingCard} style={{ flex: '0 0 30%' }}>
{this.renderRankingList()}
</div>
</div>
{/* 第二行 - 隐患整改情况柱状图 */}
<div className={styles.trainingRow}>
<div className={styles.trainingCard}>
<div id="hiddenRectifyChart" className={styles.trainingChartContainer}></div>
</div>
</div>
</div>
</div>
);
}
}
export default HiddenSolve;