// 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;