// DangerJob.js - 危险作业页面组件
import React from 'react';
import { Table, Select, Pagination, DatePicker } from 'antd';
import echarts from 'echarts';
import styles from './../fullinter.less';
const { Option } = Select;
class DangerJob extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPage: 1,
pageSize: 10,
};
this.echartsInstances = {
dangerOperationChart: null, // 当日工作票统计柱状图
};
this.chartResizeHandlers = {};
this.isUnmounted = false;
// 添加缓存,用于比较数据是否真正变化
this.lastJobTodayQty = null;
}
// 生成公司选项(与 TrainingContent 保持一致)
getCompanyOptions = () => {
const { companyData } = this.props;
if (!companyData || companyData.length === 0) {
return ;
}
return companyData.map((company, index) => (
));
};
// 处理公司筛选变化
handleCompanyChange = (value) => {
const { onCompanyChange } = this.props;
this.setState({ currentPage: 1 }, () => {
if (onCompanyChange) {
onCompanyChange(value);
}
});
};
// 处理开始日期变化
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);
}
});
};
// 处理页码变化
handlePageChange = (page, pageSize) => {
this.setState({ currentPage: page, pageSize });
};
// 等待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 `
${params[0].name}:
${params[0].value}
`;
},
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 = {};
};
// 获取过滤后的数据(添加类型检查和默认值)
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,
};
};
// 添加 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;
}
// 渲染危险作业表格
renderDangerTable = () => {
const { selectedCompany, companyData } = this.props;
const { currentPage, pageSize } = this.state;
const { data: tableData, total } = this.getCurrentPageData();
// 如果没有数据,返回空
if (!tableData || tableData.length === 0) {
return (
暂无危险作业数据
);
}
// 表格列配置
const columns = [
{
title: '公司',
dataIndex: 'companyName',
key: 'companyName',
align: 'center',
width: 120,
fixed: 'left',
render: (text) => {text},
},
{
title: '开始时间',
dataIndex: 'startDate',
key: 'startDate',
align: 'center',
width: 160,
render: (text) => {text || '-'},
},
{
title: '结束时间',
dataIndex: 'endDate',
key: 'endDate',
align: 'center',
width: 160,
render: (text) => {text || '-'},
},
{
title: '作业名称',
dataIndex: 'jobName',
key: 'jobName',
align: 'center',
minWidth: 120,
render: (text) => {text || '-'},
},
{
title: '作业区域',
dataIndex: 'areaName',
key: 'areaName',
align: 'center',
minWidth: 120,
render: (text) => {text || '-'},
},
{
title: '作业地点',
dataIndex: 'place',
key: 'place',
align: 'center',
minWidth: 150,
render: (text) => {text || '-'},
},
{
title: '作业人员',
dataIndex: 'users',
key: 'users',
align: 'center',
minWidth: 150,
render: (text) => {text || '-'},
},
{
title: '监护人',
dataIndex: 'monitor',
key: 'monitor',
align: 'center',
width: 120,
render: (text) => {text || '-'},
},
{
title: '审批领导',
dataIndex: 'approveUsers',
key: 'approveUsers',
align: 'center',
minWidth: 150,
render: (text) => {text || '-'},
},
];
// 不设置 y 滚动,让表格自然高度,由外层容器控制滚动
const scrollConfig = { x: columns.length * 100, y: 320 };
// 表格数据转换(添加唯一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 (
<>
当日各公司危险作业清单
{/* 分页组件 */}
每页显示:
`共 ${total} 条记录`}
onChange={this.handlePageChange}
/>
>
);
};
componentDidMount() {
this.isUnmounted = false;
this.initAllCharts();
}
componentDidUpdate(prevProps) {
// 当筛选条件变化时,重置到第一页
if (prevProps.selectedCompany !== this.props.selectedCompany) {
this.setState({ currentPage: 1 });
}
if (prevProps.selectedStartDate !== this.props.selectedStartDate) {
this.setState({ currentPage: 1 });
}
if (prevProps.selectedEndDate !== this.props.selectedEndDate) {
this.setState({ currentPage: 1 });
}
// 当数据变化时,如果当前页没有数据且不是第一页,重置到第一页
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 });
}
}
// 检查 jobTodayQty 是否变化,只在数据真正变化时重新渲染图表
const prevJobTodayQty = prevProps.jobTodayQty;
const currentJobTodayQty = this.props.jobTodayQty;
const jobTodayQtyChanged = JSON.stringify(prevJobTodayQty) !== JSON.stringify(currentJobTodayQty);
if (jobTodayQtyChanged) {
this.renderDangerOperationChart();
}
}
componentWillUnmount() {
this.isUnmounted = true;
this.disposeAllCharts();
}
disabledCurrentYearDate = (current) => {
if (!current) return false;
const currentYear = new Date().getFullYear();
const selectedYear = current.year();
// 只能选择当前年份
return selectedYear !== currentYear;
};
render() {
const { companyData, selectedCompany, selectedStartDate, selectedEndDate } = this.props;
return (
{/* 筛选器 */}
选择公司:
开始日期:
结束日期:
{this.renderDangerTable()}
{/* 第二行 - 当日工作票统计柱状图 */}
);
}
}
export default DangerJob;