// HomeContent.js
import React, { useEffect, useRef } from 'react';
import { Row, Col, Icon, Carousel } from 'antd';
import styles from './../fullinter.less';
import echarts from 'echarts';
class HomeContent extends React.Component {
constructor(props) {
super(props);
this.carouselRef = React.createRef();
this.echartsInstances = {
riskLevel: null,
safeCheckChart: null,
dangerOperation: null,
backLogChart: null,
};
this.chartResizeHandlers = {};
this.isUnmounted = false;
}
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);
});
};
riskLevel = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('riskLevelFull');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.riskLevel) {
this.echartsInstances.riskLevel.dispose();
this.echartsInstances.riskLevel = null;
}
const riskLevels = document.getElementById('riskLevelFull');
if (!riskLevels) return;
this.echartsInstances.riskLevel = echarts.init(riskLevels);
const data = this.props.riskTypeRate || [];
const option = {
color: ['#c92a2a', '#ffa94d', '#ffe066', '#4285F4'],
title: [
{
text: '风险分级占比',
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000' },
},
],
tooltip: {
trigger: 'item',
formatter: function (params) {
const color = params.color;
return `
${params.name}:
${params.value}
`;
},
backgroundColor: 'rgba(255, 255, 255, 0.5)',
borderColor: '#FFFFFF',
borderWidth: 2,
textStyle: { color: '#000', fontSize: 14 },
},
series: [
{
name: '访问来源',
type: 'pie',
minAngle: 20,
radius: ['55%', '80%'],
center: ['50%', '50%'],
clockwise: true,
avoidLabelOverlap: true,
hoverOffset: 15,
label: {
show: true,
position: 'inside',
formatter: '{a|{b}:{c}}{e|({d}%)}\n',
color: '#000',
textBorderWidth: 0,
rich: {
a: { padding: [-15, 0, 0, 0], fontSize: 15, color: '#000', textBorderWidth: 0 },
e: { fontSize: 14, color: '#000', padding: [-15, 0, 0, 5], textBorderWidth: 0 },
},
},
labelLine: { normal: { show: false } },
data: data.map((item) => ({ name: item.riskType, value: item.count })),
},
],
};
this.echartsInstances.riskLevel.setOption(option);
const resizeHandler = () => {
if (this.echartsInstances.riskLevel && !this.isUnmounted) {
this.echartsInstances.riskLevel.resize();
}
};
this.chartResizeHandlers.riskLevel = resizeHandler;
window.addEventListener('resize', resizeHandler);
};
transformDat = (originalData, barTopColor, num) => {
if (!originalData || !Array.isArray(originalData) || originalData.length === 0) {
return { companyNames: [], series: [], legendData: [] };
}
let allTypes = [];
if (num == 1) {
allTypes = [...new Set(originalData.flatMap((item) => item.details?.map((detail) => detail.jobName)))];
} else {
allTypes = [...new Set(originalData.flatMap((item) => item.details?.map((detail) => detail.name)))];
}
allTypes = allTypes.filter(Boolean);
const companyNames = originalData?.map((item) => item.company);
const series = allTypes?.map((typeName, index) => ({
name: typeName,
type: 'bar',
itemStyle: {
normal: {
color: function (params) {
if (num == 1) {
return barTopColor[index];
} else {
return new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: barTopColor[index][0] },
{ offset: 1, color: barTopColor[index][1] },
]);
}
},
},
},
data: originalData?.map((company) => {
const detail = company.details?.find((d) => (num == 1 ? d.jobName : d.name) === typeName);
return detail ? detail.qty : 0;
}),
}));
return { companyNames, series, legendData: allTypes };
};
safeCheckChart = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('safeCheckChart');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.safeCheckChart) {
this.echartsInstances.safeCheckChart.dispose();
this.echartsInstances.safeCheckChart = null;
}
let safeCheckCharts = document.getElementById('safeCheckChart');
if (!safeCheckCharts) return;
const barTopColor = ['#02c3f1', '#53e568', '#a154e9'];
const jobData = this.props.jobTodayTop3 || [];
if (jobData.length === 0) return;
this.echartsInstances.safeCheckChart = echarts.init(safeCheckCharts);
let xdata = this.transformDat(jobData, barTopColor, 1);
this.echartsInstances.safeCheckChart.setOption({
title: {
text: '当日工作票排名前三家的数据',
textStyle: { fontSize: 16, color: '#fff' },
},
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
legend: {
data: xdata.legendData,
align: 'right',
right: 10,
itemGap: 16,
itemWidth: 18,
itemHeight: 10,
textStyle: { color: '#fff', fontSize: 14 },
},
color: barTopColor,
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: [
{
type: 'category',
data: xdata.companyNames,
axisLine: { lineStyle: { color: '#3eb2e8' } },
axisLabel: { textStyle: { color: '#fff' } },
},
],
yAxis: [
{
type: 'value',
axisLine: { show: false },
axisLabel: { textStyle: { color: '#fff' } },
splitLine: { lineStyle: { color: '#4784e8' } },
},
],
series: xdata.series,
});
const resizeHandler = () => {
if (this.echartsInstances.safeCheckChart && !this.isUnmounted) {
this.echartsInstances.safeCheckChart.resize();
}
};
this.chartResizeHandlers.safeCheckChart = resizeHandler;
window.addEventListener('resize', resizeHandler);
};
dangerOperation = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('dangerOperationChart');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.dangerOperation) {
this.echartsInstances.dangerOperation.dispose();
this.echartsInstances.dangerOperation = null;
}
const dangerOperationCharts = document.getElementById('dangerOperationChart');
if (!dangerOperationCharts) return;
const linkData = this.props.linkSum || [];
if (linkData.length === 0) return;
this.echartsInstances.dangerOperation = echarts.init(dangerOperationCharts);
const xAxisData = linkData.map((item) => item.name);
const seriesData = linkData.map((item) => item.qty);
const option = {
color: ['#4285F4'],
title: [
{
text: '当日工作票的统计数量',
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000' },
},
],
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
const color = params[0].color;
return `
${params[0].name}:
${params[0].value}
`;
},
backgroundColor: 'rgba(255, 255, 255, 0.5)',
borderColor: '#FFFFFF',
borderWidth: 2,
textStyle: { color: '#000', fontSize: 14 },
},
grid: { left: '10%', right: '5%', top: '15%', bottom: '10%', containLabel: true },
xAxis: [
{
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#000' } },
axisTick: { show: false },
axisLabel: { textStyle: { color: '#000' }, rotate: 30, interval: 0 },
},
],
yAxis: [
{
type: 'value',
name: '',
nameTextStyle: { color: '#000' },
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { textStyle: { color: '#000' } },
splitLine: { show: false },
},
],
series: [
{
name: '危险作业数量',
type: 'bar',
data: seriesData,
itemStyle: {
normal: { color: '#4285F4', barBorderRadius: 12 },
emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' },
},
label: { show: true, position: 'top', textStyle: { color: '#000', fontSize: 12 }, formatter: '{c}' },
barWidth: '60%',
},
],
};
this.echartsInstances.dangerOperation.setOption(option);
const resizeHandler = () => {
if (this.echartsInstances.dangerOperation && !this.isUnmounted) {
this.echartsInstances.dangerOperation.resize();
}
};
this.chartResizeHandlers.dangerOperation = resizeHandler;
window.addEventListener('resize', resizeHandler);
};
backLog = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('backLogChart');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.backLogChart) {
this.echartsInstances.backLogChart.dispose();
this.echartsInstances.backLogChart = null;
}
let backLogCharts = document.getElementById('backLogChart');
if (!backLogCharts) return;
// 使用培训数据
const { trainingData } = this.props;
const listNAME = trainingData?.listNAME || [];
const monthRecordCount = trainingData?.MonthRecordCount || []; // 培训场次
const monthPersonCount = trainingData?.MonthPersonCount || []; // 培训人次
if (listNAME.length === 0) {
this.echartsInstances.backLogChart = echarts.init(backLogCharts);
this.echartsInstances.backLogChart.setOption({
title: {
text: '当月工作培训统计数量',
x: 'center',
y: 'center',
textStyle: { fontSize: 16, color: '#999' },
},
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: '暂无培训数据',
fill: '#999',
fontSize: 14,
},
},
});
return;
}
this.echartsInstances.backLogChart = echarts.init(backLogCharts);
const option = {
title: {
text: '当月工作培训统计数量',
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000' },
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
let result = `${params[0].axisValue}
`;
params.forEach((param) => {
result += `${param.marker}${param.seriesName}: ${param.value}
`;
});
return result;
},
},
legend: {
data: ['培训人次', '培训场次'],
orient: 'vertical', // 垂直排列
right: 0, // 水平居中
top: 'middle', // 垂直居中
itemGap: 16, // 图例项间隔
itemWidth: 18,
itemHeight: 12,
textStyle: { color: '#000', fontSize: 14 },
},
grid: {
left: '5%',
right: '5%', // 为右侧垂直图例留出空间
top: '18%',
bottom: '5%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: listNAME,
axisLine: { show: false }, // 隐藏x轴线
axisTick: { show: false }, // 隐藏x轴刻度线
axisLabel: {
textStyle: { color: '#000' },
rotate: listNAME.length > 4 ? 15 : 0,
interval: 0,
},
},
],
yAxis: [
{
type: 'value',
show: true, // 显示y轴
axisLine: { show: false }, // 隐藏y轴线
axisTick: { show: false }, // 隐藏y轴刻度线
axisLabel: {
show: true, // 显示数值标签
textStyle: { color: '#000' },
},
splitLine: { show: false }, // 隐藏横向网格线
name: '', // 不显示单位名称
nameTextStyle: { show: false }, // 隐藏单位文字
},
],
series: [
{
name: '培训人次',
type: 'bar',
data: monthPersonCount,
itemStyle: {
normal: {
color: '#4285F4', // 蓝色
barBorderRadius: 12, // 柱体圆角
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#4285F4', fontSize: 12 },
formatter: '{c}',
},
barWidth: '35%',
},
{
name: '培训场次',
type: 'bar',
data: monthRecordCount,
itemStyle: {
normal: {
color: '#ffe066', // 黄色
barBorderRadius: 12, // 柱体圆角(上左、上右、下右、下左)
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#ffe066', fontSize: 12 },
formatter: '{c}',
},
barWidth: '35%',
},
],
};
this.echartsInstances.backLogChart.setOption(option);
const resizeHandler = () => {
if (this.echartsInstances.backLogChart && !this.isUnmounted) {
this.echartsInstances.backLogChart.resize();
}
};
this.chartResizeHandlers.backLogChart = resizeHandler;
window.addEventListener('resize', resizeHandler);
};
initAllCharts = () => {
if (this.isUnmounted) return;
setTimeout(() => {
if (this.isUnmounted) return;
this.riskLevel();
this.safeCheckChart();
this.dangerOperation();
this.backLog();
}, 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 = {};
};
handlePrev = () => {
if (this.carouselRef.current) {
this.carouselRef.current.prev();
}
};
handleNext = () => {
if (this.carouselRef.current) {
this.carouselRef.current.next();
}
};
handleCarouselChange = (current) => {
this.props.onCarouselChange?.(current);
};
handleDotClick = (index) => {
this.props.onDotClick?.(index);
if (this.carouselRef.current) {
this.carouselRef.current.goTo(index);
}
};
componentDidMount() {
this.isUnmounted = false;
this.initAllCharts();
}
componentDidUpdate(prevProps) {
if (
prevProps.riskTypeRate !== this.props.riskTypeRate ||
prevProps.jobTodayTop3 !== this.props.jobTodayTop3 ||
prevProps.linkSum !== this.props.linkSum ||
prevProps.taskTop3 !== this.props.taskTop3 ||
prevProps.trainingData !== this.props.trainingData // 新增
) {
this.disposeAllCharts();
this.initAllCharts();
}
}
componentWillUnmount() {
this.isUnmounted = true;
this.disposeAllCharts();
}
render() {
const {
riskTypeRate = [],
riskTotal = [],
mediaList = [],
announcementList = [],
currentMediaIndex = 0,
} = this.props;
return (
{/* 左侧区域 */}
{riskTypeRate.map((item, index) => (
{item.riskType}
{item.count}
))}
年度隐患数据
{riskTotal.map((item, index) => (
))}
{/* 中间区域 */}
安全方针:以人为本、关注健康、依法治企、安全发展。
安全理念:一切风险皆可控,一切事故皆可防!
{mediaList.map((item, index) => (
{item.type === 'video' ? (
) : (

)}
))}
{mediaList.map((_, index) => (
this.handleDotClick(index)}
/>
))}
公司公告
共 {announcementList.length} 条公告
{announcementList.length > 0 ? (
{announcementList.map((item, index) => (
- item.url && window.open(item.url)}
>
{item.title}
{item.publishTime || item.createTime || item.date}
))}
) : (
暂无公告
)}
{/* 右侧区域 */}
年度培训数据
{this.props.trainingData?.listNAME?.map((name, index) => (
{name}
{this.props.trainingData?.YearCount?.[index] || 0}
))}
{/* 如果 listNAME 为空,显示默认提示 */}
{(!this.props.trainingData?.listNAME || this.props.trainingData.listNAME.length === 0) && (
暂无培训数据
)}
);
}
}
export default HomeContent;