2026-04-28 16:52:44 +08:00
|
|
|
|
// DangerJob.js - 危险作业页面组件
|
|
|
|
|
|
import React from 'react';
|
2026-05-21 17:10:47 +08:00
|
|
|
|
import { Table, Select, Pagination, DatePicker } from 'antd';
|
|
|
|
|
|
import echarts from 'echarts';
|
2026-04-28 16:52:44 +08:00
|
|
|
|
import styles from './../fullinter.less';
|
|
|
|
|
|
|
|
|
|
|
|
const { Option } = Select;
|
|
|
|
|
|
|
|
|
|
|
|
class DangerJob extends React.Component {
|
|
|
|
|
|
constructor(props) {
|
|
|
|
|
|
super(props);
|
|
|
|
|
|
this.state = {
|
|
|
|
|
|
currentPage: 1,
|
2026-05-21 17:10:47 +08:00
|
|
|
|
pageSize: 10,
|
2026-04-28 16:52:44 +08:00
|
|
|
|
};
|
2026-05-21 17:10:47 +08:00
|
|
|
|
this.echartsInstances = {
|
|
|
|
|
|
dangerOperationChart: null, // 当日工作票统计柱状图
|
|
|
|
|
|
};
|
|
|
|
|
|
this.chartResizeHandlers = {};
|
2026-04-28 16:52:44 +08:00
|
|
|
|
this.isUnmounted = false;
|
2026-05-21 17:10:47 +08:00
|
|
|
|
// 添加缓存,用于比较数据是否真正变化
|
|
|
|
|
|
this.lastJobTodayQty = null;
|
2026-04-28 16:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成公司选项(与 TrainingContent 保持一致)
|
|
|
|
|
|
getCompanyOptions = () => {
|
|
|
|
|
|
const { companyData } = this.props;
|
|
|
|
|
|
if (!companyData || companyData.length === 0) {
|
|
|
|
|
|
return <Option value="">暂无公司</Option>;
|
|
|
|
|
|
}
|
|
|
|
|
|
return companyData.map((company, index) => (
|
|
|
|
|
|
<Option key={company.ID || index} value={company.ID}>
|
|
|
|
|
|
{company.NAME}
|
|
|
|
|
|
</Option>
|
|
|
|
|
|
));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理公司筛选变化
|
|
|
|
|
|
handleCompanyChange = (value) => {
|
|
|
|
|
|
const { onCompanyChange } = this.props;
|
|
|
|
|
|
this.setState({ currentPage: 1 }, () => {
|
|
|
|
|
|
if (onCompanyChange) {
|
|
|
|
|
|
onCompanyChange(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-21 17:10:47 +08:00
|
|
|
|
// 处理开始日期变化
|
|
|
|
|
|
startChange = (value) => {
|
|
|
|
|
|
const { onStartChange } = this.props;
|
|
|
|
|
|
this.setState({ currentPage: 1 }, () => {
|
|
|
|
|
|
if (onStartChange) {
|
|
|
|
|
|
onStartChange(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理结束日期变化
|
|
|
|
|
|
endChange = (value) => {
|
|
|
|
|
|
const { onEndChange } = this.props;
|
|
|
|
|
|
this.setState({ currentPage: 1 }, () => {
|
|
|
|
|
|
if (onEndChange) {
|
|
|
|
|
|
onEndChange(value);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-04-28 16:52:44 +08:00
|
|
|
|
// 处理页码变化
|
|
|
|
|
|
handlePageChange = (page, pageSize) => {
|
|
|
|
|
|
this.setState({ currentPage: page, pageSize });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-21 17:10:47 +08:00
|
|
|
|
// 等待DOM元素加载完成
|
|
|
|
|
|
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);
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 设置resize监听
|
|
|
|
|
|
setupResizeHandler = (chartName, renderMethod) => {
|
|
|
|
|
|
const resizeHandler = () => {
|
|
|
|
|
|
if (this.echartsInstances[chartName] && !this.isUnmounted) {
|
|
|
|
|
|
this.echartsInstances[chartName].resize();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
this.chartResizeHandlers[chartName] = resizeHandler;
|
|
|
|
|
|
window.addEventListener('resize', resizeHandler);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 图表: 当日工作票统计柱状图
|
|
|
|
|
|
renderDangerOperationChart = async () => {
|
|
|
|
|
|
if (this.isUnmounted) return;
|
|
|
|
|
|
const elementExists = await this.waitForElement('dangerOperationChart');
|
|
|
|
|
|
if (!elementExists || this.isUnmounted) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (this.echartsInstances.dangerOperationChart) {
|
|
|
|
|
|
this.echartsInstances.dangerOperationChart.dispose();
|
|
|
|
|
|
this.echartsInstances.dangerOperationChart = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const chartDom = document.getElementById('dangerOperationChart');
|
|
|
|
|
|
if (!chartDom) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.echartsInstances.dangerOperationChart = echarts.init(chartDom);
|
|
|
|
|
|
|
|
|
|
|
|
const { jobTodayQty } = this.props;
|
|
|
|
|
|
let linkData = jobTodayQty || [];
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有数据,显示“暂无数据”提示
|
|
|
|
|
|
if (linkData.length === 0) {
|
|
|
|
|
|
this.echartsInstances.dangerOperationChart.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 xAxisData = linkData.map((item) => item.name);
|
|
|
|
|
|
const seriesData = linkData.map((item) => item.qty);
|
|
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
|
|
title: {
|
|
|
|
|
|
text: '当日工作票的统计数量',
|
|
|
|
|
|
x: 'center',
|
|
|
|
|
|
y: '5%',
|
|
|
|
|
|
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
|
|
|
|
|
|
},
|
|
|
|
|
|
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.9)',
|
|
|
|
|
|
borderColor: '#4285F4',
|
|
|
|
|
|
borderWidth: 1,
|
|
|
|
|
|
textStyle: { color: '#000', fontSize: 14 },
|
|
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
left: '8%',
|
|
|
|
|
|
right: '5%',
|
|
|
|
|
|
top: '18%',
|
|
|
|
|
|
bottom: '8%',
|
|
|
|
|
|
containLabel: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
xAxis: [
|
|
|
|
|
|
{
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
data: xAxisData,
|
|
|
|
|
|
axisLine: { show: false },
|
|
|
|
|
|
axisTick: { show: false },
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
textStyle: { color: '#000' },
|
|
|
|
|
|
rotate: xAxisData.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: seriesData,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
normal: {
|
|
|
|
|
|
color: '#4285F4',
|
|
|
|
|
|
borderRadius: [12, 12, 0, 0],
|
|
|
|
|
|
},
|
|
|
|
|
|
emphasis: {
|
|
|
|
|
|
shadowBlur: 10,
|
|
|
|
|
|
shadowOffsetX: 0,
|
|
|
|
|
|
shadowColor: 'rgba(0, 0, 0, 0.3)',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
position: 'top',
|
|
|
|
|
|
textStyle: { color: '#4285F4', fontSize: 12, fontWeight: 'bold' },
|
|
|
|
|
|
formatter: (params) => `${params.value}`,
|
|
|
|
|
|
},
|
|
|
|
|
|
barWidth: '50%',
|
|
|
|
|
|
barMaxWidth: 60,
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
this.echartsInstances.dangerOperationChart.setOption(option);
|
|
|
|
|
|
this.setupResizeHandler('dangerOperationChart', this.renderDangerOperationChart);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化所有图表
|
|
|
|
|
|
initAllCharts = () => {
|
|
|
|
|
|
if (this.isUnmounted) return;
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (this.isUnmounted) return;
|
|
|
|
|
|
this.renderDangerOperationChart();
|
|
|
|
|
|
}, 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 = {};
|
2026-04-28 16:52:44 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取过滤后的数据(添加类型检查和默认值)
|
|
|
|
|
|
getFilteredData = () => {
|
|
|
|
|
|
const { dangerSubData, selectedCompany, companyData } = this.props;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保 dangerSubData 是数组
|
|
|
|
|
|
let dataArray = Array.isArray(dangerSubData) ? dangerSubData : [];
|
|
|
|
|
|
|
|
|
|
|
|
if (dataArray.length === 0) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据选中的公司进行过滤(使用 ID 进行匹配)
|
|
|
|
|
|
if (selectedCompany) {
|
|
|
|
|
|
// 查找选中的公司名称
|
|
|
|
|
|
const selectedCompanyObj = companyData?.find((company) => company.ID === selectedCompany);
|
|
|
|
|
|
const selectedCompanyName = selectedCompanyObj?.NAME;
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedCompanyName) {
|
|
|
|
|
|
return dataArray.filter((item) => item.companyName === selectedCompanyName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return dataArray;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前页数据
|
|
|
|
|
|
getCurrentPageData = () => {
|
|
|
|
|
|
const { currentPage, pageSize } = this.state;
|
|
|
|
|
|
const filteredData = this.getFilteredData();
|
|
|
|
|
|
// 确保 filteredData 是数组
|
|
|
|
|
|
const dataArray = Array.isArray(filteredData) ? filteredData : [];
|
|
|
|
|
|
const startIndex = (currentPage - 1) * pageSize;
|
|
|
|
|
|
const endIndex = startIndex + pageSize;
|
|
|
|
|
|
return {
|
|
|
|
|
|
data: dataArray.slice(startIndex, endIndex),
|
|
|
|
|
|
total: dataArray.length,
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-21 17:10:47 +08:00
|
|
|
|
// 添加 shouldComponentUpdate 优化性能
|
|
|
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
|
|
|
|
|
// 检查状态变化
|
|
|
|
|
|
if (this.state.currentPage !== nextState.currentPage || this.state.pageSize !== nextState.pageSize) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 检查 props 变化
|
|
|
|
|
|
if (
|
|
|
|
|
|
this.props.jobTodayQty !== nextProps.jobTodayQty ||
|
|
|
|
|
|
this.props.dangerSubData !== nextProps.dangerSubData ||
|
|
|
|
|
|
this.props.selectedCompany !== nextProps.selectedCompany ||
|
|
|
|
|
|
this.props.selectedStartDate !== nextProps.selectedStartDate ||
|
|
|
|
|
|
this.props.selectedEndDate !== nextProps.selectedEndDate ||
|
|
|
|
|
|
this.props.companyData !== nextProps.companyData
|
|
|
|
|
|
) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 16:52:44 +08:00
|
|
|
|
// 渲染危险作业表格
|
|
|
|
|
|
renderDangerTable = () => {
|
|
|
|
|
|
const { selectedCompany, companyData } = this.props;
|
|
|
|
|
|
const { currentPage, pageSize } = this.state;
|
|
|
|
|
|
const { data: tableData, total } = this.getCurrentPageData();
|
|
|
|
|
|
|
2026-05-21 17:10:47 +08:00
|
|
|
|
// 如果没有数据,返回空
|
2026-04-28 16:52:44 +08:00
|
|
|
|
if (!tableData || tableData.length === 0) {
|
2026-05-21 17:20:36 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
|
padding: '50px',
|
|
|
|
|
|
color: '#999',
|
|
|
|
|
|
fontSize: '20px',
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
flex: 1,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
暂无危险作业数据
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2026-04-28 16:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 表格列配置
|
|
|
|
|
|
const columns = [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '公司',
|
|
|
|
|
|
dataIndex: 'companyName',
|
|
|
|
|
|
key: 'companyName',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
fixed: 'left',
|
|
|
|
|
|
render: (text) => <strong>{text}</strong>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '开始时间',
|
|
|
|
|
|
dataIndex: 'startDate',
|
|
|
|
|
|
key: 'startDate',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
width: 160,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '结束时间',
|
|
|
|
|
|
dataIndex: 'endDate',
|
|
|
|
|
|
key: 'endDate',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
width: 160,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '作业名称',
|
|
|
|
|
|
dataIndex: 'jobName',
|
|
|
|
|
|
key: 'jobName',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
minWidth: 120,
|
|
|
|
|
|
render: (text) => <span style={{ fontWeight: 'bold' }}>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '作业区域',
|
|
|
|
|
|
dataIndex: 'areaName',
|
|
|
|
|
|
key: 'areaName',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
minWidth: 120,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '作业地点',
|
|
|
|
|
|
dataIndex: 'place',
|
|
|
|
|
|
key: 'place',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
minWidth: 150,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '作业人员',
|
|
|
|
|
|
dataIndex: 'users',
|
|
|
|
|
|
key: 'users',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
minWidth: 150,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '监护人',
|
|
|
|
|
|
dataIndex: 'monitor',
|
|
|
|
|
|
key: 'monitor',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '审批领导',
|
|
|
|
|
|
dataIndex: 'approveUsers',
|
|
|
|
|
|
key: 'approveUsers',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
minWidth: 150,
|
|
|
|
|
|
render: (text) => <span>{text || '-'}</span>,
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2026-05-21 17:10:47 +08:00
|
|
|
|
// 不设置 y 滚动,让表格自然高度,由外层容器控制滚动
|
|
|
|
|
|
const scrollConfig = { x: columns.length * 100, y: 320 };
|
2026-04-28 16:52:44 +08:00
|
|
|
|
// 表格数据转换(添加唯一key)
|
|
|
|
|
|
const dataSource = tableData.map((item, index) => ({
|
|
|
|
|
|
key: `${item.companyName}_${item.startDate}_${index}`,
|
|
|
|
|
|
companyName: item.companyName,
|
|
|
|
|
|
startDate: item.startDate,
|
|
|
|
|
|
endDate: item.endDate,
|
|
|
|
|
|
jobName: item.jobName,
|
|
|
|
|
|
areaName: item.areaName,
|
|
|
|
|
|
place: item.place,
|
|
|
|
|
|
users: item.users,
|
|
|
|
|
|
monitor: item.monitor,
|
|
|
|
|
|
approveUsers: item.approveUsers,
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-05-21 17:10:47 +08:00
|
|
|
|
<>
|
|
|
|
|
|
<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={{ flex: 1, minHeight: 0, padding: '10px' }}>
|
|
|
|
|
|
<Table
|
|
|
|
|
|
columns={columns}
|
|
|
|
|
|
dataSource={dataSource}
|
|
|
|
|
|
pagination={false}
|
|
|
|
|
|
scroll={scrollConfig}
|
|
|
|
|
|
size="middle"
|
|
|
|
|
|
bordered
|
|
|
|
|
|
className={styles.certificateTable}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{/* 分页组件 */}
|
|
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
padding: '12px 16px',
|
|
|
|
|
|
borderTop: '1px solid #f0f0f0',
|
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
|
flexShrink: 0,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span style={{ marginRight: '8px' }}>每页显示:</span>
|
|
|
|
|
|
<Select
|
|
|
|
|
|
value={pageSize}
|
|
|
|
|
|
onChange={(size) => {
|
|
|
|
|
|
this.setState({ currentPage: 1, pageSize: size });
|
|
|
|
|
|
}}
|
|
|
|
|
|
style={{ width: 80 }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Option value={10}>10条</Option>
|
|
|
|
|
|
<Option value={20}>20条</Option>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Pagination
|
|
|
|
|
|
current={currentPage}
|
|
|
|
|
|
pageSize={pageSize}
|
|
|
|
|
|
total={total}
|
|
|
|
|
|
showQuickJumper
|
|
|
|
|
|
showTotal={(total) => `共 ${total} 条记录`}
|
|
|
|
|
|
onChange={this.handlePageChange}
|
|
|
|
|
|
/>
|
2026-05-06 15:08:13 +08:00
|
|
|
|
</div>
|
2026-04-28 16:52:44 +08:00
|
|
|
|
</div>
|
2026-05-21 17:10:47 +08:00
|
|
|
|
</>
|
2026-04-28 16:52:44 +08:00
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
|
this.isUnmounted = false;
|
2026-05-21 17:10:47 +08:00
|
|
|
|
this.initAllCharts();
|
2026-04-28 16:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps) {
|
|
|
|
|
|
// 当筛选条件变化时,重置到第一页
|
|
|
|
|
|
if (prevProps.selectedCompany !== this.props.selectedCompany) {
|
|
|
|
|
|
this.setState({ currentPage: 1 });
|
|
|
|
|
|
}
|
2026-05-21 17:10:47 +08:00
|
|
|
|
if (prevProps.selectedStartDate !== this.props.selectedStartDate) {
|
|
|
|
|
|
this.setState({ currentPage: 1 });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (prevProps.selectedEndDate !== this.props.selectedEndDate) {
|
|
|
|
|
|
this.setState({ currentPage: 1 });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 16:52:44 +08:00
|
|
|
|
// 当数据变化时,如果当前页没有数据且不是第一页,重置到第一页
|
|
|
|
|
|
if (prevProps.dangerSubData !== this.props.dangerSubData) {
|
|
|
|
|
|
const { currentPage } = this.state;
|
|
|
|
|
|
const filteredData = this.getFilteredData();
|
|
|
|
|
|
const dataArray = Array.isArray(filteredData) ? filteredData : [];
|
|
|
|
|
|
const maxPage = Math.ceil(dataArray.length / this.state.pageSize) || 1;
|
|
|
|
|
|
if (currentPage > maxPage) {
|
|
|
|
|
|
this.setState({ currentPage: 1 });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-21 17:10:47 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查 jobTodayQty 是否变化,只在数据真正变化时重新渲染图表
|
|
|
|
|
|
const prevJobTodayQty = prevProps.jobTodayQty;
|
|
|
|
|
|
const currentJobTodayQty = this.props.jobTodayQty;
|
|
|
|
|
|
const jobTodayQtyChanged = JSON.stringify(prevJobTodayQty) !== JSON.stringify(currentJobTodayQty);
|
|
|
|
|
|
|
|
|
|
|
|
if (jobTodayQtyChanged) {
|
|
|
|
|
|
this.renderDangerOperationChart();
|
|
|
|
|
|
}
|
2026-04-28 16:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
|
|
this.isUnmounted = true;
|
2026-05-21 17:10:47 +08:00
|
|
|
|
this.disposeAllCharts();
|
2026-04-28 16:52:44 +08:00
|
|
|
|
}
|
2026-05-21 17:20:36 +08:00
|
|
|
|
disabledCurrentYearDate = (current) => {
|
|
|
|
|
|
if (!current) return false;
|
|
|
|
|
|
const currentYear = new Date().getFullYear();
|
|
|
|
|
|
const selectedYear = current.year();
|
|
|
|
|
|
// 只能选择当前年份
|
|
|
|
|
|
return selectedYear !== currentYear;
|
|
|
|
|
|
};
|
2026-04-28 16:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
render() {
|
2026-05-21 17:10:47 +08:00
|
|
|
|
const { companyData, selectedCompany, selectedStartDate, selectedEndDate } = this.props;
|
2026-04-28 16:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className={styles.trainingContentWrapper}>
|
|
|
|
|
|
<div className={styles.trainingGrid}>
|
|
|
|
|
|
<div className={styles.trainingRow}>
|
|
|
|
|
|
<div className={styles.trainingCard}>
|
2026-05-21 17:10:47 +08:00
|
|
|
|
{/* 筛选器 */}
|
2026-04-28 16:52:44 +08:00
|
|
|
|
<div className={styles.monthSelectorWrapper}>
|
|
|
|
|
|
<span className={styles.monthSelectorLabel}>选择公司:</span>
|
|
|
|
|
|
<Select
|
|
|
|
|
|
value={selectedCompany}
|
|
|
|
|
|
onChange={this.handleCompanyChange}
|
|
|
|
|
|
style={{ width: 150 }}
|
|
|
|
|
|
className={styles.monthSelect}
|
|
|
|
|
|
allowClear
|
|
|
|
|
|
placeholder="全部公司"
|
|
|
|
|
|
>
|
|
|
|
|
|
{this.getCompanyOptions()}
|
|
|
|
|
|
</Select>
|
2026-05-21 17:10:47 +08:00
|
|
|
|
<span className={styles.monthSelectorLabel} style={{ margin: '0 20px' }}>
|
|
|
|
|
|
开始日期:
|
|
|
|
|
|
</span>
|
2026-05-21 17:20:36 +08:00
|
|
|
|
<DatePicker
|
|
|
|
|
|
value={selectedStartDate}
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
onChange={this.startChange}
|
|
|
|
|
|
disabledDate={this.disabledCurrentYearDate}
|
|
|
|
|
|
/>
|
2026-05-21 17:10:47 +08:00
|
|
|
|
<span className={styles.monthSelectorLabel} style={{ margin: '0 20px' }}>
|
|
|
|
|
|
结束日期:
|
|
|
|
|
|
</span>
|
2026-05-21 17:20:36 +08:00
|
|
|
|
<DatePicker
|
|
|
|
|
|
value={selectedEndDate}
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
onChange={this.endChange}
|
|
|
|
|
|
disabledDate={this.disabledCurrentYearDate}
|
|
|
|
|
|
/>
|
2026-04-28 16:52:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
{this.renderDangerTable()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-05-21 17:10:47 +08:00
|
|
|
|
{/* 第二行 - 当日工作票统计柱状图 */}
|
|
|
|
|
|
<div className={styles.trainingRow}>
|
|
|
|
|
|
<div className={styles.trainingCard}>
|
|
|
|
|
|
<div id="dangerOperationChart" className={styles.trainingChartContainer}></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-28 16:52:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default DangerJob;
|