// 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: '25%', 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) => (
{item.name}
{item.value}
))}
{/* 中间区域 */}
安全方针:以人为本、关注健康、依法治企、安全发展。
安全理念:一切风险皆可控,一切事故皆可防!
{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;