From e3faa7549b40d35a0920843f7a20cb25bf31d087 Mon Sep 17 00:00:00 2001 From: yunkexin <760754045@qq.com> Date: Tue, 28 Apr 2026 16:52:44 +0800 Subject: [PATCH 1/3] npm install react-slick slick-carousel --- package-lock.json | 48 +- package.json | 1 + src/layout/FullOther/ClassBuild.js | 228 +++++++++ src/layout/FullOther/DangerJob.js | 312 +++++++++++ src/layout/FullOther/HiddenSolve.js | 581 +++++++++++++++++++++ src/layout/FullOther/HomeContent.js | 621 ++++++++++++++++------ src/layout/FullOther/RiskControl.js | 515 +++++++++++++++++++ src/layout/FullOther/TrainingContent.js | 6 +- src/layout/FullScreenInter.js | 654 +++++++++++++++++++++--- src/layout/fullinter.less | 133 ++++- yarn.lock | 16 +- 11 files changed, 2846 insertions(+), 269 deletions(-) create mode 100644 src/layout/FullOther/ClassBuild.js create mode 100644 src/layout/FullOther/DangerJob.js create mode 100644 src/layout/FullOther/HiddenSolve.js create mode 100644 src/layout/FullOther/RiskControl.js diff --git a/package-lock.json b/package-lock.json index 598a7b5..c68223d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "react-loadable": "5.5.0", "react-orgchart": "^1.0.5", "react-resizable": "^1.10.1", + "react-slick": "^0.31.0", "react-to-print": "^2.0.0-alpha-2", "react-umeditor": "1.0.12", "react-websocket": "2.0.1", @@ -2637,6 +2638,22 @@ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz", "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w==" }, + "node_modules/antd/node_modules/react-slick": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/react-slick/-/react-slick-0.25.2.tgz", + "integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==", + "dependencies": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", @@ -21055,19 +21072,18 @@ "license": "BSD-3-Clause" }, "node_modules/react-slick": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz", - "integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==", + "version": "0.31.0", + "resolved": "https://registry.npmmirror.com/react-slick/-/react-slick-0.31.0.tgz", + "integrity": "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==", "dependencies": { "classnames": "^2.2.5", - "enquire.js": "^2.1.6", "json2mq": "^0.2.0", "lodash.debounce": "^4.0.8", "resize-observer-polyfill": "^1.5.0" }, "peerDependencies": { - "react": "^0.14.0 || ^15.0.1 || ^16.0.0", - "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0" + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/react-test-renderer": { @@ -22508,7 +22524,6 @@ "version": "1.8.1", "resolved": "https://registry.npmmirror.com/slick-carousel/-/slick-carousel-1.8.1.tgz", "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==", - "license": "MIT", "peerDependencies": { "jquery": ">=1.8.0" } @@ -28673,6 +28688,18 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz", "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w==" + }, + "react-slick": { + "version": "0.25.2", + "resolved": "https://registry.npmmirror.com/react-slick/-/react-slick-0.25.2.tgz", + "integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==", + "requires": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + } } } }, @@ -42912,12 +42939,11 @@ } }, "react-slick": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz", - "integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==", + "version": "0.31.0", + "resolved": "https://registry.npmmirror.com/react-slick/-/react-slick-0.31.0.tgz", + "integrity": "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==", "requires": { "classnames": "^2.2.5", - "enquire.js": "^2.1.6", "json2mq": "^0.2.0", "lodash.debounce": "^4.0.8", "resize-observer-polyfill": "^1.5.0" diff --git a/package.json b/package.json index 00e0845..757a26f 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "react-loadable": "5.5.0", "react-orgchart": "^1.0.5", "react-resizable": "^1.10.1", + "react-slick": "^0.31.0", "react-to-print": "^2.0.0-alpha-2", "react-umeditor": "1.0.12", "react-websocket": "2.0.1", diff --git a/src/layout/FullOther/ClassBuild.js b/src/layout/FullOther/ClassBuild.js new file mode 100644 index 0000000..293ffd6 --- /dev/null +++ b/src/layout/FullOther/ClassBuild.js @@ -0,0 +1,228 @@ +// ClassBuild.js - 班组建设页面组件 +import React from 'react'; +import { Table } from 'antd'; +import styles from './../fullinter.less'; +import echarts from 'echarts'; + +class ClassBuild extends React.Component { + constructor(props) { + super(props); + this.echartsInstances = { + typeBarChart: null, // 班组风险类别柱状图 + }; + this.chartResizeHandlers = {}; + this.isUnmounted = 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); + }); + }; + + // 班组风险类别柱状图 + renderTypeBarChart = async () => { + if (this.isUnmounted) return; + const elementExists = await this.waitForElement('typeBarChart'); + if (!elementExists || this.isUnmounted) return; + + if (this.echartsInstances.typeBarChart) { + this.echartsInstances.typeBarChart.dispose(); + this.echartsInstances.typeBarChart = null; + } + + const chartDom = document.getElementById('typeBarChart'); + if (!chartDom) return; + + this.echartsInstances.typeBarChart = echarts.init(chartDom); + + const { classSubData } = this.props; + const companyList = classSubData || []; + + if (companyList.length === 0) { + this.echartsInstances.typeBarChart.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; + } + + // 注意:数据结构是 { companyName: '邦泰', rate: '52%' } + const companyNames = companyList.map((item) => item.companyName); + const rates = companyList.map((item) => { + // 处理 rate 可能是字符串带百分号的情况 + const rateValue = typeof item.rate === 'string' ? parseFloat(item.rate.replace('%', '')) : item.rate; + return isNaN(rateValue) ? 0 : rateValue; + }); + + const option = { + title: { + text: '本月班前会议及时完成率分析', + x: 'center', + y: '5%', + textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { type: 'shadow' }, + formatter: function (params) { + let result = `${params[0].axisValue}
`; + params.forEach((param) => { + result += `${param.marker}${param.seriesName}: ${param.value}%
`; + }); + return result; + }, + }, + grid: { + left: '8%', + right: '5%', + top: '18%', + bottom: '8%', + containLabel: true, + }, + xAxis: [ + { + type: 'category', + data: companyNames, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + textStyle: { color: '#000' }, + interval: 0, + fontSize: 12, + }, + }, + ], + yAxis: [ + { + type: 'value', + show: true, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + show: true, + textStyle: { color: '#000' }, + formatter: '{value}%', + }, + splitLine: { show: true, lineStyle: { color: '#e0e0e0', type: 'dashed' } }, + }, + ], + series: [ + { + name: '班组建设完成率', + type: 'bar', + data: rates, + itemStyle: { + normal: { + color: '#4285F4', + borderRadius: [4, 4, 0, 0], + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#4285F4', fontSize: 12 }, + formatter: (params) => `${params.value}%`, + }, + barWidth: '50%', + }, + ], + }; + + this.echartsInstances.typeBarChart.setOption(option); + this.setupResizeHandler('typeBarChart', this.renderTypeBarChart); + }; + + setupResizeHandler = (chartName, renderMethod) => { + const resizeHandler = () => { + if (this.echartsInstances[chartName] && !this.isUnmounted) { + this.echartsInstances[chartName].resize(); + } + }; + this.chartResizeHandlers[chartName] = resizeHandler; + window.addEventListener('resize', resizeHandler); + }; + + initAllCharts = () => { + if (this.isUnmounted) return; + setTimeout(() => { + if (this.isUnmounted) return; + this.renderTypeBarChart(); + }, 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 = {}; + }; + + componentDidMount() { + this.isUnmounted = false; + this.initAllCharts(); + } + + // 修复:监听 classSubData 而不是 riskSubData + componentDidUpdate(prevProps) { + if (prevProps.classSubData !== this.props.classSubData) { + this.renderTypeBarChart(); + } + } + + componentWillUnmount() { + this.isUnmounted = true; + this.disposeAllCharts(); + } + + render() { + return ( +
+
+
+
+
+
+
+
+
+ ); + } +} + +export default ClassBuild; diff --git a/src/layout/FullOther/DangerJob.js b/src/layout/FullOther/DangerJob.js new file mode 100644 index 0000000..e38e3c7 --- /dev/null +++ b/src/layout/FullOther/DangerJob.js @@ -0,0 +1,312 @@ +// DangerJob.js - 危险作业页面组件 +import React from 'react'; +import { Table, Select, Pagination } from 'antd'; +import styles from './../fullinter.less'; + +const { Option } = Select; + +class DangerJob extends React.Component { + constructor(props) { + super(props); + this.state = { + currentPage: 1, + pageSize: 10, + }; + this.isUnmounted = false; + } + + // 生成公司选项(与 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); + } + }); + }; + + // 处理页码变化 + handlePageChange = (page, pageSize) => { + this.setState({ currentPage: page, pageSize }); + }; + + // 处理每页条数变化 + handleShowSizeChange = (current, size) => { + this.setState({ currentPage: 1, pageSize: size }); + }; + + // 获取过滤后的数据(添加类型检查和默认值) + 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, + }; + }; + + // 渲染危险作业表格 + 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}暂无危险作业数据` : '暂无危险作业数据'} +
+ ); + } + + // 表格列配置 + 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 || '-'}, + }, + ]; + + // 计算横向滚动宽度(如果列数过多) + const scrollX = columns.reduce((sum, col) => sum + (col.width || 120), 0); + const scrollConfig = scrollX > 1200 ? { x: scrollX } : {}; + + // 表格数据转换(添加唯一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} + onShowSizeChange={this.handleShowSizeChange} + pageSizeOptions={['10', '20', '50']} + /> +
+ + ); + }; + + componentDidMount() { + this.isUnmounted = false; + } + + componentDidUpdate(prevProps) { + // 当筛选条件变化时,重置到第一页 + if (prevProps.selectedCompany !== this.props.selectedCompany) { + 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 }); + } + } + } + + componentWillUnmount() { + this.isUnmounted = true; + } + + render() { + const { companyData, selectedCompany } = this.props; + + return ( +
+
+
+
+ {/* 公司筛选器 */} +
+ 选择公司: + +
+ {this.renderDangerTable()} +
+
+
+
+ ); + } +} + +export default DangerJob; diff --git a/src/layout/FullOther/HiddenSolve.js b/src/layout/FullOther/HiddenSolve.js new file mode 100644 index 0000000..d0f5c1e --- /dev/null +++ b/src/layout/FullOther/HiddenSolve.js @@ -0,0 +1,581 @@ +// HiddenSolve.js - 隐患解决页面组件 +import React from 'react'; +import { Select } from 'antd'; +import styles from './../fullinter.less'; +import echarts from 'echarts'; + +const { Option } = Select; + +class HiddenSolve extends React.Component { + constructor(props) { + super(props); + this.echartsInstances = { + hiddenBarChart: null, // 各公司隐患统计柱状图(使用 hiddenList) + hiddenRectifyChart: null, // 隐患整改情况柱状图(使用 hiddenRectifyList) + }; + this.chartResizeHandlers = {}; + this.isUnmounted = false; + } + + // 获取公司选项(使用 props 传入的 companyData,与 TrainingContent 一致) + getCompanyOptions = () => { + const { companyData } = this.props; + if (!companyData || companyData.length === 0) { + return []; + } + return companyData.map((company, index) => ( + + )); + }; + + // 根据选中的公司ID获取公司名称(用于筛选模拟数据) + getSelectedCompanyName = () => { + const { companyData, selectedCompany } = this.props; + if (!selectedCompany || !companyData || companyData.length === 0) { + return null; + } + const selectedCompanyObj = companyData.find((company) => company.ID === selectedCompany); + return selectedCompanyObj ? selectedCompanyObj.NAME : null; + }; + + 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); + }); + }; + + // 图表1: 各公司隐患统计柱状图(使用 hiddenList 数据) + renderHiddenBarChart = async () => { + if (this.isUnmounted) return; + const elementExists = await this.waitForElement('hiddenBarChart'); + if (!elementExists || this.isUnmounted) return; + + if (this.echartsInstances.hiddenBarChart) { + this.echartsInstances.hiddenBarChart.dispose(); + this.echartsInstances.hiddenBarChart = null; + } + + const chartDom = document.getElementById('hiddenBarChart'); + if (!chartDom) return; + + this.echartsInstances.hiddenBarChart = echarts.init(chartDom); + + const { hiddenSubData } = this.props; + let hiddenList = hiddenSubData?.hiddenList || []; + + if (hiddenList.length === 0) { + this.echartsInstances.hiddenBarChart.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 companyNames = hiddenList.map((item) => item.companyName); + const majorCounts = hiddenList.map((item) => item.majorCount); + const generalCounts = hiddenList.map((item) => item.generalCount); + + const option = { + title: { + text: '各公司隐患统计', + x: 'center', + y: '5%', + textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { type: 'shadow' }, + formatter: function (params) { + let result = `${params[0].axisValue}
`; + params.forEach((param) => { + result += `${param.marker}${param.seriesName}: ${param.value}
`; + }); + return result; + }, + }, + legend: { + data: ['重大隐患', '一般隐患'], + orient: 'vertical', + right: '3%', + top: '5%', + itemGap: 16, + itemWidth: 18, + itemHeight: 12, + textStyle: { color: '#000', fontSize: 14 }, + }, + grid: { + left: '8%', + right: '5%', + top: '18%', + bottom: '8%', + containLabel: true, + }, + xAxis: [ + { + type: 'category', + data: companyNames, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + textStyle: { color: '#000' }, + rotate: companyNames.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: majorCounts, + itemStyle: { + normal: { + color: '#c92a2a', // 红色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#c92a2a', fontSize: 12 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '35%', + }, + { + name: '一般隐患', + type: 'bar', + data: generalCounts, + itemStyle: { + normal: { + color: '#4285F4', // 蓝色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#4285F4', fontSize: 12 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '35%', + }, + ], + }; + + this.echartsInstances.hiddenBarChart.setOption(option); + this.setupResizeHandler('hiddenBarChart', this.renderHiddenBarChart); + }; + + // 图表2: 隐患整改情况柱状图(使用 hiddenRectifyList 数据) + renderHiddenRectifyChart = async () => { + if (this.isUnmounted) return; + const elementExists = await this.waitForElement('hiddenRectifyChart'); + if (!elementExists || this.isUnmounted) return; + + if (this.echartsInstances.hiddenRectifyChart) { + this.echartsInstances.hiddenRectifyChart.dispose(); + this.echartsInstances.hiddenRectifyChart = null; + } + + const chartDom = document.getElementById('hiddenRectifyChart'); + if (!chartDom) return; + + this.echartsInstances.hiddenRectifyChart = echarts.init(chartDom); + + const { hiddenSubData } = this.props; + let hiddenRectifyList = hiddenSubData?.hiddenRectifyList || []; + + if (hiddenRectifyList.length === 0) { + this.echartsInstances.hiddenRectifyChart.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 companyNames = hiddenRectifyList.map((item) => item.companyName); + const majorTotal = hiddenRectifyList.map((item) => item.majorCount); + const majorRectified = hiddenRectifyList.map((item) => (item.majorCount || 0) - (item.majorCountNo || 0)); + const generalTotal = hiddenRectifyList.map((item) => item.generalCount); + const generalRectified = hiddenRectifyList.map((item) => (item.generalCount || 0) - (item.generalCountNo || 0)); + + const option = { + title: { + text: '各公司隐患整改情况', + x: 'center', + y: '5%', + textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { type: 'shadow' }, + formatter: function (params) { + let result = `${params[0].axisValue}
`; + params.forEach((param) => { + result += `${param.marker}${param.seriesName}: ${param.value}
`; + }); + return result; + }, + }, + legend: { + data: ['重大隐患总数', '重大隐患已整改', '一般隐患总数', '一般隐患已整改'], + orient: 'vertical', + right: '3%', + top: '5%', + itemGap: 12, + itemWidth: 18, + itemHeight: 12, + textStyle: { color: '#000', fontSize: 12 }, + }, + grid: { + left: '8%', + right: '5%', + top: '18%', + bottom: '8%', + containLabel: true, + }, + xAxis: [ + { + type: 'category', + data: companyNames, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + textStyle: { color: '#000' }, + rotate: companyNames.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: majorTotal, + itemStyle: { + normal: { + color: '#c92a2a', // 红色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#c92a2a', fontSize: 11 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '20%', + }, + { + name: '重大隐患已整改', + type: 'bar', + data: majorRectified, + itemStyle: { + normal: { + color: '#ffa94d', // 橙色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#ffa94d', fontSize: 11 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '20%', + }, + { + name: '一般隐患总数', + type: 'bar', + data: generalTotal, + itemStyle: { + normal: { + color: '#4285F4', // 蓝色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#4285F4', fontSize: 11 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '20%', + }, + { + name: '一般隐患已整改', + type: 'bar', + data: generalRectified, + itemStyle: { + normal: { + color: '#ffe066', // 黄色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#d4a000', fontSize: 11 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '20%', + }, + ], + }; + + this.echartsInstances.hiddenRectifyChart.setOption(option); + this.setupResizeHandler('hiddenRectifyChart', this.renderHiddenRectifyChart); + }; + + // 排名列表组件(支持公司筛选) + renderRankingList = () => { + const { hiddenSubData, selectedCompany, onCompanyChange, companyData } = this.props; + let hiddenRanking = hiddenSubData?.hiddenRanking || []; + + // 根据选中的公司筛选数据 + const selectedCompanyName = this.getSelectedCompanyName(); + if (selectedCompanyName) { + hiddenRanking = hiddenRanking.filter((item) => item.companyName === selectedCompanyName); + } + + if (hiddenRanking.length === 0) { + return ( +
+ {selectedCompanyName ? `${selectedCompanyName} 暂无隐患排名数据` : '暂无隐患排名数据'} +
+ ); + } + + return ( +
+
+ 隐患排名 +
+ {/* 公司筛选器 - 与 TrainingContent 样式保持一致 */} +
+ 选择公司: + +
+
+
+ + + + + + + + + + {hiddenRanking.map((item, index) => ( + + + + + + + ))} + +
排名公司 + 隐患名称 + 数量
+ + {index + 1} + + + {item.companyName} + {item.hiddenName} + {item.qty} +
+
+
+ ); + }; + + setupResizeHandler = (chartName, renderMethod) => { + const resizeHandler = () => { + if (this.echartsInstances[chartName] && !this.isUnmounted) { + this.echartsInstances[chartName].resize(); + } + }; + this.chartResizeHandlers[chartName] = resizeHandler; + window.addEventListener('resize', resizeHandler); + }; + + initAllCharts = () => { + if (this.isUnmounted) return; + setTimeout(() => { + if (this.isUnmounted) return; + this.renderHiddenBarChart(); + this.renderHiddenRectifyChart(); + }, 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 = {}; + }; + + componentDidMount() { + this.isUnmounted = false; + this.initAllCharts(); + } + + componentDidUpdate(prevProps) { + // 当 hiddenSubData 或 selectedCompany 变化时重新渲染图表 + if ( + prevProps.hiddenSubData !== this.props.hiddenSubData || + prevProps.selectedCompany !== this.props.selectedCompany + ) { + this.renderHiddenBarChart(); + this.renderHiddenRectifyChart(); + } + } + + componentWillUnmount() { + this.isUnmounted = true; + this.disposeAllCharts(); + } + + render() { + const { selectedCompany, onCompanyChange } = this.props; + + return ( +
+
+ {/* 第一行 - 柱状图(left)+ 排名列表(right) */} +
+
+ {/* 添加公司筛选器(左上角,与 TrainingContent 样式一致) */} +
+
+
+ {this.renderRankingList()} +
+
+ {/* 第二行 - 隐患整改情况柱状图 */} +
+
+
+
+
+
+
+ ); + } +} + +export default HiddenSolve; diff --git a/src/layout/FullOther/HomeContent.js b/src/layout/FullOther/HomeContent.js index 10bdee3..ca5e83f 100644 --- a/src/layout/FullOther/HomeContent.js +++ b/src/layout/FullOther/HomeContent.js @@ -1,13 +1,18 @@ // HomeContent.js import React, { useEffect, useRef } from 'react'; -import { Row, Col, Icon, Carousel } from 'antd'; +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 } from '../../utils/common'; class HomeContent extends React.Component { constructor(props) { super(props); - this.carouselRef = React.createRef(); + this.sliderRef = React.createRef(); this.echartsInstances = { riskLevel: null, safeCheckChart: null, @@ -16,6 +21,7 @@ class HomeContent extends React.Component { }; this.chartResizeHandlers = {}; this.isUnmounted = false; + this.autoplayTimer = null; } waitForElement = (elementId, maxRetries = 10) => { @@ -111,46 +117,6 @@ class HomeContent extends React.Component { window.addEventListener('resize', resizeHandler); }; - transformDat = (originalData, barTopColor, num) => { - if (!originalData || !Array.isArray(originalData) || originalData.length === 0) { - return { companyNames: [], series: [], legendData: [] }; - } - - let allTypes = []; - if (num == 1) { - allTypes = [...new Set(originalData.flatMap((item) => item.details?.map((detail) => detail.jobName)))]; - } else { - allTypes = [...new Set(originalData.flatMap((item) => item.details?.map((detail) => detail.name)))]; - } - - allTypes = allTypes.filter(Boolean); - const companyNames = originalData?.map((item) => item.company); - const series = allTypes?.map((typeName, index) => ({ - name: typeName, - type: 'bar', - itemStyle: { - normal: { - color: function (params) { - if (num == 1) { - return barTopColor[index]; - } else { - return new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { offset: 0, color: barTopColor[index][0] }, - { offset: 1, color: barTopColor[index][1] }, - ]); - } - }, - }, - }, - data: originalData?.map((company) => { - const detail = company.details?.find((d) => (num == 1 ? d.jobName : d.name) === typeName); - return detail ? detail.qty : 0; - }), - })); - - return { companyNames, series, legendData: allTypes }; - }; - safeCheckChart = async () => { if (this.isUnmounted) return; const elementExists = await this.waitForElement('safeCheckChart'); @@ -164,48 +130,130 @@ class HomeContent extends React.Component { let safeCheckCharts = document.getElementById('safeCheckChart'); if (!safeCheckCharts) return; - const barTopColor = ['#02c3f1', '#53e568', '#a154e9']; - const jobData = this.props.jobTodayTop3 || []; + const hiddenSummary = this.props.hiddenSummary || {}; - if (jobData.length === 0) return; + // 检查是否有有效数据 + 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); - let xdata = this.transformDat(jobData, barTopColor, 1); this.echartsInstances.safeCheckChart.setOption({ title: { - text: '当日工作票排名前三家的数据', - textStyle: { fontSize: 16, color: '#fff' }, + 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}
`; + params.forEach((param) => { + result += `${param.marker}${param.seriesName}: ${param.value}
`; + }); + return result; + }, }, - tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, legend: { - data: xdata.legendData, - align: 'right', - right: 10, - itemGap: 16, - itemWidth: 18, + data: legendData, + right: 0, + top: '10%', + itemGap: 12, + itemWidth: 16, itemHeight: 10, - textStyle: { color: '#fff', fontSize: 14 }, + textStyle: { color: '#000', fontSize: 12 }, + }, + color: seriesData.map((item) => item.color), + grid: { + left: '5%', + right: '8%', + top: '28%', + bottom: '5%', + containLabel: true, }, - color: barTopColor, - grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: [ { type: 'category', - data: xdata.companyNames, - axisLine: { lineStyle: { color: '#3eb2e8' } }, - axisLabel: { textStyle: { color: '#fff' } }, + data: ['当月隐患'], + axisLine: { lineStyle: { color: '#c4c6c9' } }, + axisLabel: { textStyle: { color: '#000', fontSize: 14 } }, + axisTick: { show: false }, }, ], yAxis: [ { type: 'value', + name: '数量', + nameTextStyle: { color: '#000' }, axisLine: { show: false }, - axisLabel: { textStyle: { color: '#fff' } }, - splitLine: { lineStyle: { color: '#4784e8' } }, + axisTick: { show: false }, + splitLine: { show: false }, + axisLabel: { textStyle: { color: '#000' } }, }, ], - series: xdata.series, + 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 = () => { @@ -230,9 +278,33 @@ class HomeContent extends React.Component { const dangerOperationCharts = document.getElementById('dangerOperationChart'); if (!dangerOperationCharts) return; - const linkData = this.props.linkSum || []; - if (linkData.length === 0) 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); @@ -253,10 +325,10 @@ class HomeContent extends React.Component { formatter: function (params) { const color = params[0].color; return `
- - ${params[0].name}: - ${params[0].value} -
`; + + ${params[0].name}: + ${params[0].value} + `; }, backgroundColor: 'rgba(255, 255, 255, 0.5)', borderColor: '#FFFFFF', @@ -268,7 +340,7 @@ class HomeContent extends React.Component { { type: 'category', data: xAxisData, - axisLine: { lineStyle: { color: '#000' } }, + axisLine: { lineStyle: { color: '#c4c6c9' } }, axisTick: { show: false }, axisLabel: { textStyle: { color: '#000' }, rotate: 30, interval: 0 }, }, @@ -374,17 +446,17 @@ class HomeContent extends React.Component { }, legend: { data: ['培训人次', '培训场次'], - orient: 'vertical', // 垂直排列 - right: 0, // 水平居中 - top: 'middle', // 垂直居中 - itemGap: 16, // 图例项间隔 + orient: 'vertical', + right: 0, + top: 'middle', + itemGap: 16, itemWidth: 18, itemHeight: 12, textStyle: { color: '#000', fontSize: 14 }, }, grid: { left: '5%', - right: '5%', // 为右侧垂直图例留出空间 + right: '5%', top: '18%', bottom: '5%', containLabel: true, @@ -393,8 +465,8 @@ class HomeContent extends React.Component { { type: 'category', data: listNAME, - axisLine: { show: false }, // 隐藏x轴线 - axisTick: { show: false }, // 隐藏x轴刻度线 + axisLine: { lineStyle: { color: '#c4c6c9' } }, + axisTick: { show: false }, axisLabel: { textStyle: { color: '#000' }, rotate: listNAME.length > 4 ? 15 : 0, @@ -405,16 +477,16 @@ class HomeContent extends React.Component { yAxis: [ { type: 'value', - show: true, // 显示y轴 - axisLine: { show: false }, // 隐藏y轴线 - axisTick: { show: false }, // 隐藏y轴刻度线 + show: true, + axisLine: { show: false }, + axisTick: { show: false }, axisLabel: { - show: true, // 显示数值标签 + show: true, textStyle: { color: '#000' }, }, - splitLine: { show: false }, // 隐藏横向网格线 - name: '', // 不显示单位名称 - nameTextStyle: { show: false }, // 隐藏单位文字 + splitLine: { show: false }, + name: '', + nameTextStyle: { show: false }, }, ], series: [ @@ -424,8 +496,8 @@ class HomeContent extends React.Component { data: monthPersonCount, itemStyle: { normal: { - color: '#4285F4', // 蓝色 - barBorderRadius: 12, // 柱体圆角 + color: '#4285F4', + barBorderRadius: 12, }, }, label: { @@ -442,8 +514,8 @@ class HomeContent extends React.Component { data: monthRecordCount, itemStyle: { normal: { - color: '#ffe066', // 黄色 - barBorderRadius: 12, // 柱体圆角(上左、上右、下右、下左) + color: '#ffe066', + barBorderRadius: 12, }, }, label: { @@ -468,6 +540,28 @@ class HomeContent extends React.Component { 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(() => { @@ -500,26 +594,52 @@ class HomeContent extends React.Component { }; handlePrev = () => { - if (this.carouselRef.current) { - this.carouselRef.current.prev(); + 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.carouselRef.current) { - this.carouselRef.current.next(); + 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(); } }; - handleCarouselChange = (current) => { - this.props.onCarouselChange?.(current); + // 重启自动播放 + 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.carouselRef.current) { - this.carouselRef.current.goTo(index); + if (this.sliderRef.current) { + this.sliderRef.current.slickGoTo(index); } + // 点击圆点时重启自动播放 + this.restartAutoplay(); + }; + + handleCarouselChange = (current) => { + this.props.onCarouselChange?.(current); }; componentDidMount() { @@ -528,12 +648,20 @@ class HomeContent extends React.Component { } 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.jobTodayTop3 !== this.props.jobTodayTop3 || - prevProps.linkSum !== this.props.linkSum || + prevProps.hiddenSummary !== this.props.hiddenSummary || + prevProps.jobTodayQty !== this.props.jobTodayQty || prevProps.taskTop3 !== this.props.taskTop3 || - prevProps.trainingData !== this.props.trainingData // 新增 + prevProps.trainingData !== this.props.trainingData ) { this.disposeAllCharts(); this.initAllCharts(); @@ -542,18 +670,171 @@ class HomeContent extends React.Component { 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 ( +
+ +
+ ); + } + + if (!currentAnnouncement) return null; + + const { TITLE, ABSTRACT, START, END, CONTENT, Nav_Files = [] } = currentAnnouncement; + + return ( +
+ {/* 标题 */} +
+ {TITLE} +
+ + {/* 摘要 */} +
+ {ABSTRACT} +
+ + {/* 日期 */} +
+ {START} 至 {END} +
+ + {/* 分割线 */} +
+ + {/* 正文内容 */} +
+ + {/* 附件列表 */} + {Nav_Files && Nav_Files.length > 0 && ( +
+
+ 附件 ({Nav_Files.length}个) +
+
+ {Nav_Files.map((file, index) => ( +
+ + + {file.Nav_ImgFile?.FILE_NAME || '未知文件'} + +
+ + +
+
+ ))} +
+
+ )} +
+ ); + }; render() { const { riskTypeRate = [], - riskTotal = [], - mediaList = [], - announcementList = [], 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, + }; + console.log('视频配置:', videoConfig); + + 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 (
@@ -581,12 +862,18 @@ class HomeContent extends React.Component {
年度隐患数据
- {riskTotal.map((item, index) => ( -
-
{item.name}
-
{item.value}
-
- ))} +
+
年度重大隐患数
+
{hiddenSummary.majorQty}
+
+
+
年度一般隐患数量
+
{hiddenSummary.generalQty}
+
+
+
未整改隐患数量
+
{hiddenSummary.unfinishQty}
+
@@ -598,47 +885,55 @@ class HomeContent extends React.Component { {/* 中间区域 */}
-
安全方针:以人为本、关注健康、依法治企、安全发展。
-
安全理念:一切风险皆可控,一切事故皆可防!
+
- - {mediaList.map((item, index) => ( -
- {item.type === 'video' ? ( -
- ))} -
-
- -
-
- -
-
- {mediaList.map((_, index) => ( - this.handleDotClick(index)} + {trainingData.listVideoImg && trainingData.listVideoImg[0] ? ( + trainingData.listVideoImg[0].TYPE == 10 ? ( +
+ ) : ( +
+ + {trainingData.listVideoImg?.map((item, index) => ( +
+ {`轮播图 +
+ ))} +
+
+ +
+
+ +
+
+ {trainingData.listVideoImg?.map((_, index) => ( + this.handleDotClick(index)} + /> + ))} +
+
+ ) + ) : ( +
+ )}
@@ -648,23 +943,21 @@ class HomeContent extends React.Component { 公司公告
- 共 {announcementList.length} 条公告 + 共 {trainingData.listAnnourcement?.length || 0} 条公告
- {announcementList.length > 0 ? ( + {trainingData.listAnnourcement?.length > 0 ? ( @@ -684,14 +977,13 @@ class HomeContent extends React.Component {
年度培训数据
- {this.props.trainingData?.listNAME?.map((name, index) => ( + {trainingData?.listNAME?.map((name, index) => (
{name}
-
{this.props.trainingData?.YearCount?.[index] || 0}
+
{trainingData?.YearCount?.[index] || 0}
))} - {/* 如果 listNAME 为空,显示默认提示 */} - {(!this.props.trainingData?.listNAME || this.props.trainingData.listNAME.length === 0) && ( + {(!trainingData?.listNAME || trainingData.listNAME.length === 0) && (
暂无培训数据
)} @@ -708,6 +1000,21 @@ class HomeContent extends React.Component {
+ {/* 公告详情弹窗 */} + + 关闭 + , + ]} + width="80%" + bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }} + > + {this.renderAnnouncementModal()} +
); } diff --git a/src/layout/FullOther/RiskControl.js b/src/layout/FullOther/RiskControl.js new file mode 100644 index 0000000..a8232aa --- /dev/null +++ b/src/layout/FullOther/RiskControl.js @@ -0,0 +1,515 @@ +// RiskControl.js - 风险管控页面组件 +import React from 'react'; +import { Table } from 'antd'; +import styles from './../fullinter.less'; +import echarts from 'echarts'; + +class RiskControl extends React.Component { + constructor(props) { + super(props); + this.echartsInstances = { + stackBarChart: null, // 堆叠柱状图 + typeBarChart: null, // 风险类别柱状图 + }; + this.chartResizeHandlers = {}; + this.isUnmounted = 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); + }); + }; + + // 堆叠柱状图: 各公司风险统计(与 trainingContent 样式一致) + renderStackBarChart = async () => { + if (this.isUnmounted) return; + const elementExists = await this.waitForElement('stackBarChart'); + if (!elementExists || this.isUnmounted) return; + + if (this.echartsInstances.stackBarChart) { + this.echartsInstances.stackBarChart.dispose(); + this.echartsInstances.stackBarChart = null; + } + + const chartDom = document.getElementById('stackBarChart'); + if (!chartDom) return; + + this.echartsInstances.stackBarChart = echarts.init(chartDom); + + const { riskSubData } = this.props; + const riskList = riskSubData?.riskList || []; + + if (riskList.length === 0) { + this.echartsInstances.stackBarChart.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 companyNames = riskList.map((item) => item.companyName); + const majorCounts = riskList.map((item) => item.majorCount); + const largerCounts = riskList.map((item) => item.largerCount); + const generalCounts = riskList.map((item) => item.generalCount); + const lowCounts = riskList.map((item) => item.lowCount); + + const option = { + title: { + text: '各家公司的风险统计情况', + x: 'center', + y: '5%', + textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { type: 'shadow' }, + formatter: function (params) { + let result = `${params[0].axisValue}
`; + params.forEach((param) => { + result += `${param.marker}${param.seriesName}: ${param.value}
`; + }); + return result; + }, + }, + legend: { + data: ['重大风险', '较大风险', '一般风险', '低风险'], + orient: 'vertical', + right: '3%', + top: '5%', + itemGap: 16, + itemWidth: 18, + itemHeight: 12, + textStyle: { color: '#000', fontSize: 14 }, + }, + grid: { + left: '8%', + right: '5%', + top: '18%', + bottom: '8%', + containLabel: true, + }, + xAxis: [ + { + type: 'category', + data: companyNames, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + textStyle: { color: '#000' }, + rotate: companyNames.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', + stack: 'total', + data: majorCounts, + itemStyle: { normal: { color: '#c92a2a' } }, + label: { + show: true, + position: 'inside', + textStyle: { color: '#fff', fontSize: 11 }, + formatter: (params) => (params.value > 0 ? params.value : ''), + }, + barWidth: '35%', + }, + { + name: '较大风险', + type: 'bar', + stack: 'total', + data: largerCounts, + itemStyle: { normal: { color: '#ffa94d' } }, + label: { + show: true, + position: 'inside', + textStyle: { color: '#fff', fontSize: 11 }, + formatter: (params) => (params.value > 0 ? params.value : ''), + }, + barWidth: '35%', + }, + { + name: '一般风险', + type: 'bar', + stack: 'total', + data: generalCounts, + itemStyle: { normal: { color: '#ffe066' } }, + label: { + show: true, + position: 'inside', + textStyle: { color: '#fff', fontSize: 11 }, + formatter: (params) => (params.value > 0 ? params.value : ''), + }, + barWidth: '35%', + }, + { + name: '低风险', + type: 'bar', + stack: 'total', + data: lowCounts, + itemStyle: { normal: { color: '#4285F4' } }, + label: { + show: true, + position: 'inside', + textStyle: { color: '#fff', fontSize: 11 }, + formatter: (params) => (params.value > 0 ? params.value : ''), + }, + barWidth: '35%', + }, + ], + }; + + this.echartsInstances.stackBarChart.setOption(option); + this.setupResizeHandler('stackBarChart', this.renderStackBarChart); + }; + + // 风险类别柱状图 + renderTypeBarChart = async () => { + if (this.isUnmounted) return; + const elementExists = await this.waitForElement('typeBarChart'); + if (!elementExists || this.isUnmounted) return; + + if (this.echartsInstances.typeBarChart) { + this.echartsInstances.typeBarChart.dispose(); + this.echartsInstances.typeBarChart = null; + } + + const chartDom = document.getElementById('typeBarChart'); + if (!chartDom) return; + + this.echartsInstances.typeBarChart = echarts.init(chartDom); + + const { riskSubData } = this.props; + const riskTypeList = riskSubData?.riskTypeList || []; + + if (riskTypeList.length === 0) { + this.echartsInstances.typeBarChart.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 typeNames = riskTypeList.map((item) => item.typeName); + const quantities = riskTypeList.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) { + let result = `${params[0].axisValue}
`; + params.forEach((param) => { + result += `${param.marker}${param.seriesName}: ${param.value}
`; + }); + return result; + }, + }, + grid: { + left: '8%', + right: '5%', + top: '18%', + bottom: '8%', + containLabel: true, + }, + xAxis: [ + { + type: 'category', + data: typeNames, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + textStyle: { color: '#000' }, + // rotate: typeNames.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 }, + }, + ], + series: [ + { + name: '风险数量', + type: 'bar', + data: quantities, + itemStyle: { + normal: { + color: '#4285F4', // 与 trainingContent 一致的蓝色 + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#4285F4', fontSize: 12 }, + formatter: (params) => `${params.value}`, + }, + barWidth: '50%', + }, + ], + }; + + this.echartsInstances.typeBarChart.setOption(option); + this.setupResizeHandler('typeBarChart', this.renderTypeBarChart); + }; + + // 表格: 各公司风险统计明细表 + renderRiskTable = () => { + const { riskSubData } = this.props; + const riskList = riskSubData?.riskList || []; + + if (riskList.length === 0) { + return
暂无风险数据
; + } + + // 表格列配置(与 trainingContent 样式保持一致) + const columns = [ + { + title: '公司名称', + dataIndex: 'companyName', + key: 'companyName', + align: 'center', + width: 120, + render: (text) => {text}, + }, + { + title: '重大风险', + dataIndex: 'majorCount', + key: 'majorCount', + align: 'center', + width: 100, + render: (text) => {text}, + }, + { + title: '较大风险', + dataIndex: 'largerCount', + key: 'largerCount', + align: 'center', + width: 100, + render: (text) => {text}, + }, + { + title: '一般风险', + dataIndex: 'generalCount', + key: 'generalCount', + align: 'center', + width: 100, + render: (text) => {text}, + }, + { + title: '低风险', + dataIndex: 'lowCount', + key: 'lowCount', + align: 'center', + width: 100, + render: (text) => {text}, + }, + { + title: '小计', + dataIndex: 'totalCount', + key: 'totalCount', + align: 'center', + width: 100, + render: (text) => {text}, + }, + ]; + + const tableData = riskList.map((item, index) => ({ + key: index, + companyName: item.companyName, + majorCount: item.majorCount, + largerCount: item.largerCount, + generalCount: item.generalCount, + lowCount: item.lowCount, + totalCount: item.totalCount, + })); + + // 合计行 + const summary = { + majorTotal: riskList.reduce((sum, item) => sum + (item.majorCount || 0), 0), + largerTotal: riskList.reduce((sum, item) => sum + (item.largerCount || 0), 0), + generalTotal: riskList.reduce((sum, item) => sum + (item.generalCount || 0), 0), + lowTotal: riskList.reduce((sum, item) => sum + (item.lowCount || 0), 0), + totalAll: riskList.reduce((sum, item) => sum + (item.totalCount || 0), 0), + }; + const scrollConfig = columns.length > 10 ? { x: columns.length * 100, y: 380 } : {}; + return ( +
+
+ 各家公司的风险统计情况 +
+
+ + + + ); + }; + + setupResizeHandler = (chartName, renderMethod) => { + const resizeHandler = () => { + if (this.echartsInstances[chartName] && !this.isUnmounted) { + this.echartsInstances[chartName].resize(); + } + }; + this.chartResizeHandlers[chartName] = resizeHandler; + window.addEventListener('resize', resizeHandler); + }; + + initAllCharts = () => { + if (this.isUnmounted) return; + setTimeout(() => { + if (this.isUnmounted) return; + this.renderStackBarChart(); + this.renderTypeBarChart(); + }, 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 = {}; + }; + + componentDidMount() { + this.isUnmounted = false; + this.initAllCharts(); + } + + componentDidUpdate(prevProps) { + if (prevProps.riskSubData !== this.props.riskSubData) { + this.renderStackBarChart(); + this.renderTypeBarChart(); + } + } + + componentWillUnmount() { + this.isUnmounted = true; + this.disposeAllCharts(); + } + + render() { + return ( +
+
+ {/* 第一行 - 表格 + 堆叠柱状图 */} +
+
{this.renderRiskTable()}
+
+
+
+
+ {/* 第二行 - 风险类别柱状图 */} +
+
+
+
+
+
+
+ ); + } +} + +export default RiskControl; diff --git a/src/layout/FullOther/TrainingContent.js b/src/layout/FullOther/TrainingContent.js index e115a23..f8e7401 100644 --- a/src/layout/FullOther/TrainingContent.js +++ b/src/layout/FullOther/TrainingContent.js @@ -226,7 +226,7 @@ class TrainingContent extends React.Component { if (companyNames.length === 0) { this.echartsInstances.chart2.setOption({ title: { - text: '本年度各公司培训分析', + text: '各公司月度培训分析', x: 'center', y: '25%', textStyle: { fontSize: 16, color: '#999' }, @@ -247,7 +247,7 @@ class TrainingContent extends React.Component { const option = { title: { - text: '本年度各公司培训分析', + text: '各公司月度培训分析', x: 'center', y: '5%', textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, @@ -693,7 +693,7 @@ class TrainingContent extends React.Component { color: '#000', }} > - 特种作业操作证统计 + 各公司证件统计
{this.renderCertificateTable()}
diff --git a/src/layout/FullScreenInter.js b/src/layout/FullScreenInter.js index a7964a5..da0d73f 100644 --- a/src/layout/FullScreenInter.js +++ b/src/layout/FullScreenInter.js @@ -2,14 +2,18 @@ import React from 'react'; import { connect } from 'dva'; import { withRouter } from 'dva/router'; -import { Icon, Row, Col } from 'antd'; +import { Icon, Row, Col, message } from 'antd'; import styles from './fullinter.less'; import logo from '../assets/layout/headerno-logo-new.png'; import debounce from 'lodash.debounce'; import storage from '../utils/storage'; -import { initFilter } from '../utils/common'; +import { initFilter, extendRule } from '../utils/common'; import HomeContent from './FullOther/HomeContent'; -import TrainingContent from './FullOther/TrainingContent'; // 新增导入 +import TrainingContent from './FullOther/TrainingContent'; +import RiskControl from './FullOther/RiskControl'; +import ClassBuild from './FullOther/ClassBuild'; +import DangerJob from './FullOther/DangerJob'; +import HiddenSolve from './FullOther/HiddenSolve'; const getScale = () => { const width = 1920, @@ -30,8 +34,8 @@ class FullScreen extends React.Component { { name: '年度一般隐患数量', value: 89 }, { name: '未整改隐患数量', value: 18 }, ], - linkSum: [], - jobTodayTop3: [], + jobTodayQty: [], + hiddenSummary: [], taskTop3: [], scale: getScale(), configBanner: ['首页', '风险管控', '隐患治理', '班组建设', '危险作业', '安全培训'], @@ -39,14 +43,17 @@ class FullScreen extends React.Component { currentMediaIndex: 0, announcementList: [], activeTab: '首页', - // 新增:培训数据 + // 安全培训相关数据 trainingData: { listNAME: [], YearCount: [], MonthRecordCount: [], MonthPersonCount: [], + listAnnourcement: [], + TITLE: '', + listVideoImg: [], + playSet: [], }, - // 新增:安全培训页面专用数据 trainingSubData: { listNAME: [], YearCount: [], @@ -61,10 +68,26 @@ class FullScreen extends React.Component { }, trainingSubIDCard: [], trainingSubBSType: {}, - companyData: [], - // 新增:当前选择的月份 - selectedMonth: new Date().getMonth() + 1, // 默认当前月份,1-12 - selectedCompany: '', + trainingCompanyData: [], // 安全培训专用公司数据 + selectedTrainingMonth: new Date().getMonth() + 1, + selectedTrainingCompany: '', + + // 隐患治理相关数据 + hiddenSubData: {}, // 隐患治理数据 + hiddenCompanyData: [], // 隐患治理专用公司数据 + selectedHiddenCompany: '', // 隐患治理选中的公司 + + // 其他页面数据 + riskSubData: {}, + classSubData: {}, + dangerSubData: {}, + dangerCompanyData: [], + selectedDangerCompany: '', + + announcementDetail: null, + announcementDetailLoading: false, + announcementModalVisible: false, + currentAnnouncement: null, }; this.isUnmounted = false; } @@ -97,14 +120,67 @@ class FullScreen extends React.Component { document.addEventListener('fullscreenchange', this.handleFullscreenChange); } + getAnnouncementDetail = (announcement) => { + return new Promise((resolve, reject) => { + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + extendRule(json, 'ID', 1, announcement.ID); + json.Include = ['Nav_Orgs', 'Nav_Orgs.Nav_OrgSub', 'Nav_Files.Nav_ImgFile']; + + this.setState({ announcementDetailLoading: true, announcementDetail: null }); + + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'PF/Annourcement/Get', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + this.setState({ + announcementDetail: ret, + announcementDetailLoading: false, + }); + resolve(ret); + } else { + message.error('暂无公告详情'); + this.setState({ announcementDetailLoading: false }); + reject(new Error('获取公告详情失败')); + } + }, + onError: (error) => { + this.setState({ announcementDetailLoading: false }); + reject(error); + }, + }); + }); + }; + + handleAnnouncementClick = async (announcement) => { + try { + const detail = await this.getAnnouncementDetail(announcement); + this.setState({ + currentAnnouncement: detail, + announcementModalVisible: true, + }); + } catch (error) { + console.error('获取公告详情失败:', error); + } + }; + + handleAnnouncementModalClose = () => { + this.setState({ + announcementModalVisible: false, + currentAnnouncement: null, + }); + }; + getAnnouncementData = () => { this.setState({ announcementList: [ - { id: 1, title: '关于2024年安全生产月活动的通知', publishTime: '2024-06-01', url: '' }, - { id: 2, title: '公司第三季度安全培训安排', publishTime: '2024-05-28', url: '' }, - { id: 3, title: '关于开展安全隐患排查整治工作的通知', publishTime: '2024-05-20', url: '' }, - { id: 4, title: '安全生产标准化建设阶段性总结', publishTime: '2024-05-15', url: '' }, - { id: 5, title: '关于表彰2024年第一季度安全生产先进单位的决定', publishTime: '2024-05-10', url: '' }, + { ID: 1, title: '关于2024年安全生产月活动的通知', publishTime: '2024-06-01', url: '' }, + { ID: 2, title: '公司第三季度安全培训安排', publishTime: '2024-05-28', url: '' }, + { ID: 3, title: '关于开展安全隐患排查整治工作的通知', publishTime: '2024-05-20', url: '' }, + { ID: 4, title: '安全生产标准化建设阶段性总结', publishTime: '2024-05-15', url: '' }, + { ID: 5, title: '关于表彰2024年第一季度安全生产先进单位的决定', publishTime: '2024-05-10', url: '' }, ], }); }; @@ -133,13 +209,28 @@ class FullScreen extends React.Component { this.loadMediaFiles(); this.getAnnouncementData(); } - // 当点击安全培训tab时,获取对应数据 + // 安全培训 - 参考原有逻辑 if (name === '安全培训') { + this.getTrainingCompanyData(); // 先获取公司列表 this.getHomeSESubYearData(); this.getHomeSESubYearMonthData(); this.getHomeSESubIDCardData(); - // this.getBSTypeMonthData(); - this.getCompanyData(); + } + // 隐患治理 - 参考安全培训的调用顺序 + if (name === '隐患治理') { + this.getHiddenCompanyData(); // 先获取公司列表(与安全培训独立) + } + // 风险管控 + if (name === '风险管控') { + this.getRiskSubData(); + } + // 班组建设 + if (name === '班组建设') { + this.getClassSubData(); + } + // 危险作业 + if (name === '危险作业') { + this.getDangerCompanyData(); } }; @@ -169,6 +260,7 @@ class FullScreen extends React.Component { }; getHomeDataArray = () => { + console.log('获取首页数据'); const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); this.props.dispatch({ @@ -179,14 +271,15 @@ class FullScreen extends React.Component { if (ret && !this.isUnmounted) { this.setState({ riskTypeRate: ret.riskTypeRate || [], - linkSum: ret.linkSum || [], - jobTodayTop3: ret.jobTodayTop3 || [], + jobTodayQty: ret.jobTodayQty || [], + hiddenSummary: ret.hiddenSummary || [], taskTop3: ret.taskTop3 || [], }); } }, }); }; + getYearPXData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); @@ -202,29 +295,63 @@ class FullScreen extends React.Component { YearCount: ret.YearCount || [], MonthRecordCount: ret.MonthRecordCount || [], MonthPersonCount: ret.MonthPersonCount || [], + listAnnourcement: ret.listAnnourcement || [], + TITLE: ret.TITLE || '', + listVideoImg: ret.listVideoImg || [], + playSet: ret.playSet || [], }, }); } }, }); }; - // 获取安全培训页面数据 + + // ==================== 安全培训相关方法 ==================== + getTrainingCompanyData = () => { + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'FM/Organization/OrderPaged', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + // 默认选中邦泰公司 + const defaultCompany = ret.find((company) => company.ID === '00500000-0000-0000-0000-000000000000'); + this.setState({ + trainingCompanyData: ret, + }); + if (defaultCompany && !this.state.selectedTrainingCompany) { + this.setState( + { + selectedTrainingCompany: defaultCompany.ID, + }, + () => { + this.getBSTypeMonthData(); // 公司设置完成后获取培训类型数据 + } + ); + } else { + this.getBSTypeMonthData(); + } + } + }, + }); + }; + getHomeSESubYearData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); - const currentYear = new Date().getFullYear(); // 获取当前年份 - json.Parameter1 = currentYear.toString(); // 设置为当前年份 + const currentYear = new Date().getFullYear(); + json.Parameter1 = currentYear.toString(); this.props.dispatch({ type: 'app/getDataByPost', payload: json, url: 'BI/BIHeadSE/HomeSESubYear', onComplete: (ret) => { if (ret && !this.isUnmounted) { - // ret 是数组格式: [{ CName: "邦泰", PCount: 0, RCount: 13 }, ...] const listNAME = ret.map((item) => item.CName); - const MonthPersonCount = ret.map((item) => item.PCount); // 培训人次 - const MonthRecordCount = ret.map((item) => item.RCount); // 培训场次 - // YearCount 暂时用 PCount 的总和或保持为空数组 + const MonthPersonCount = ret.map((item) => item.PCount); + const MonthRecordCount = ret.map((item) => item.RCount); const YearCount = ret.map((item) => item.PCount); this.setState({ @@ -239,38 +366,34 @@ class FullScreen extends React.Component { }, }); }; - // 新增:处理月份变化 - handleMonthChange = (month) => { - this.setState({ selectedMonth: month }, () => { - // 重新获取月份数据 + + handleTrainingMonthChange = (month) => { + this.setState({ selectedTrainingMonth: month }, () => { this.getHomeSESubYearMonthData(); }); }; - handleCompanyChange = (company) => { - console.log(company, '11111'); - this.setState({ selectedCompany: company }, () => { - // 重新获取月份数据 + + handleTrainingCompanyChange = (company) => { + this.setState({ selectedTrainingCompany: company }, () => { this.getBSTypeMonthData(); }); }; - // 获取安全培训页面数据 + getHomeSESubYearMonthData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); - const currentYear = new Date().getFullYear(); // 获取当前年份 - json.Parameter1 = currentYear.toString(); // 设置为当前年份 - json.Parameter2 = this.state.selectedMonth.toString(); // 月份 + const currentYear = new Date().getFullYear(); + json.Parameter1 = currentYear.toString(); + json.Parameter2 = this.state.selectedTrainingMonth.toString(); this.props.dispatch({ type: 'app/getDataByPost', payload: json, url: 'BI/BIHeadSE/HomeSESubYear', onComplete: (ret) => { if (ret && !this.isUnmounted) { - // ret 是数组格式: [{ CName: "邦泰", PCount: 0, RCount: 13 }, ...] const listNAME = ret.map((item) => item.CName); - const MonthPersonCount = ret.map((item) => item.PCount); // 培训人次 - const MonthRecordCount = ret.map((item) => item.RCount); // 培训场次 - // YearCount 暂时用 PCount 的总和或保持为空数组 + const MonthPersonCount = ret.map((item) => item.PCount); + const MonthRecordCount = ret.map((item) => item.RCount); const YearCount = ret.map((item) => item.PCount); this.setState({ @@ -285,6 +408,7 @@ class FullScreen extends React.Component { }, }); }; + getHomeSESubIDCardData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); @@ -292,9 +416,9 @@ class FullScreen extends React.Component { const year = currentDate.getFullYear(); const month = String(currentDate.getMonth() + 1).padStart(2, '0'); const day = String(currentDate.getDate()).padStart(2, '0'); - const currentDateStr = `${year}-${month}-${day}`; // 例如:2024-06-15 + const currentDateStr = `${year}-${month}-${day}`; - json.Parameter1 = currentDateStr; // 设置为当前日期 + json.Parameter1 = currentDateStr; this.props.dispatch({ type: 'app/getDataByPost', payload: json, @@ -308,6 +432,7 @@ class FullScreen extends React.Component { }, }); }; + getBSTypeMonthData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); @@ -315,11 +440,11 @@ class FullScreen extends React.Component { const year = currentDate.getFullYear(); const month = String(currentDate.getMonth() + 1).padStart(2, '0'); const day = String(currentDate.getDate()).padStart(2, '0'); - const currentDateStr = `${year}-${month}-${day}`; // 例如:2024-06-15 + const currentDateStr = `${year}-${month}-${day}`; - json.Parameter1 = currentDateStr; // 设置为当前日期 - if (this.state.selectedCompany) { - json.Parameter2 = this.state.selectedCompany; + json.Parameter1 = currentDateStr; + if (this.state.selectedTrainingCompany) { + json.Parameter2 = this.state.selectedTrainingCompany; } this.props.dispatch({ type: 'app/getDataByPost', @@ -334,7 +459,10 @@ class FullScreen extends React.Component { }, }); }; - getCompanyData = () => { + + // ==================== 隐患治理相关方法 ==================== + // 获取隐患治理的公司列表(独立于安全培训) + getHiddenCompanyData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); this.props.dispatch({ @@ -343,52 +471,422 @@ class FullScreen extends React.Component { url: 'FM/Organization/OrderPaged', onComplete: (ret) => { if (ret && !this.isUnmounted) { - //加载页面默认为绑泰 + // 默认选中邦泰公司 const defaultCompany = ret.find((company) => company.ID === '00500000-0000-0000-0000-000000000000'); this.setState({ - companyData: ret, + hiddenCompanyData: ret, }); - // 如果找到了默认公司,设置 selectedCompany - if (defaultCompany && !this.state.selectedCompany) { + if (defaultCompany && !this.state.selectedHiddenCompany) { this.setState({ - selectedCompany: defaultCompany.ID, + selectedHiddenCompany: defaultCompany.ID, }); } - this.getBSTypeMonthData(); + this.getHiddenSubData(); } }, }); }; + // 隐患治理公司筛选变化处理 + handleHiddenCompanyChange = (company) => { + this.setState({ selectedHiddenCompany: company }); + this.getHiddenSubData(); + }; + + getHiddenSubData = () => { + let filteredData = { + hiddenList: [ + { + companyName: '邦泰', + majorCount: 52, + generalCount: 128, + }, + { + companyName: '宏源建设', + majorCount: 18, + generalCount: 94, + }, + { + companyName: '中城建工', + majorCount: 31, + generalCount: 205, + }, + ], + hiddenRanking: [ + // 邦泰 5 个 + { + companyName: '邦泰', + hiddenName: '安全防护缺失', + qty: 42, + }, + { + companyName: '邦泰', + hiddenName: '临时用电不规范', + qty: 38, + }, + { + companyName: '邦泰', + hiddenName: '机械设备故障', + qty: 27, + }, + { + companyName: '邦泰', + hiddenName: '特种设备未年检', + qty: 15, + }, + { + companyName: '邦泰', + hiddenName: '消防器材过期', + qty: 12, + }, + // 路源 6 个 + { + companyName: '路源', + hiddenName: '消防通道堵塞', + qty: 29, + }, + { + companyName: '路源', + hiddenName: '电气线路老化', + qty: 18, + }, + { + companyName: '路源', + hiddenName: '应急照明故障', + qty: 8, + }, + { + companyName: '路源', + hiddenName: '安全标识缺失', + qty: 7, + }, + { + companyName: '路源', + hiddenName: '脚手架搭设不规范', + qty: 6, + }, + { + companyName: '路源', + hiddenName: '劳保用品佩戴不规范', + qty: 5, + }, + // 天山 7 个 + { + companyName: '天山', + hiddenName: '脚手架搭设违规', + qty: 36, + }, + { + companyName: '天山', + hiddenName: '高空抛物隐患', + qty: 22, + }, + { + companyName: '天山', + hiddenName: '动火作业监护缺失', + qty: 14, + }, + { + companyName: '天山', + hiddenName: '受限空间未审批', + qty: 11, + }, + { + companyName: '天山', + hiddenName: '起重吊装违规操作', + qty: 9, + }, + { + companyName: '天山', + hiddenName: '临时用电私拉乱接', + qty: 7, + }, + { + companyName: '天山', + hiddenName: '高处作业安全带未挂', + qty: 5, + }, + ], + hiddenRectifyList: [ + { + companyName: '邦泰', + majorCount: 52, + majorCountNo: 12, + generalCount: 128, + generalCountNo: 31, + }, + { + companyName: '宏源建设', + majorCount: 18, + majorCountNo: 5, + generalCount: 94, + generalCountNo: 22, + }, + { + companyName: '中城建工', + majorCount: 31, + majorCountNo: 9, + generalCount: 205, + generalCountNo: 47, + }, + ], + }; + if (this.state.selectedHiddenCompany) { + const selectedCompanyObj = this.state.hiddenCompanyData?.find( + (company) => company.ID === this.state.selectedHiddenCompany + ); + const selectedCompanyName = selectedCompanyObj?.NAME; + + if (selectedCompanyName) { + // 筛选 hiddenRanking + filteredData.hiddenRanking = filteredData.hiddenRanking.filter( + (item) => item.companyName === selectedCompanyName + ); + } + } + + // 模拟数据 + this.setState({ + hiddenSubData: filteredData, + }); + + // 以下是真实的API调用代码,如需使用请取消注释 + // const orgId = storage('lacal').getItem('webOrgId')?.val; + // const json = initFilter(orgId); + // this.props.dispatch({ + // type: 'app/getDataByPost', + // payload: json, + // url: 'BI/BIKanBanController/HiddenManage', + // onComplete: (ret) => { + // if (ret && !this.isUnmounted) { + // this.setState({ + // hiddenSubData: ret.Data, + // }); + // } + // }, + // }); + }; + + // ==================== 其他页面方法 ==================== + getRiskSubData = () => { + this.setState({ + riskSubData: { + riskList: [ + { + companyName: '邦泰', + majorCount: 2, + largerCount: 5, + generalCount: 12, + lowCount: 33, + totalCount: 52, + }, + { + companyName: '鸿源建设', + majorCount: 1, + largerCount: 3, + generalCount: 8, + lowCount: 20, + totalCount: 32, + }, + { + companyName: '华安工程', + majorCount: 0, + largerCount: 2, + generalCount: 6, + lowCount: 15, + totalCount: 23, + }, + { + companyName: '天达科技', + majorCount: 3, + largerCount: 4, + generalCount: 10, + lowCount: 18, + totalCount: 35, + }, + ], + riskTypeList: [ + { typeName: '动火作业', qty: 18 }, + { typeName: '高处作业', qty: 24 }, + { typeName: '临时用电', qty: 12 }, + { typeName: '起重吊装', qty: 9 }, + { typeName: '受限空间', qty: 7 }, + ], + }, + }); + }; + + getClassSubData = () => { + this.setState({ + classSubData: [ + { companyName: '邦泰', rate: '52%' }, + { companyName: '路源', rate: '52%' }, + { companyName: '安达', rate: '78%' }, + { companyName: '迅捷', rate: '63%' }, + { companyName: '恒通', rate: '45%' }, + { companyName: '华威', rate: '91%' }, + { companyName: '鼎盛', rate: '37%' }, + ], + }); + }; + + getDangerCompanyData = () => { + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'FM/Organization/OrderPaged', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + const defaultCompany = ret.find((company) => company.ID === '00500000-0000-0000-0000-000000000000'); + this.setState({ + dangerCompanyData: ret, + }); + if (defaultCompany && !this.state.selectedDangerCompany) { + this.setState( + { + selectedDangerCompany: defaultCompany.ID, + }, + () => { + this.getDangerSubData(); + } + ); + } else { + this.getDangerSubData(); + } + } + }, + }); + }; + + handleDangerCompanyChange = (company) => { + this.setState({ selectedDangerCompany: company }, () => { + this.getDangerSubData(); + }); + }; + + getDangerSubData = () => { + let filteredData = [ + { + companyName: '邦泰', + startDate: '2025-01-01 08:30:00', + endDate: '2025-01-01 17:00:00', + jobName: '爆破作业', + areaName: '矿山采场', + place: '东采区+320平台', + users: '张三,李四,王五', + monitor: '赵正三', + approveUsers: '刘安全,陈技术', + }, + { + companyName: '邦泰', + startDate: '2025-01-03 09:00:00', + endDate: '2025-01-03 18:00:00', + jobName: '高处作业', + areaName: '装置区', + place: '脱硫塔顶部', + users: '王高空,李安全带,张监护', + monitor: '周正三', + approveUsers: '吴经理,郑安全', + }, + { + companyName: '天山', + startDate: '2025-01-02 08:00:00', + endDate: '2025-01-02 17:30:00', + jobName: '高处作业', + areaName: '水泥生产线', + place: '预热器塔架', + users: '检修班组', + monitor: '天山-赵正', + approveUsers: '天山-刘经理,天山-王安全', + }, + { + companyName: '天山', + startDate: '2025-01-04 09:30:00', + endDate: '2025-01-04 18:00:00', + jobName: '有限空间作业', + areaName: '均化库', + place: '库内底部', + users: '清库作业组', + monitor: '天山-钱正', + approveUsers: '天山-孙厂长,天山-李安全', + }, + ]; + + if (this.state.selectedDangerCompany) { + const selectedCompanyObj = this.state.dangerCompanyData?.find( + (company) => company.ID === this.state.selectedDangerCompany + ); + const selectedCompanyName = selectedCompanyObj?.NAME; + if (selectedCompanyName) { + filteredData = filteredData.filter((item) => item.companyName === selectedCompanyName); + } + } + + this.setState({ + dangerSubData: filteredData, + }); + }; + renderOtherTabContent = () => { const { activeTab, - trainingData, trainingSubData, - trainingSubDataMonth, - selectedMonth, - selectedCompany, trainingSubBSType, - companyData, + trainingCompanyData, + trainingSubDataMonth, + selectedTrainingMonth, + selectedTrainingCompany, trainingSubIDCard, + riskSubData, + classSubData, + hiddenSubData, + hiddenCompanyData, + selectedHiddenCompany, } = this.state; - // 如果是安全培训,显示专门的培训页面 + if (activeTab === '安全培训') { return ( ); } + if (activeTab === '风险管控') { + return ; + } + if (activeTab === '隐患治理') { + return ( + + ); + } + if (activeTab === '班组建设') { + return ; + } + if (activeTab === '危险作业') { + return ( + + ); + } return (
@@ -408,13 +906,16 @@ class FullScreen extends React.Component { activeTab, riskTypeRate, riskTotal, - jobTodayTop3, - linkSum, + hiddenSummary, + jobTodayQty, taskTop3, mediaList, announcementList, currentMediaIndex, trainingData, + announcementModalVisible, + currentAnnouncement, + announcementDetailLoading, } = this.state; return ( @@ -470,15 +971,20 @@ class FullScreen extends React.Component { ) : ( this.renderOtherTabContent() diff --git a/src/layout/fullinter.less b/src/layout/fullinter.less index b9bccfe..54e5648 100644 --- a/src/layout/fullinter.less +++ b/src/layout/fullinter.less @@ -281,7 +281,7 @@ justify-content: center; font-weight: bold; color: #fff; - background-color: #1e2a3a; + background-color: #909399; margin: 10px 20px; padding: 5px 10px; border-radius: 4px; @@ -313,7 +313,7 @@ justify-content: center; font-weight: bold; color: #fff; - background-color: #1e2a3a; + background-color: #909399; margin: 10px 20px; padding: 5px 10px; border-radius: 4px; @@ -492,38 +492,59 @@ margin-bottom: 16px; } -// 轮播图控件 .customArrowLeft, .customArrowRight { position: absolute; top: 50%; transform: translateY(-50%); - width: 40px; - height: 60px; + width: 44px; + height: 64px; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 10; transition: all 0.3s ease; - backdrop-filter: blur(4px); + border-radius: 8px; + + // 深色半透明背景,确保在任何背景下都可见 + background-color: rgba(0, 0, 0, 0.5); + + // 添加白色外发光 + box-shadow: 0 0 8px rgba(255, 255, 255, 0.3); + + // 图标颜色设为白色 + i, + .anticon { + color: #ffffff !important; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + } &:hover { - background-color: rgba(0, 0, 0, 0.3); - transform: translateY(-50%) scale(1.1); + background-color: rgba(0, 0, 0, 0.7); + transform: translateY(-50%) scale(1.05); + box-shadow: 0 0 12px rgba(255, 255, 255, 0.5); + + i, + .anticon { + color: #ffffff !important; + transform: scale(1.1); + } } &:active { - transform: translateY(-50%) scale(0.95); + transform: translateY(-50%) scale(0.98); } } .customArrowLeft { - left: 20px; + left: 16px; + border-radius: 0 8px 8px 0; } .customArrowRight { - right: 20px; + right: 16px; + border-radius: 8px 0 0 8px; } .customDots { @@ -691,19 +712,89 @@ white-space: nowrap; } - // 奇数行背景色 - .ant-table-tbody > tr:nth-child(odd) > td { - background-color: #d4d5e3; - } - - // 偶数行背景色 - .ant-table-tbody > tr:nth-child(even) > td { - background-color: #d0cedc; - } - // hover 效果 .ant-table-tbody > tr:hover > td { background-color: #e6f7ff; } } } + +// fullinter.less - 在文件末尾添加 + +// 公告内容富文本样式 +.announcementContent { + font-size: 14px; + line-height: 1.6; + color: #333; + + // 富文本内容样式 + h1, + h2, + h3, + h4, + h5, + h6 { + margin: 10px 0; + font-weight: bold; + } + + p { + margin: 8px 0; + } + + img { + max-width: 100%; + height: auto; + } + + ul, + ol { + padding-left: 20px; + margin: 8px 0; + } + + li { + margin: 4px 0; + } + + table { + border-collapse: collapse; + width: 100%; + margin: 8px 0; + } + + th, + td { + border: 1px solid #ddd; + padding: 6px; + text-align: left; + } + + th { + background-color: #f5f5f5; + } + + strong, + b { + font-weight: bold; + } + + em, + i { + font-style: italic; + } + + u { + text-decoration: underline; + } + + sub { + vertical-align: sub; + font-size: smaller; + } + + sup { + vertical-align: super; + font-size: smaller; + } +} diff --git a/yarn.lock b/yarn.lock index 6ec1f1e..5842eb0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10651,7 +10651,7 @@ "recompose" "^0.27.1" "shallowequal" "^1.0.2" -"react-dom@*", "react-dom@^0.14.0 || ^15.0.0 || ^16.0.0", "react-dom@^0.14.0 || ^15.0.0 || ^16.0.0-beta || ^16.0.0", "react-dom@^0.14.0 || ^15.0.0-0 || ^16.0.0", "react-dom@^0.14.0 || ^15.0.0-rc || ^16.0.0-rc || ^16.0.0", "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0", "react-dom@^15.0.0 || ^16.0.0", "react-dom@^15.0.2 || ^16.0.0-rc || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^15.0.2|| ^16.0.0-rc || ^16.0.0", "react-dom@^16.0.0", "react-dom@^16.0.0-0", "react-dom@^16.3.0", "react-dom@^16.4.1", "react-dom@^16.6.3", "react-dom@^16.8.0", "react-dom@>=15.0.0", "react-dom@>=15.x", "react-dom@>=16.0.0", "react-dom@>=16.9.0", "react-dom@>0.14.5", "react-dom@0.14.x || 15.x || 16.x", "react-dom@15.x || ^16.0.0-0", "react-dom@16.8.2": +"react-dom@*", "react-dom@^0.14.0 || ^15.0.0 || ^16.0.0", "react-dom@^0.14.0 || ^15.0.0 || ^16.0.0-beta || ^16.0.0", "react-dom@^0.14.0 || ^15.0.0-0 || ^16.0.0", "react-dom@^0.14.0 || ^15.0.0-rc || ^16.0.0-rc || ^16.0.0", "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0", "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^15.0.0 || ^16.0.0", "react-dom@^15.0.2 || ^16.0.0-rc || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^15.0.2|| ^16.0.0-rc || ^16.0.0", "react-dom@^16.0.0", "react-dom@^16.0.0-0", "react-dom@^16.3.0", "react-dom@^16.4.1", "react-dom@^16.6.3", "react-dom@^16.8.0", "react-dom@>=15.0.0", "react-dom@>=15.x", "react-dom@>=16.0.0", "react-dom@>=16.9.0", "react-dom@>0.14.5", "react-dom@0.14.x || 15.x || 16.x", "react-dom@15.x || ^16.0.0-0", "react-dom@16.8.2": "integrity" "sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg==" "resolved" "https://registry.npmmirror.com/react-dom/-/react-dom-16.8.2.tgz" "version" "16.8.2" @@ -10789,9 +10789,19 @@ "prop-types" "^15.6.1" "warning" "^4.0.1" +"react-slick@^0.31.0": + "integrity" "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==" + "resolved" "https://registry.npmmirror.com/react-slick/-/react-slick-0.31.0.tgz" + "version" "0.31.0" + dependencies: + "classnames" "^2.2.5" + "json2mq" "^0.2.0" + "lodash.debounce" "^4.0.8" + "resize-observer-polyfill" "^1.5.0" + "react-slick@~0.25.2": "integrity" "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==" - "resolved" "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz" + "resolved" "https://registry.npmmirror.com/react-slick/-/react-slick-0.25.2.tgz" "version" "0.25.2" dependencies: "classnames" "^2.2.5" @@ -10834,7 +10844,7 @@ "resolved" "https://registry.npmmirror.com/react-websocket/-/react-websocket-2.0.1.tgz" "version" "2.0.1" -"react@*", "react@^0.14 || ^15.0.0 || ^16.0.0-alpha", "react@^0.14.0 || ^15.0.0 || ^16.0.0", "react@^0.14.0 || ^15.0.0 || ^16.0.0-0", "react@^0.14.0 || ^15.0.0 || ^16.0.0-beta || ^16.0.0", "react@^0.14.0 || ^15.0.0-0 || ^16.0.0", "react@^0.14.0 || ^15.0.0-0 || ^16.0.0-0", "react@^0.14.0 || ^15.0.0-rc || ^16.0.0-rc || ^16.0.0", "react@^0.14.0 || ^15.0.1 || ^16.0.0", "react@^15.0.0 || ^16.0.0", "react@^15.0.2 || ^16.0.0-rc || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^15.0.2|| ^16.0.0-rc || ^16.0.0", "react@^16.0.0", "react@^16.0.0-0", "react@^16.3.0", "react@^16.4.1", "react@^16.6.3", "react@^16.8.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@>= 16.3", "react@>=0.13.2 || ^0.14 || ^15.0.0 || >=16.0.0", "react@>=15", "react@>=15.0.0", "react@>=15.x", "react@>=16.0.0", "react@>=16.9.0", "react@>0.14.5", "react@0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0", "react@0.14.x || 15.x || 16.x", "react@15.x || ^16.0.0-0", "react@16.8.2", "react@16.x": +"react@*", "react@^0.14 || ^15.0.0 || ^16.0.0-alpha", "react@^0.14.0 || ^15.0.0 || ^16.0.0", "react@^0.14.0 || ^15.0.0 || ^16.0.0-0", "react@^0.14.0 || ^15.0.0 || ^16.0.0-beta || ^16.0.0", "react@^0.14.0 || ^15.0.0-0 || ^16.0.0", "react@^0.14.0 || ^15.0.0-0 || ^16.0.0-0", "react@^0.14.0 || ^15.0.0-rc || ^16.0.0-rc || ^16.0.0", "react@^0.14.0 || ^15.0.1 || ^16.0.0", "react@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^15.0.0 || ^16.0.0", "react@^15.0.2 || ^16.0.0-rc || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^15.0.2|| ^16.0.0-rc || ^16.0.0", "react@^16.0.0", "react@^16.0.0-0", "react@^16.3.0", "react@^16.4.1", "react@^16.6.3", "react@^16.8.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@>= 16.3", "react@>=0.13.2 || ^0.14 || ^15.0.0 || >=16.0.0", "react@>=15", "react@>=15.0.0", "react@>=15.x", "react@>=16.0.0", "react@>=16.9.0", "react@>0.14.5", "react@0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0", "react@0.14.x || 15.x || 16.x", "react@15.x || ^16.0.0-0", "react@16.8.2", "react@16.x": "integrity" "sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw==" "resolved" "https://registry.npmmirror.com/react/-/react-16.8.2.tgz" "version" "16.8.2" From 9ed7b3b94169fabb4e9b17827f9555a2e6f9b35a Mon Sep 17 00:00:00 2001 From: yunkexin <760754045@qq.com> Date: Wed, 29 Apr 2026 11:11:23 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=8F=AF=E8=A7=86=E5=8C=96=E5=A4=A7?= =?UTF-8?q?=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layout/FullOther/HiddenSolve.js | 103 ++++--- src/layout/FullOther/HomeContent.js | 1 - src/layout/FullScreenInter.js | 444 ++++++++-------------------- 3 files changed, 179 insertions(+), 369 deletions(-) diff --git a/src/layout/FullOther/HiddenSolve.js b/src/layout/FullOther/HiddenSolve.js index d0f5c1e..0fd37fb 100644 --- a/src/layout/FullOther/HiddenSolve.js +++ b/src/layout/FullOther/HiddenSolve.js @@ -76,7 +76,7 @@ class HiddenSolve extends React.Component { if (hiddenList.length === 0) { this.echartsInstances.hiddenBarChart.setOption({ title: { - text: '各公司隐患统计', + text: '各公司累计隐患统计数据', x: 'center', y: '25%', textStyle: { fontSize: 16, color: '#999' }, @@ -101,7 +101,7 @@ class HiddenSolve extends React.Component { const option = { title: { - text: '各公司隐患统计', + text: '各公司累计隐患统计数据', x: 'center', y: '5%', textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, @@ -227,7 +227,7 @@ class HiddenSolve extends React.Component { if (hiddenRectifyList.length === 0) { this.echartsInstances.hiddenRectifyChart.setOption({ title: { - text: '各公司隐患整改情况', + text: '当月各公司隐患统计数据', x: 'center', y: '25%', textStyle: { fontSize: 16, color: '#999' }, @@ -254,7 +254,7 @@ class HiddenSolve extends React.Component { const option = { title: { - text: '各公司隐患整改情况', + text: '当月各公司隐患统计数据', x: 'center', y: '5%', textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, @@ -271,7 +271,7 @@ class HiddenSolve extends React.Component { }, }, legend: { - data: ['重大隐患总数', '重大隐患已整改', '一般隐患总数', '一般隐患已整改'], + data: ['重大隐患量', '重大隐患未整改量', '一般隐患量', '一般隐患未整改量'], orient: 'vertical', right: '3%', top: '5%', @@ -318,7 +318,7 @@ class HiddenSolve extends React.Component { ], series: [ { - name: '重大隐患总数', + name: '重大隐患量', type: 'bar', data: majorTotal, itemStyle: { @@ -335,7 +335,7 @@ class HiddenSolve extends React.Component { barWidth: '20%', }, { - name: '重大隐患已整改', + name: '重大隐患未整改量', type: 'bar', data: majorRectified, itemStyle: { @@ -352,7 +352,7 @@ class HiddenSolve extends React.Component { barWidth: '20%', }, { - name: '一般隐患总数', + name: '一般隐患量', type: 'bar', data: generalTotal, itemStyle: { @@ -369,7 +369,7 @@ class HiddenSolve extends React.Component { barWidth: '20%', }, { - name: '一般隐患已整改', + name: '一般隐患未整改量', type: 'bar', data: generalRectified, itemStyle: { @@ -403,13 +403,13 @@ class HiddenSolve extends React.Component { hiddenRanking = hiddenRanking.filter((item) => item.companyName === selectedCompanyName); } - if (hiddenRanking.length === 0) { - return ( -
- {selectedCompanyName ? `${selectedCompanyName} 暂无隐患排名数据` : '暂无隐患排名数据'} -
- ); - } + // if (hiddenRanking.length === 0) { + // return ( + //
+ // {selectedCompanyName ? `${selectedCompanyName} 暂无隐患排名数据` : '暂无隐患排名数据'} + //
+ // ); + // } return (
@@ -422,9 +422,8 @@ class HiddenSolve extends React.Component { color: '#000', }} > - 隐患排名 + 隐患次数排名列表
- {/* 公司筛选器 - 与 TrainingContent 样式保持一致 */}
选择公司:
- - + @@ -450,39 +450,36 @@ class HiddenSolve extends React.Component { - {hiddenRanking.map((item, index) => ( - - - - - - - ))} + {hiddenRanking.length > 0 ? ( + hiddenRanking.map((item, index) => ( + + + + + + )) + ) : ( +
{'暂无隐患排名数据'}
+ )}
排名公司序号 隐患名称
- - {index + 1} - - - {item.companyName} - {item.hiddenName} - {item.qty} -
+ + {index + 1} + + {item.hiddenName} + {item.qty} +
diff --git a/src/layout/FullOther/HomeContent.js b/src/layout/FullOther/HomeContent.js index ca5e83f..08a095f 100644 --- a/src/layout/FullOther/HomeContent.js +++ b/src/layout/FullOther/HomeContent.js @@ -817,7 +817,6 @@ class HomeContent extends React.Component { muted: playSet.V_ISSILENT !== undefined ? playSet.V_ISSILENT : true, controls: playSet.V_ISSHOWCONTROL !== undefined ? playSet.V_ISSHOWCONTROL : false, }; - console.log('视频配置:', videoConfig); const isAutoplay = playSet.IMG_ISRE !== undefined ? playSet.IMG_ISRE : true; const autoplaySpeed = playSet.IMG_TIMESPAN ? playSet.IMG_TIMESPAN * 1000 : 5000; diff --git a/src/layout/FullScreenInter.js b/src/layout/FullScreenInter.js index da0d73f..e08706b 100644 --- a/src/layout/FullScreenInter.js +++ b/src/layout/FullScreenInter.js @@ -29,11 +29,7 @@ class FullScreen extends React.Component { this.state = { nowDate: '', riskTypeRate: [], - riskTotal: [ - { name: '年度重大隐患数', value: 1 }, - { name: '年度一般隐患数量', value: 89 }, - { name: '未整改隐患数量', value: 18 }, - ], + riskTotal: [], jobTodayQty: [], hiddenSummary: [], taskTop3: [], @@ -90,6 +86,7 @@ class FullScreen extends React.Component { currentAnnouncement: null, }; this.isUnmounted = false; + this.dataTimer = null; // 新增:用于数据定时请求 } setScale = debounce(() => { @@ -104,15 +101,56 @@ class FullScreen extends React.Component { document.exitFullscreen?.(); } }; + startDataRefreshTimer = () => { + // 清除已有定时器 + if (this.dataTimer) { + clearInterval(this.dataTimer); + } + + // 设置新的定时器,每5分钟执行一次 + this.dataTimer = setInterval( + () => { + if (!this.isUnmounted) { + if (this.state.activeTab === '首页') { + this.getHomeDataArray(); + this.getYearPXData(); + } + // 安全培训 - 参考原有逻辑 + else if (this.state.activeTab === '安全培训') { + this.getTrainingCompanyData(); // 先获取公司列表 + this.getHomeSESubYearData(); + this.getHomeSESubYearMonthData(); + this.getHomeSESubIDCardData(); + } + // 隐患治理 - 参考安全培训的调用顺序 + else if (this.state.activeTab === '隐患治理') { + this.getHiddenCompanyData(); // 先获取公司列表(与安全培训独立) + } + // 风险管控 + else if (this.state.activeTab === '风险管控') { + this.getRiskSubData(); + } + // 班组建设 + else if (this.state.activeTab === '班组建设') { + this.getClassSubData(); + } + // 危险作业 + else if (this.state.activeTab === '危险作业') { + this.getDangerCompanyData(); + } + } + }, + 1 * 60 * 1000 + ); // 5分钟 = 300000毫秒 + }; componentDidMount() { this.isUnmounted = false; window.addEventListener('resize', this.setScale); this.getHomeDataArray(); this.getYearPXData(); - this.loadMediaFiles(); - this.getAnnouncementData(); - + // 启动定时刷新 + this.startDataRefreshTimer(); this.timer = setInterval(() => { if (!this.isUnmounted) this.setState({ nowDate: this.getDate() }); }, 1000); @@ -173,27 +211,6 @@ class FullScreen extends React.Component { }); }; - getAnnouncementData = () => { - this.setState({ - announcementList: [ - { ID: 1, title: '关于2024年安全生产月活动的通知', publishTime: '2024-06-01', url: '' }, - { ID: 2, title: '公司第三季度安全培训安排', publishTime: '2024-05-28', url: '' }, - { ID: 3, title: '关于开展安全隐患排查整治工作的通知', publishTime: '2024-05-20', url: '' }, - { ID: 4, title: '安全生产标准化建设阶段性总结', publishTime: '2024-05-15', url: '' }, - { ID: 5, title: '关于表彰2024年第一季度安全生产先进单位的决定', publishTime: '2024-05-10', url: '' }, - ], - }); - }; - - loadMediaFiles = () => { - this.setState({ - mediaList: [ - { type: 'image', url: 'http://10.2.7.18:28028//WZ_Images/static/smyzw@2x.png' }, - { type: 'video', url: 'http://10.2.7.18:28028/WZ_Images/Img_JFSC/welcom/1.mp4' }, - ], - }); - }; - handleCarouselChange = (current) => { this.setState({ currentMediaIndex: current }); }; @@ -205,9 +222,8 @@ class FullScreen extends React.Component { handleTabClick = (name) => { this.setState({ activeTab: name }); if (name === '首页') { + this.getHomeDataArray(); this.getYearPXData(); - this.loadMediaFiles(); - this.getAnnouncementData(); } // 安全培训 - 参考原有逻辑 if (name === '安全培训') { @@ -242,6 +258,9 @@ class FullScreen extends React.Component { this.isUnmounted = true; window.removeEventListener('resize', this.setScale); if (this.timer) clearInterval(this.timer); + // 清除数据刷新定时器 + if (this.dataTimer) clearInterval(this.dataTimer); + document.removeEventListener('fullscreenchange', this.handleFullscreenChange); } @@ -260,7 +279,6 @@ class FullScreen extends React.Component { }; getHomeDataArray = () => { - console.log('获取首页数据'); const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); this.props.dispatch({ @@ -489,244 +507,75 @@ class FullScreen extends React.Component { // 隐患治理公司筛选变化处理 handleHiddenCompanyChange = (company) => { - this.setState({ selectedHiddenCompany: company }); - this.getHiddenSubData(); + this.setState({ selectedHiddenCompany: company }, () => { + this.getHiddenSubData(); + }); }; getHiddenSubData = () => { - let filteredData = { - hiddenList: [ - { - companyName: '邦泰', - majorCount: 52, - generalCount: 128, - }, - { - companyName: '宏源建设', - majorCount: 18, - generalCount: 94, - }, - { - companyName: '中城建工', - majorCount: 31, - generalCount: 205, - }, - ], - hiddenRanking: [ - // 邦泰 5 个 - { - companyName: '邦泰', - hiddenName: '安全防护缺失', - qty: 42, - }, - { - companyName: '邦泰', - hiddenName: '临时用电不规范', - qty: 38, - }, - { - companyName: '邦泰', - hiddenName: '机械设备故障', - qty: 27, - }, - { - companyName: '邦泰', - hiddenName: '特种设备未年检', - qty: 15, - }, - { - companyName: '邦泰', - hiddenName: '消防器材过期', - qty: 12, - }, - // 路源 6 个 - { - companyName: '路源', - hiddenName: '消防通道堵塞', - qty: 29, - }, - { - companyName: '路源', - hiddenName: '电气线路老化', - qty: 18, - }, - { - companyName: '路源', - hiddenName: '应急照明故障', - qty: 8, - }, - { - companyName: '路源', - hiddenName: '安全标识缺失', - qty: 7, - }, - { - companyName: '路源', - hiddenName: '脚手架搭设不规范', - qty: 6, - }, - { - companyName: '路源', - hiddenName: '劳保用品佩戴不规范', - qty: 5, - }, - // 天山 7 个 - { - companyName: '天山', - hiddenName: '脚手架搭设违规', - qty: 36, - }, - { - companyName: '天山', - hiddenName: '高空抛物隐患', - qty: 22, - }, - { - companyName: '天山', - hiddenName: '动火作业监护缺失', - qty: 14, - }, - { - companyName: '天山', - hiddenName: '受限空间未审批', - qty: 11, - }, - { - companyName: '天山', - hiddenName: '起重吊装违规操作', - qty: 9, - }, - { - companyName: '天山', - hiddenName: '临时用电私拉乱接', - qty: 7, - }, - { - companyName: '天山', - hiddenName: '高处作业安全带未挂', - qty: 5, - }, - ], - hiddenRectifyList: [ - { - companyName: '邦泰', - majorCount: 52, - majorCountNo: 12, - generalCount: 128, - generalCountNo: 31, - }, - { - companyName: '宏源建设', - majorCount: 18, - majorCountNo: 5, - generalCount: 94, - generalCountNo: 22, - }, - { - companyName: '中城建工', - majorCount: 31, - majorCountNo: 9, - generalCount: 205, - generalCountNo: 47, - }, - ], - }; - if (this.state.selectedHiddenCompany) { - const selectedCompanyObj = this.state.hiddenCompanyData?.find( - (company) => company.ID === this.state.selectedHiddenCompany - ); - const selectedCompanyName = selectedCompanyObj?.NAME; - - if (selectedCompanyName) { - // 筛选 hiddenRanking - filteredData.hiddenRanking = filteredData.hiddenRanking.filter( - (item) => item.companyName === selectedCompanyName - ); - } - } - - // 模拟数据 - this.setState({ - hiddenSubData: filteredData, - }); - // 以下是真实的API调用代码,如需使用请取消注释 - // const orgId = storage('lacal').getItem('webOrgId')?.val; - // const json = initFilter(orgId); - // this.props.dispatch({ - // type: 'app/getDataByPost', - // payload: json, - // url: 'BI/BIKanBanController/HiddenManage', - // onComplete: (ret) => { - // if (ret && !this.isUnmounted) { - // this.setState({ - // hiddenSubData: ret.Data, - // }); - // } - // }, - // }); + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'BI/BIKanBanController/HiddenManage', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + let filteredData = ret; + if (this.state.selectedHiddenCompany) { + const selectedCompanyObj = this.state.hiddenCompanyData?.find( + (company) => company.ID === this.state.selectedHiddenCompany + ); + const selectedCompanyName = selectedCompanyObj?.NAME; + + if (selectedCompanyName) { + // 筛选 hiddenRanking + filteredData.hiddenRanking = filteredData.hiddenRanking.filter( + (item) => item.companyName === selectedCompanyName + ); + } + } + this.setState({ + hiddenSubData: filteredData, + }); + } + }, + }); }; // ==================== 其他页面方法 ==================== getRiskSubData = () => { - this.setState({ - riskSubData: { - riskList: [ - { - companyName: '邦泰', - majorCount: 2, - largerCount: 5, - generalCount: 12, - lowCount: 33, - totalCount: 52, - }, - { - companyName: '鸿源建设', - majorCount: 1, - largerCount: 3, - generalCount: 8, - lowCount: 20, - totalCount: 32, - }, - { - companyName: '华安工程', - majorCount: 0, - largerCount: 2, - generalCount: 6, - lowCount: 15, - totalCount: 23, - }, - { - companyName: '天达科技', - majorCount: 3, - largerCount: 4, - generalCount: 10, - lowCount: 18, - totalCount: 35, - }, - ], - riskTypeList: [ - { typeName: '动火作业', qty: 18 }, - { typeName: '高处作业', qty: 24 }, - { typeName: '临时用电', qty: 12 }, - { typeName: '起重吊装', qty: 9 }, - { typeName: '受限空间', qty: 7 }, - ], + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'BI/BIKanBanController/RiskManage', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + this.setState({ + riskSubData: ret, + }); + } }, }); }; getClassSubData = () => { - this.setState({ - classSubData: [ - { companyName: '邦泰', rate: '52%' }, - { companyName: '路源', rate: '52%' }, - { companyName: '安达', rate: '78%' }, - { companyName: '迅捷', rate: '63%' }, - { companyName: '恒通', rate: '45%' }, - { companyName: '华威', rate: '91%' }, - { companyName: '鼎盛', rate: '37%' }, - ], + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'BI/BIKanBanController/TeamManage', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + this.setState({ + classSubData: ret, + }); + } + }, }); }; @@ -767,65 +616,30 @@ class FullScreen extends React.Component { }; getDangerSubData = () => { - let filteredData = [ - { - companyName: '邦泰', - startDate: '2025-01-01 08:30:00', - endDate: '2025-01-01 17:00:00', - jobName: '爆破作业', - areaName: '矿山采场', - place: '东采区+320平台', - users: '张三,李四,王五', - monitor: '赵正三', - approveUsers: '刘安全,陈技术', - }, - { - companyName: '邦泰', - startDate: '2025-01-03 09:00:00', - endDate: '2025-01-03 18:00:00', - jobName: '高处作业', - areaName: '装置区', - place: '脱硫塔顶部', - users: '王高空,李安全带,张监护', - monitor: '周正三', - approveUsers: '吴经理,郑安全', - }, - { - companyName: '天山', - startDate: '2025-01-02 08:00:00', - endDate: '2025-01-02 17:30:00', - jobName: '高处作业', - areaName: '水泥生产线', - place: '预热器塔架', - users: '检修班组', - monitor: '天山-赵正', - approveUsers: '天山-刘经理,天山-王安全', - }, - { - companyName: '天山', - startDate: '2025-01-04 09:30:00', - endDate: '2025-01-04 18:00:00', - jobName: '有限空间作业', - areaName: '均化库', - place: '库内底部', - users: '清库作业组', - monitor: '天山-钱正', - approveUsers: '天山-孙厂长,天山-李安全', - }, - ]; + const orgId = storage('lacal').getItem('webOrgId')?.val; + const json = initFilter(orgId); + this.props.dispatch({ + type: 'app/getDataByPost', + payload: json, + url: 'BI/BIKanBanController/JobManage', + onComplete: (ret) => { + if (ret && !this.isUnmounted) { + let filteredData = ret; + if (this.state.selectedDangerCompany) { + const selectedCompanyObj = this.state.dangerCompanyData?.find( + (company) => company.ID === this.state.selectedDangerCompany + ); + const selectedCompanyName = selectedCompanyObj?.NAME; + if (selectedCompanyName) { + filteredData = filteredData.filter((item) => item.companyName === selectedCompanyName); + } + } - if (this.state.selectedDangerCompany) { - const selectedCompanyObj = this.state.dangerCompanyData?.find( - (company) => company.ID === this.state.selectedDangerCompany - ); - const selectedCompanyName = selectedCompanyObj?.NAME; - if (selectedCompanyName) { - filteredData = filteredData.filter((item) => item.companyName === selectedCompanyName); - } - } - - this.setState({ - dangerSubData: filteredData, + this.setState({ + dangerSubData: filteredData, + }); + } + }, }); }; From 34f22248b481268f4ae7485699f32d830c30eda4 Mon Sep 17 00:00:00 2001 From: yunkexin <760754045@qq.com> Date: Wed, 29 Apr 2026 11:12:46 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=AE=9A=E6=97=B65=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layout/FullScreenInter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layout/FullScreenInter.js b/src/layout/FullScreenInter.js index e08706b..ecb8356 100644 --- a/src/layout/FullScreenInter.js +++ b/src/layout/FullScreenInter.js @@ -140,7 +140,7 @@ class FullScreen extends React.Component { } } }, - 1 * 60 * 1000 + 5 * 60 * 1000 ); // 5分钟 = 300000毫秒 };