// HomeContent.js
import React, { useEffect, useRef } from 'react';
import { Row, Col, Icon, Modal, Button } from 'antd';
import Slider from 'react-slick';
import styles from './../fullinter.less';
import echarts from 'echarts';
import configc from '../../config';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import { extendRule, showFileModal, showFiles, GetFileModel } from '../../utils/common';
import FormPage from '../../components/FormPage';
class HomeContent extends React.Component {
constructor(props) {
super(props);
this.sliderRef = React.createRef();
this.echartsInstances = {
riskLevel: null,
safeCheckChart: null,
dangerOperation: null,
backLogChart: null,
};
this.chartResizeHandlers = {};
this.isUnmounted = false;
this.autoplayTimer = null;
this.state = {
fileForm: {
title: '',
visible: 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: '{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);
};
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 hiddenSummary = this.props.hiddenSummary || {};
// 检查是否有有效数据
const hasData =
hiddenSummary.monthMajorQty !== undefined ||
hiddenSummary.unfinishMonthMajorQty !== undefined ||
hiddenSummary.monthGeneralQty !== undefined ||
hiddenSummary.unfinishMonthGeneralQty !== undefined;
if (!hasData) {
this.echartsInstances.safeCheckChart = echarts.init(safeCheckCharts);
this.echartsInstances.safeCheckChart.setOption({
title: {
text: '当月隐患统计',
x: 'center',
y: '25%',
textStyle: { fontSize: 16, color: '#fff' },
},
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: '暂无数据',
fill: '#999',
fontSize: 14,
},
},
});
return;
}
// 定义图例和数据
const legendData = ['重大隐患数', '重大隐患未整改数', '一般隐患数', '一般隐患未整改数'];
const seriesData = [
{ name: '重大隐患数', value: hiddenSummary.monthMajorQty || 0, color: '#c92a2a' },
{ name: '重大隐患未整改数', value: hiddenSummary.unfinishMonthMajorQty || 0, color: '#ffa94d' },
{ name: '一般隐患数', value: hiddenSummary.monthGeneralQty || 0, color: '#ffe066' },
{ name: '一般隐患未整改数', value: hiddenSummary.unfinishMonthGeneralQty || 0, color: '#4285F4' },
];
this.echartsInstances.safeCheckChart = echarts.init(safeCheckCharts);
this.echartsInstances.safeCheckChart.setOption({
title: {
text: '当月隐患统计',
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000' },
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
if (!params || params.length === 0) return '';
let result = `${params[0].axisValue}
`;
params.forEach((param) => {
result += `${param.marker}${param.seriesName}: ${param.value}
`;
});
return result;
},
},
legend: {
data: legendData,
right: '3%',
top: '10%',
itemGap: 12,
itemWidth: 16,
itemHeight: 10,
textStyle: { color: '#000', fontSize: 12 },
},
color: seriesData.map((item) => item.color),
grid: {
left: '5%',
right: '8%',
top: '28%',
bottom: '5%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: ['当月隐患'],
axisLine: { lineStyle: { color: '#c4c6c9' } },
axisLabel: { textStyle: { color: '#000', fontSize: 14 } },
axisTick: { show: false },
},
],
yAxis: [
{
type: 'value',
name: '数量',
nameTextStyle: { color: '#000' },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { textStyle: { color: '#000' } },
},
],
series: seriesData.map((item) => ({
name: item.name,
type: 'bar',
data: [item.value],
itemStyle: {
normal: {
color: item.color,
barBorderRadius: [8, 8, 0, 0],
},
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: '10%',
barGap: '30%',
barCategoryGap: '20%',
})),
});
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.jobTodayQty || [];
// 如果没有数据,显示“暂无数据”提示
if (linkData.length === 0) {
this.echartsInstances.dangerOperation = echarts.init(dangerOperationCharts);
this.echartsInstances.dangerOperation.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.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: '#c4c6c9' } },
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: ['培训人次', '培训场次'],
right: '3%',
top: '10%',
itemGap: 12,
itemWidth: 16,
itemHeight: 10,
textStyle: { color: '#000', fontSize: 12 },
},
grid: {
left: '5%',
right: '5%',
top: '28%',
bottom: '5%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: listNAME,
axisLine: { lineStyle: { color: '#c4c6c9' } },
axisTick: { show: false },
axisLabel: {
textStyle: { color: '#000' },
rotate: listNAME.length > 4 ? 15 : 0,
interval: 0,
},
},
],
yAxis: [
{
type: 'value',
show: true,
axisLine: { show: false },
axisTick: { show: false },
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);
};
// 启动自动轮播
startAutoplay = (intervalTime) => {
this.stopAutoplay();
if (!intervalTime || intervalTime <= 0) return;
this.autoplayTimer = setInterval(() => {
if (this.sliderRef.current && !this.isUnmounted) {
const nextIndex = (this.props.currentMediaIndex + 1) % (this.props.trainingData?.listVideoImg?.length || 1);
this.props.onCarouselChange?.(nextIndex);
this.sliderRef.current.slickGoTo(nextIndex);
}
}, intervalTime);
};
// 停止自动轮播
stopAutoplay = () => {
if (this.autoplayTimer) {
clearInterval(this.autoplayTimer);
this.autoplayTimer = null;
}
};
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.sliderRef.current) {
this.sliderRef.current.slickPrev();
const nextIndex = this.props.currentMediaIndex - 1;
if (nextIndex >= 0) {
this.props.onCarouselChange?.(nextIndex);
}
// 鼠标点击时重启自动播放计时器
this.restartAutoplay();
}
};
handleNext = () => {
if (this.sliderRef.current) {
this.sliderRef.current.slickNext();
const nextIndex = this.props.currentMediaIndex + 1;
const total = this.props.trainingData?.listVideoImg?.length || 0;
if (nextIndex < total) {
this.props.onCarouselChange?.(nextIndex);
}
// 鼠标点击时重启自动播放计时器
this.restartAutoplay();
}
};
// 重启自动播放
restartAutoplay = () => {
const playSet = this.props.trainingData?.playSet || {};
const isAutoplay = playSet.IMG_ISRE !== undefined ? playSet.IMG_ISRE : true;
const autoplaySpeed = playSet.IMG_TIMESPAN ? playSet.IMG_TIMESPAN * 1000 : 5000;
if (isAutoplay && autoplaySpeed > 0) {
this.startAutoplay(autoplaySpeed);
}
};
handleDotClick = (index) => {
this.props.onDotClick?.(index);
if (this.sliderRef.current) {
this.sliderRef.current.slickGoTo(index);
}
// 点击圆点时重启自动播放
this.restartAutoplay();
};
handleCarouselChange = (current) => {
this.props.onCarouselChange?.(current);
};
componentDidMount() {
this.isUnmounted = false;
this.initAllCharts();
}
componentDidUpdate(prevProps) {
// 当 trainingData 变化时,重新初始化自动播放
if (
prevProps.trainingData?.playSet?.IMG_ISRE !== this.props.trainingData?.playSet?.IMG_ISRE ||
prevProps.trainingData?.playSet?.IMG_TIMESPAN !== this.props.trainingData?.playSet?.IMG_TIMESPAN
) {
this.restartAutoplay();
}
if (
prevProps.riskTypeRate !== this.props.riskTypeRate ||
prevProps.hiddenSummary !== this.props.hiddenSummary ||
prevProps.jobTodayQty !== this.props.jobTodayQty ||
prevProps.taskTop3 !== this.props.taskTop3 ||
prevProps.trainingData !== this.props.trainingData
) {
this.disposeAllCharts();
this.initAllCharts();
}
}
componentWillUnmount() {
this.isUnmounted = true;
this.stopAutoplay();
this.disposeAllCharts();
}
// 新增:下载附件方法
handleDownload = (file) => {
const fileUrl = configc.picServerHost + file.Nav_ImgFile.FILE_PATH;
const link = document.createElement('a');
link.href = fileUrl;
link.download = file.Nav_ImgFile.FILE_NAME;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 新增:预览附件方法
handlePreview = (file) => {
const fileUrl = configc.picServerHost + file.Nav_ImgFile.FILE_PATH;
window.open(fileUrl, '_blank');
};
// 新增:渲染公告弹窗内容
renderAnnouncementModal = () => {
const { currentAnnouncement, announcementDetailLoading } = this.props;
if (announcementDetailLoading) {
return (
);
}
if (!currentAnnouncement) return null;
const { TITLE, ABSTRACT, START, END, CONTENT, CREATE_USER_NAME, Nav_Files = [] } = currentAnnouncement;
return (
{/* 标题 */}
{TITLE}
{/* 摘要 */}
{ABSTRACT}
{/* 日期 */}
{START ? START : '--'} 至 {END ? END : '--'}
{CREATE_USER_NAME ? CREATE_USER_NAME : ''}
{/* 分割线 */}
{/* 正文内容 */}
{/* 附件列表 */}
{Nav_Files && Nav_Files.length > 0 && (
附件 ({Nav_Files.length}个)
{showFiles(Nav_Files, configc.picServerHost, this)}
{GetFileModel(Modal, FormPage, this, this.state.fileForm.visible)}
)}
);
};
render() {
const {
riskTypeRate = [],
currentMediaIndex = 0,
hiddenSummary = {},
trainingData = {}, // 新增 props
onAnnouncementClick,
announcementModalVisible,
onAnnouncementModalClose,
} = this.props;
// 获取播放配置,提供默认值
const playSet = trainingData.playSet || {};
const videoConfig = {
autoPlay: playSet.V_ISAUTO !== undefined ? playSet.V_ISAUTO : true,
loop: playSet.V_ISRE !== undefined ? playSet.V_ISRE : true,
muted: playSet.V_ISSILENT !== undefined ? playSet.V_ISSILENT : true,
controls: playSet.V_ISSHOWCONTROL !== undefined ? playSet.V_ISSHOWCONTROL : false,
};
const isAutoplay = playSet.IMG_ISRE !== undefined ? playSet.IMG_ISRE : true;
const autoplaySpeed = playSet.IMG_TIMESPAN ? playSet.IMG_TIMESPAN * 1000 : 5000;
const slickSettings = {
dots: false,
arrows: false, // 🔑 隐藏默认箭头 ← 添加这一行
infinite: true,
speed: 500,
slidesToShow: 1,
slidesToScroll: 1,
autoplay: isAutoplay,
autoplaySpeed: autoplaySpeed,
fade: true,
pauseOnHover: true,
afterChange: this.handleCarouselChange,
};
return (
{/* 左侧区域 */}
{riskTypeRate.map((item, index) => (
{item.riskType}
{item.count}
))}
年度隐患数据
年度重大隐患数量
{hiddenSummary.majorQty}
年度一般隐患数量
{hiddenSummary.generalQty}
未整改隐患数量
{hiddenSummary.unfinishQty}
{/* 中间区域 */}
{trainingData.listVideoImg && trainingData.listVideoImg[0] ? (
trainingData.listVideoImg[0].TYPE == 10 ? (
) : (
{trainingData.listVideoImg?.map((item, index) => (
))}
{trainingData.listVideoImg?.map((_, index) => (
this.handleDotClick(index)}
/>
))}
)
) : (
)}
公司公告
共 {trainingData.listAnnourcement?.length || 0} 条公告
{trainingData.listAnnourcement?.length > 0 ? (
{trainingData.listAnnourcement.map((item, index) => (
- onAnnouncementClick && onAnnouncementClick(item)}
>
{item.TITLE}
{item.START}
))}
) : (
暂无公告
)}
{/* 右侧区域 */}
年度培训数据
{trainingData?.listNAME?.map((name, index) => (
{name}
{trainingData?.YearCount?.[index] || 0}
))}
{(!trainingData?.listNAME || trainingData.listNAME.length === 0) && (
暂无培训数据
)}
{/* 公告详情弹窗 */}
关闭
,
]}
width="80%"
bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }}
>
{this.renderAnnouncementModal()}
);
}
}
export default HomeContent;