mh_jy_safe_web/src/layout/FullOther/HiddenSolve.js
2026-05-12 11:34:09 +08:00

599 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// HiddenSolve.js - 隐患解决页面组件
import React from 'react';
import { Select, Table } 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;
// 添加缓存,用于比较数据是否真正变化
this.lastHiddenList = null;
this.lastHiddenRectifyList = null;
}
// 获取公司选项(使用 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: {
text: '各公司累计隐患统计数据',
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: {
text: '各公司累计隐患统计数据',
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: ['重大隐患', '一般隐患'],
right: '3%',
top: '10%',
itemGap: 12,
itemWidth: 16,
itemHeight: 10,
textStyle: { color: '#000', fontSize: 12 },
},
grid: {
left: '8%',
right: '5%',
top: '28%',
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: {
text: '当月各公司隐患统计数据',
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.majorCountNo);
const generalTotal = hiddenRectifyList.map((item) => item.generalCount);
const generalRectified = hiddenRectifyList.map((item) => item.generalCountNo);
const option = {
title: {
text: '当月各公司隐患统计数据',
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: ['重大隐患量', '重大隐患未整改量', '一般隐患量', '一般隐患未整改量'],
right: '3%',
top: '10%',
itemGap: 12,
itemWidth: 16,
itemHeight: 10,
textStyle: { color: '#000', fontSize: 12 },
},
grid: {
left: '8%',
right: '5%',
top: '28%',
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: majorTotal,
itemStyle: {
normal: {
color: '#c92a2a', // 红色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#c92a2a', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
{
name: '重大隐患未整改量',
type: 'bar',
data: majorRectified,
itemStyle: {
normal: {
color: '#ffa94d', // 橙色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#ffa94d', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
{
name: '一般隐患量',
type: 'bar',
data: generalTotal,
itemStyle: {
normal: {
color: '#4285F4', // 蓝色
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#4285F4', fontSize: 11 },
formatter: (params) => `${params.value}`,
},
barWidth: '20%',
},
{
name: '一般隐患未整改量',
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();
let filteredRanking = hiddenRanking;
if (selectedCompanyName) {
filteredRanking = hiddenRanking.filter((item) => item.companyName === selectedCompanyName);
}
// 如果没选中公司或筛选后没有数据,显示全部或空状态
if (!selectedCompanyName || filteredRanking.length === 0) {
filteredRanking = selectedCompanyName ? [] : hiddenRanking;
}
// 表格列配置(与 RiskControl 样式保持一致)
const columns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
align: 'center',
width: 80,
render: (text, record, index) => <span style={{ fontWeight: 'bold' }}>{index + 1}</span>,
},
{
title: '隐患名称',
dataIndex: 'hiddenName',
key: 'hiddenName',
align: 'center',
// 中间列自适应,不设置固定宽度
render: (text) => <span style={{ wordBreak: 'break-word', whiteSpace: 'normal' }}>{text}</span>,
},
{
title: '数量',
dataIndex: 'qty',
key: 'qty',
align: 'center',
width: 80,
render: (text) => <span style={{ fontWeight: 'bold' }}>{text}</span>,
},
];
const tableData = filteredRanking.map((item, index) => ({
key: index,
index: index + 1,
hiddenName: item.hiddenName,
qty: item.qty,
}));
const scrollConfig = filteredRanking.length > 5 ? { y: 300 } : {};
return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<div
style={{
textAlign: 'center',
padding: '20px 0 0 0',
fontWeight: 'bold',
fontSize: '17px',
color: '#000',
}}
>
隐患次数排名列表
</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>
<div style={{ flex: 1, padding: '10px' }}>
<Table
columns={columns}
dataSource={tableData}
pagination={false}
scroll={scrollConfig}
size="small"
bordered={false}
className={styles.hiddenTable}
style={{ border: 'none' }}
/>
</div>
</div>
);
};
// 添加 shouldComponentUpdate 优化性能
shouldComponentUpdate(nextProps, nextState) {
// 只有当这些 props 变化时才重新渲染
if (
this.props.hiddenSubData?.hiddenRanking !== nextProps.hiddenSubData?.hiddenRanking ||
this.props.selectedCompany !== nextProps.selectedCompany ||
this.props.companyData !== nextProps.companyData
) {
return true;
}
return false;
}
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) {
const prevHiddenList = prevProps.hiddenSubData?.hiddenList;
const currentHiddenList = this.props.hiddenSubData?.hiddenList;
const prevHiddenRectifyList = prevProps.hiddenSubData?.hiddenRectifyList;
const currentHiddenRectifyList = this.props.hiddenSubData?.hiddenRectifyList;
// 检查 hiddenList 是否变化
const hiddenListChanged = JSON.stringify(prevHiddenList) !== JSON.stringify(currentHiddenList);
// 检查 hiddenRectifyList 是否变化
const hiddenRectifyListChanged = JSON.stringify(prevHiddenRectifyList) !== JSON.stringify(currentHiddenRectifyList);
// 只在数据真正变化时重新渲染图表
if (hiddenListChanged) {
this.renderHiddenBarChart();
}
if (hiddenRectifyListChanged) {
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;