diff --git a/src/layout/FullOther/DangerJob.js b/src/layout/FullOther/DangerJob.js index 27468a8..1f36159 100644 --- a/src/layout/FullOther/DangerJob.js +++ b/src/layout/FullOther/DangerJob.js @@ -1,6 +1,7 @@ // DangerJob.js - 危险作业页面组件 import React from 'react'; -import { Table, Select, Pagination } from 'antd'; +import { Table, Select, Pagination, DatePicker } from 'antd'; +import echarts from 'echarts'; import styles from './../fullinter.less'; const { Option } = Select; @@ -10,9 +11,15 @@ class DangerJob extends React.Component { super(props); this.state = { currentPage: 1, - pageSize: 20, + pageSize: 10, }; + this.echartsInstances = { + dangerOperationChart: null, // 当日工作票统计柱状图 + }; + this.chartResizeHandlers = {}; this.isUnmounted = false; + // 添加缓存,用于比较数据是否真正变化 + this.lastJobTodayQty = null; } // 生成公司选项(与 TrainingContent 保持一致) @@ -23,8 +30,6 @@ class DangerJob extends React.Component { } return companyData.map((company, index) => ( )); @@ -40,14 +45,222 @@ class DangerJob extends React.Component { }); }; + // 处理开始日期变化 + 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 }); }; - // 处理每页条数变化 - handleShowSizeChange = (current, size) => { - this.setState({ currentPage: 1, pageSize: size }); + // 等待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 = {}; }; // 获取过滤后的数据(添加类型检查和默认值) @@ -88,33 +301,48 @@ class DangerJob extends React.Component { }; }; + // 添加 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) { - // 获取选中的公司名称用于显示 - let companyName = ''; - if (selectedCompany && companyData) { - const selectedCompanyObj = companyData.find((company) => company.ID === selectedCompany); - companyName = selectedCompanyObj?.NAME || ''; - } return (
- {selectedCompany ? `${companyName}暂无危险作业数据` : '暂无危险作业数据'} + 暂无危险作业数据
); } @@ -196,10 +424,8 @@ class DangerJob extends React.Component { }, ]; - // 计算横向滚动宽度(如果列数过多) - const scrollX = columns.reduce((sum, col) => sum + (col.width || 120), 0); - const scrollConfig = scrollX > 1200 ? { x: scrollX } : {}; - + // 不设置 y 滚动,让表格自然高度,由外层容器控制滚动 + const scrollConfig = { x: columns.length * 100, y: 320 }; // 表格数据转换(添加唯一key) const dataSource = tableData.map((item, index) => ({ key: `${item.companyName}_${item.startDate}_${index}`, @@ -215,72 +441,72 @@ class DangerJob extends React.Component { })); return ( -
-
- 当日各公司危险作业清单 -
-
- - - {/* 分页组件 */} -
- {/* 自定义每页条数选择器 */} -
- 每页显示: - + <> +
+
+ 当日各公司危险作业清单 +
+
+
+ + {/* 分页组件 */} +
+
+ 每页显示: + +
+ `共 ${total} 条记录`} + onChange={this.handlePageChange} + />
- `共 ${total} 条记录`} - onChange={this.handlePageChange} - onShowSizeChange={false} - // pageSizeOptions={['10', '20']} - /> - + ); }; componentDidMount() { this.isUnmounted = false; + this.initAllCharts(); } componentDidUpdate(prevProps) { @@ -288,6 +514,13 @@ class DangerJob extends React.Component { 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; @@ -298,21 +531,38 @@ class DangerJob extends React.Component { 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 } = this.props; + const { companyData, selectedCompany, selectedStartDate, selectedEndDate } = this.props; return (
- {/* 公司筛选器 */} + {/* 筛选器 */}
选择公司: + + 开始日期: + + + + 结束日期: + +
{this.renderDangerTable()}
+ {/* 第二行 - 当日工作票统计柱状图 */} +
+
+
+
+
); diff --git a/src/layout/FullOther/HomeContent.js b/src/layout/FullOther/HomeContent.js index 45851c5..4c5d5d9 100644 --- a/src/layout/FullOther/HomeContent.js +++ b/src/layout/FullOther/HomeContent.js @@ -575,7 +575,7 @@ class HomeContent extends React.Component { if (this.isUnmounted) return; this.riskLevel(); this.safeCheckChart(); - this.dangerOperation(); + // this.dangerOperation(); this.backLog(); }, 100); }; @@ -970,15 +970,15 @@ class HomeContent extends React.Component { >
露天矿 - 1家 + --家
地下矿 - 12家 + --家
尾矿库 - 13家 + --家
)} diff --git a/src/layout/FullScreenInter.js b/src/layout/FullScreenInter.js index 487e7bf..3be5407 100644 --- a/src/layout/FullScreenInter.js +++ b/src/layout/FullScreenInter.js @@ -14,6 +14,7 @@ import RiskControl from './FullOther/RiskControl'; import ClassBuild from './FullOther/ClassBuild'; import DangerJob from './FullOther/DangerJob'; import HiddenSolve from './FullOther/HiddenSolve'; +import moment from 'moment'; // 添加这一行 const getScale = () => { const width = 1920, @@ -30,7 +31,7 @@ class FullScreen extends React.Component { nowDate: '', riskTypeRate: [], riskTotal: [], - jobTodayQty: [], + // jobTodayQty: [], hiddenSummary: [], taskTop3: [], scale: getScale(), @@ -77,8 +78,11 @@ class FullScreen extends React.Component { riskSubData: {}, classSubData: [], dangerSubData: {}, + jobTodayQty: [], dangerCompanyData: [], selectedDangerCompany: '', + selectedStartDate: moment(), + selectedEndDate: moment(), announcementDetail: null, announcementDetailLoading: false, @@ -289,7 +293,7 @@ class FullScreen extends React.Component { if (ret && !this.isUnmounted) { this.setState({ riskTypeRate: ret.riskTypeRate || [], - jobTodayQty: ret.jobTodayQty || [], + // jobTodayQty: ret.jobTodayQty || [], hiddenSummary: ret.hiddenSummary || [], taskTop3: ret.taskTop3 || [], }); @@ -589,17 +593,29 @@ class FullScreen extends React.Component { this.getDangerSubData(); }); }; + handleStartDateChange = (startDate) => { + this.setState({ selectedStartDate: startDate }, () => { + this.getDangerSubData(); + }); + }; + handleEndDateChange = (endDate) => { + this.setState({ selectedEndDate: endDate }, () => { + this.getDangerSubData(); + }); + }; getDangerSubData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); + json.Parameter1 = this.state.selectedStartDate.format('YYYY-MM-DD'); + json.Parameter2 = this.state.selectedEndDate.format('YYYY-MM-DD'); this.props.dispatch({ type: 'app/getDataByPost', payload: json, url: 'BI/BIKanBanController/JobManage', onComplete: (ret) => { if (ret && !this.isUnmounted) { - let filteredData = ret; + let filteredData = ret.jobList; if (this.state.selectedDangerCompany) { const selectedCompanyObj = this.state.dangerCompanyData?.find( (company) => company.ID === this.state.selectedDangerCompany @@ -612,6 +628,7 @@ class FullScreen extends React.Component { this.setState({ dangerSubData: filteredData, + jobTodayQty: ret.jobTodayQty, }); } }, @@ -670,9 +687,14 @@ class FullScreen extends React.Component { return ( ); } @@ -761,7 +783,7 @@ class FullScreen extends React.Component { riskTypeRate={riskTypeRate} riskTotal={riskTotal} hiddenSummary={hiddenSummary} - jobTodayQty={jobTodayQty} + // jobTodayQty={jobTodayQty} taskTop3={taskTop3} mediaList={mediaList} announcementList={announcementList} diff --git a/src/layout/fullinter.less b/src/layout/fullinter.less index ac31893..72bd5ae 100644 --- a/src/layout/fullinter.less +++ b/src/layout/fullinter.less @@ -174,7 +174,7 @@ font-weight: bold; text-align: left; color: #000; - margin-bottom: 24px; + margin-bottom: 10px; } .infoCard { display: flex;