1057 lines
36 KiB
JavaScript
1057 lines
36 KiB
JavaScript
// 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 `<div style="display: flex; align-items: center;">
|
||
<span style="display: inline-block; width: 12px; height: 12px; border-radius: 50%; background-color: ${color}; margin-right: 8px;"></span>
|
||
<span>${params.name}:</span>
|
||
<span style="font-weight: bold; margin-left: 8px; font-size: 16px;">${params.value}</span>
|
||
</div>`;
|
||
},
|
||
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}<br/>`;
|
||
params.forEach((param) => {
|
||
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
|
||
});
|
||
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 `<div style="display: flex; align-items: center;">
|
||
<span style="display: inline-block; width: 12px; height: 12px; background-color: ${color}; margin-right: 8px;"></span>
|
||
<span>${params[0].name}:</span>
|
||
<span style="font-weight: bold; margin-left: 8px; font-size: 16px;">${params[0].value}</span>
|
||
</div>`;
|
||
},
|
||
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%',
|
||
barMaxWidth: 40,
|
||
},
|
||
],
|
||
};
|
||
|
||
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}<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: '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 (
|
||
<div style={{ textAlign: 'center', padding: '50px' }}>
|
||
<Icon type="loading" style={{ fontSize: '32px' }} />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (!currentAnnouncement) return null;
|
||
|
||
const { TITLE, ABSTRACT, START, END, CONTENT, CREATE_USER_NAME, Nav_Files = [] } = currentAnnouncement;
|
||
|
||
return (
|
||
<div>
|
||
{/* 标题 */}
|
||
<div style={{ textAlign: 'center', fontSize: '16px', color: '#333', marginBottom: '16px', fontWeight: 'bold' }}>
|
||
{TITLE}
|
||
</div>
|
||
|
||
{/* 摘要 */}
|
||
<div
|
||
style={{
|
||
fontSize: '14px',
|
||
color: '#676767',
|
||
wordBreak: 'break-all',
|
||
wordWrap: 'break-word',
|
||
marginBottom: '12px',
|
||
lineHeight: '1.5',
|
||
}}
|
||
>
|
||
{ABSTRACT}
|
||
</div>
|
||
|
||
{/* 日期 */}
|
||
<div style={{ fontSize: '12px', color: '#999', marginBottom: '16px', textAlign: 'center' }}>
|
||
{START ? START : '--'} 至 {END ? END : '--'}
|
||
<span style={{ marginLeft: '20px' }}>{CREATE_USER_NAME ? CREATE_USER_NAME : ''}</span>
|
||
</div>
|
||
|
||
{/* 分割线 */}
|
||
<div style={{ borderTop: '1px solid #e8e8e8', marginBottom: '16px' }}></div>
|
||
|
||
{/* 正文内容 */}
|
||
<div
|
||
className={styles.announcementContent}
|
||
dangerouslySetInnerHTML={{ __html: CONTENT || '' }}
|
||
style={{
|
||
fontSize: '14px',
|
||
lineHeight: '1.6',
|
||
color: '#333',
|
||
marginBottom: '16px',
|
||
}}
|
||
/>
|
||
|
||
{/* 附件列表 */}
|
||
{Nav_Files && Nav_Files.length > 0 && (
|
||
<div style={{ marginTop: '16px' }}>
|
||
<div style={{ fontWeight: 'bold', marginBottom: '12px', fontSize: '14px', color: '#333' }}>
|
||
附件 ({Nav_Files.length}个)
|
||
</div>
|
||
{showFiles(Nav_Files, configc.picServerHost, this)}
|
||
{GetFileModel(Modal, FormPage, this, this.state.fileForm.visible)}
|
||
<FormPage {...this.state.tmpData} />
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
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 (
|
||
<div className={styles.homeContentWrapper}>
|
||
<Row className={styles.homeContentRow} gutter={0}>
|
||
{/* 左侧区域 */}
|
||
<Col span={7} className={styles.leftCol}>
|
||
<div className={styles.riskCard}>
|
||
<Row className={styles.riskRow}>
|
||
<Col span={8} className={styles.riskStatsCol}>
|
||
{riskTypeRate.map((item, index) => (
|
||
<div key={index} className={`${styles.riskStatItem} ${styles['riskLevel' + item.riskType]}`}>
|
||
<div className={styles.riskStatName}>{item.riskType}</div>
|
||
<div className={styles.riskStatValue}>{item.count}</div>
|
||
</div>
|
||
))}
|
||
</Col>
|
||
<Col span={16} className={styles.chartCol}>
|
||
<div id="riskLevelFull" className={styles.chartContainer}></div>
|
||
</Col>
|
||
</Row>
|
||
</div>
|
||
|
||
<div className={styles.spacer}></div>
|
||
|
||
<div className={styles.hiddenCard}>
|
||
<Row className={styles.riskRow}>
|
||
<Col span={8} className={styles.hiddenStatsCol}>
|
||
<div className={styles.hiddenTitle}>年度隐患数据</div>
|
||
<div className={styles.hiddenStatItem}>
|
||
<div className={styles.hiddenStatName}>年度重大隐患数量</div>
|
||
<div className={styles.hiddenStatValue}>{hiddenSummary.majorQty}</div>
|
||
</div>
|
||
<div className={styles.hiddenStatItem}>
|
||
<div className={styles.hiddenStatName}>年度一般隐患数量</div>
|
||
<div className={styles.hiddenStatValue}>{hiddenSummary.generalQty}</div>
|
||
</div>
|
||
<div className={styles.hiddenStatItem}>
|
||
<div className={styles.hiddenStatName}>未整改隐患数量</div>
|
||
<div className={styles.hiddenStatValue}>{hiddenSummary.unfinishQty}</div>
|
||
</div>
|
||
</Col>
|
||
<Col span={16} className={styles.chartCol}>
|
||
<div id="safeCheckChart" className={styles.chartContainer}></div>
|
||
</Col>
|
||
</Row>
|
||
</div>
|
||
</Col>
|
||
|
||
{/* 中间区域 */}
|
||
<Col span={10} className={styles.middleCol}>
|
||
<div className={styles.sloganCard}>
|
||
<div className={styles.sloganContent} dangerouslySetInnerHTML={{ __html: trainingData.TITLE }} />
|
||
</div>
|
||
|
||
<div className={styles.carouselCard}>
|
||
<div className={styles.carouselWrapper}>
|
||
{trainingData.listVideoImg && trainingData.listVideoImg[0] ? (
|
||
trainingData.listVideoImg[0].TYPE == 10 ? (
|
||
<video
|
||
src={configc.picServerHost + trainingData.listVideoImg[0].FILE_PATH}
|
||
className={styles.mediaVideo}
|
||
autoplay={videoConfig.autoPlay ? 'autoplay' : undefined}
|
||
loop={videoConfig.loop ? 'loop' : undefined}
|
||
muted={videoConfig.muted ? 'muted' : undefined}
|
||
controls={videoConfig.controls ? 'controls' : undefined}
|
||
playsInline
|
||
/>
|
||
) : (
|
||
<div>
|
||
<Slider ref={this.sliderRef} {...slickSettings} className={styles.carousel}>
|
||
{trainingData.listVideoImg?.map((item, index) => (
|
||
<div key={index} className={styles.carouselItem}>
|
||
<img src={configc.picServerHost + item.FILE_PATH} className={styles.mediaImage} />
|
||
</div>
|
||
))}
|
||
</Slider>
|
||
<div className={styles.customArrowLeft} onClick={this.handlePrev}>
|
||
<Icon type="left" style={{ fontSize: '24px', color: 'rgba(0, 0, 0, 0.5)' }} />
|
||
</div>
|
||
<div className={styles.customArrowRight} onClick={this.handleNext}>
|
||
<Icon type="right" style={{ fontSize: '24px', color: 'rgba(0, 0, 0, 0.5)' }} />
|
||
</div>
|
||
<div className={styles.customDots}>
|
||
{trainingData.listVideoImg?.map((_, index) => (
|
||
<span
|
||
key={index}
|
||
className={`${styles.dot} ${currentMediaIndex === index ? styles.activeDot : ''}`}
|
||
onClick={() => this.handleDotClick(index)}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)
|
||
) : (
|
||
<div></div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className={styles.announcementCard}>
|
||
<div className={styles.announcementHeader}>
|
||
<div className={styles.announcementTitle}>
|
||
<Icon type="sound" className={styles.announcementIcon} />
|
||
<span>公司公告</span>
|
||
</div>
|
||
<span className={styles.announcementCount}>共 {trainingData.listAnnourcement?.length || 0} 条公告</span>
|
||
</div>
|
||
<div className={styles.announcementList}>
|
||
{trainingData.listAnnourcement?.length > 0 ? (
|
||
<ul className={styles.announcementUl}>
|
||
{trainingData.listAnnourcement.map((item, index) => (
|
||
<li
|
||
key={item.id || index}
|
||
className={styles.announcementItem}
|
||
onClick={() => onAnnouncementClick && onAnnouncementClick(item)}
|
||
>
|
||
<span className={styles.announcementItemTitle} title={item.TITLE}>
|
||
{item.TITLE}
|
||
</span>
|
||
<span className={styles.announcementItemTime}>{item.START}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
) : (
|
||
<div className={styles.emptyAnnouncement}>
|
||
<Icon type="inbox" className={styles.emptyIcon} />
|
||
<span>暂无公告</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</Col>
|
||
|
||
{/* 右侧区域 */}
|
||
<Col span={7} className={styles.rightCol}>
|
||
<div className={styles.dangerCard}>
|
||
{/* <div id="dangerOperationChart" className={styles.fullChartContainer}></div> */}
|
||
<div className={styles.infoContainer}>
|
||
<div className={styles.infoTitle}>集团企业信息汇总</div>
|
||
<div className={styles.infoCard}>
|
||
<div className={styles.infoCardMark}>集团公司</div>
|
||
<div className={styles.infoCardTitle}>吐鲁番金源矿冶有限责任公司</div>
|
||
<div className={styles.infoCardDetail}>子公司数量:{trainingData?.SumSubCount || '-'}家</div>
|
||
</div>
|
||
<div className={styles.infoBanner}>
|
||
{trainingData.listSubTypeCount && Object.keys(trainingData.listSubTypeCount).length > 0 ? (
|
||
<>
|
||
<div className={styles.bannerScrollContainer} ref={this.bannerScrollRef}>
|
||
<div className={styles.bannerScrollContent}>
|
||
{Object.entries(trainingData.listSubTypeCount).map(([key, value], index) => (
|
||
<div key={index} className={styles.infoSpan}>
|
||
<span>{key}</span>
|
||
<span>{value}家</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</>
|
||
) : (
|
||
// 默认显示(当没有数据时,保持原有3个的样子)
|
||
<div
|
||
className={styles.bannerScrollContent}
|
||
style={{ width: '100%', justifyContent: 'space-around' }}
|
||
>
|
||
<div className={styles.infoSpan}>
|
||
<span>露天矿</span>
|
||
<span>--家</span>
|
||
</div>
|
||
<div className={styles.infoSpan}>
|
||
<span>地下矿</span>
|
||
<span>--家</span>
|
||
</div>
|
||
<div className={styles.infoSpan}>
|
||
<span>尾矿库</span>
|
||
<span>--家</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className={styles.infoBottom}>
|
||
<div className={styles.infoBottomContent}>
|
||
<div className={styles.infoBottomImg}>
|
||
<img src={require('../../assets/layout/scgm.png')}></img>
|
||
</div>
|
||
<div className={styles.infoBottomContentRight}>
|
||
<div className={styles.infoBottomTitle}>生产规模</div>
|
||
<div className={styles.infoBottomNum}>
|
||
<span>{trainingData?.SumSubMode || '-'}</span>吨/年
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className={styles.infoBottomContent}>
|
||
<div className={styles.infoBottomImg}>
|
||
<img src={require('../../assets/layout/kqmj.png')}></img>
|
||
</div>
|
||
<div className={styles.infoBottomContentRight}>
|
||
<div className={styles.infoBottomTitle}>矿区面积</div>
|
||
<div className={styles.infoBottomNum}>
|
||
<span>{trainingData?.SumSubArea || '-'}</span>平方公里
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className={styles.spacer}></div>
|
||
|
||
<div className={styles.riskCard}>
|
||
<Row className={styles.riskRow}>
|
||
<Col span={8} className={styles.trainingStatsCol}>
|
||
<div className={styles.trainingTitle}>年度培训数据</div>
|
||
{trainingData?.listNAME?.map((name, index) => (
|
||
<div key={index} className={styles.trainingStatItem}>
|
||
<div className={styles.trainingStatName}>{name}</div>
|
||
<div className={styles.trainingStatValue}>{trainingData?.YearCount?.[index] || 0}</div>
|
||
</div>
|
||
))}
|
||
{(!trainingData?.listNAME || trainingData.listNAME.length === 0) && (
|
||
<div style={{ textAlign: 'center', color: '#999', padding: '20px 0' }}>暂无培训数据</div>
|
||
)}
|
||
</Col>
|
||
<Col span={16} className={styles.chartCol}>
|
||
<div id="backLogChart" className={styles.chartContainer}></div>
|
||
</Col>
|
||
</Row>
|
||
</div>
|
||
</Col>
|
||
</Row>
|
||
{/* 公告详情弹窗 */}
|
||
<Modal
|
||
title="公告详情"
|
||
visible={announcementModalVisible}
|
||
onCancel={onAnnouncementModalClose}
|
||
footer={[
|
||
<Button key="close" onClick={onAnnouncementModalClose}>
|
||
关闭
|
||
</Button>,
|
||
]}
|
||
width="80%"
|
||
bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }}
|
||
>
|
||
{this.renderAnnouncementModal()}
|
||
</Modal>
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
export default HomeContent;
|