Merge branch 'master' of http://121.41.2.71:3000/wyw/mh_jy_safe_web
This commit is contained in:
commit
4542b53d26
48
package-lock.json
generated
48
package-lock.json
generated
@ -51,6 +51,7 @@
|
|||||||
"react-loadable": "5.5.0",
|
"react-loadable": "5.5.0",
|
||||||
"react-orgchart": "^1.0.5",
|
"react-orgchart": "^1.0.5",
|
||||||
"react-resizable": "^1.10.1",
|
"react-resizable": "^1.10.1",
|
||||||
|
"react-slick": "^0.31.0",
|
||||||
"react-to-print": "^2.0.0-alpha-2",
|
"react-to-print": "^2.0.0-alpha-2",
|
||||||
"react-umeditor": "1.0.12",
|
"react-umeditor": "1.0.12",
|
||||||
"react-websocket": "2.0.1",
|
"react-websocket": "2.0.1",
|
||||||
@ -2637,6 +2638,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
|
||||||
"integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w=="
|
"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": {
|
"node_modules/any-promise": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
@ -21055,19 +21072,18 @@
|
|||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/react-slick": {
|
"node_modules/react-slick": {
|
||||||
"version": "0.25.2",
|
"version": "0.31.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz",
|
"resolved": "https://registry.npmmirror.com/react-slick/-/react-slick-0.31.0.tgz",
|
||||||
"integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==",
|
"integrity": "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"enquire.js": "^2.1.6",
|
|
||||||
"json2mq": "^0.2.0",
|
"json2mq": "^0.2.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"resize-observer-polyfill": "^1.5.0"
|
"resize-observer-polyfill": "^1.5.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"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-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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-test-renderer": {
|
"node_modules/react-test-renderer": {
|
||||||
@ -22508,7 +22524,6 @@
|
|||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmmirror.com/slick-carousel/-/slick-carousel-1.8.1.tgz",
|
"resolved": "https://registry.npmmirror.com/slick-carousel/-/slick-carousel-1.8.1.tgz",
|
||||||
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
|
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"jquery": ">=1.8.0"
|
"jquery": ">=1.8.0"
|
||||||
}
|
}
|
||||||
@ -28673,6 +28688,18 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
|
||||||
"integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w=="
|
"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": {
|
"react-slick": {
|
||||||
"version": "0.25.2",
|
"version": "0.31.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz",
|
"resolved": "https://registry.npmmirror.com/react-slick/-/react-slick-0.31.0.tgz",
|
||||||
"integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==",
|
"integrity": "sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"enquire.js": "^2.1.6",
|
|
||||||
"json2mq": "^0.2.0",
|
"json2mq": "^0.2.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"resize-observer-polyfill": "^1.5.0"
|
"resize-observer-polyfill": "^1.5.0"
|
||||||
|
|||||||
@ -52,6 +52,7 @@
|
|||||||
"react-loadable": "5.5.0",
|
"react-loadable": "5.5.0",
|
||||||
"react-orgchart": "^1.0.5",
|
"react-orgchart": "^1.0.5",
|
||||||
"react-resizable": "^1.10.1",
|
"react-resizable": "^1.10.1",
|
||||||
|
"react-slick": "^0.31.0",
|
||||||
"react-to-print": "^2.0.0-alpha-2",
|
"react-to-print": "^2.0.0-alpha-2",
|
||||||
"react-umeditor": "1.0.12",
|
"react-umeditor": "1.0.12",
|
||||||
"react-websocket": "2.0.1",
|
"react-websocket": "2.0.1",
|
||||||
|
|||||||
228
src/layout/FullOther/ClassBuild.js
Normal file
228
src/layout/FullOther/ClassBuild.js
Normal file
@ -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}<br/>`;
|
||||||
|
params.forEach((param) => {
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}%<br/>`;
|
||||||
|
});
|
||||||
|
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 (
|
||||||
|
<div className={styles.trainingContentWrapper}>
|
||||||
|
<div className={styles.trainingGrid}>
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="typeBarChart" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ClassBuild;
|
||||||
312
src/layout/FullOther/DangerJob.js
Normal file
312
src/layout/FullOther/DangerJob.js
Normal file
@ -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 <Option value="">暂无公司</Option>;
|
||||||
|
}
|
||||||
|
return companyData.map((company, index) => (
|
||||||
|
<Option key={company.ID || index} value={company.ID}>
|
||||||
|
{' '}
|
||||||
|
{/* 使用 ID 作为 value,与 TrainingContent 一致 */}
|
||||||
|
{company.NAME}
|
||||||
|
</Option>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理公司筛选变化
|
||||||
|
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 (
|
||||||
|
<div style={{ textAlign: 'center', padding: '50px', color: '#999' }}>
|
||||||
|
{selectedCompany ? `${companyName}暂无危险作业数据` : '暂无危险作业数据'}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '公司',
|
||||||
|
dataIndex: 'companyName',
|
||||||
|
key: 'companyName',
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
fixed: 'left',
|
||||||
|
render: (text) => <strong>{text}</strong>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始时间',
|
||||||
|
dataIndex: 'startDate',
|
||||||
|
key: 'startDate',
|
||||||
|
align: 'center',
|
||||||
|
width: 160,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束时间',
|
||||||
|
dataIndex: 'endDate',
|
||||||
|
key: 'endDate',
|
||||||
|
align: 'center',
|
||||||
|
width: 160,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作业名称',
|
||||||
|
dataIndex: 'jobName',
|
||||||
|
key: 'jobName',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120,
|
||||||
|
render: (text) => <span style={{ fontWeight: 'bold' }}>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作业区域',
|
||||||
|
dataIndex: 'areaName',
|
||||||
|
key: 'areaName',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作业地点',
|
||||||
|
dataIndex: 'place',
|
||||||
|
key: 'place',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 150,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '作业人员',
|
||||||
|
dataIndex: 'users',
|
||||||
|
key: 'users',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 150,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '监护人',
|
||||||
|
dataIndex: 'monitor',
|
||||||
|
key: 'monitor',
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '审批领导',
|
||||||
|
dataIndex: 'approveUsers',
|
||||||
|
key: 'approveUsers',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 150,
|
||||||
|
render: (text) => <span>{text || '-'}</span>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 计算横向滚动宽度(如果列数过多)
|
||||||
|
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 (
|
||||||
|
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: '20px 0 0 0',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: '17px',
|
||||||
|
color: '#000',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
当日各公司危险作业清单
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1, overflow: 'auto', padding: '10px' }}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={dataSource}
|
||||||
|
pagination={false}
|
||||||
|
scroll={scrollConfig}
|
||||||
|
size="small"
|
||||||
|
bordered
|
||||||
|
className={styles.certificateTable}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* 分页组件 */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
padding: '12px 16px',
|
||||||
|
borderTop: '1px solid #f0f0f0',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Pagination
|
||||||
|
current={currentPage}
|
||||||
|
pageSize={pageSize}
|
||||||
|
total={total}
|
||||||
|
showSizeChanger
|
||||||
|
showQuickJumper
|
||||||
|
showTotal={(total) => `共 ${total} 条记录`}
|
||||||
|
onChange={this.handlePageChange}
|
||||||
|
onShowSizeChange={this.handleShowSizeChange}
|
||||||
|
pageSizeOptions={['10', '20', '50']}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className={styles.trainingContentWrapper}>
|
||||||
|
<div className={styles.trainingGrid}>
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
{/* 公司筛选器 */}
|
||||||
|
<div className={styles.monthSelectorWrapper}>
|
||||||
|
<span className={styles.monthSelectorLabel}>选择公司:</span>
|
||||||
|
<Select
|
||||||
|
value={selectedCompany}
|
||||||
|
onChange={this.handleCompanyChange}
|
||||||
|
style={{ width: 150 }}
|
||||||
|
className={styles.monthSelect}
|
||||||
|
allowClear
|
||||||
|
placeholder="全部公司"
|
||||||
|
>
|
||||||
|
{this.getCompanyOptions()}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
{this.renderDangerTable()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DangerJob;
|
||||||
578
src/layout/FullOther/HiddenSolve.js
Normal file
578
src/layout/FullOther/HiddenSolve.js
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
// 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) => (
|
||||||
|
<Option key={company.ID || index} value={company.ID}>
|
||||||
|
{company.NAME}
|
||||||
|
</Option>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据选中的公司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}<br/>`;
|
||||||
|
params.forEach((param) => {
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
|
||||||
|
});
|
||||||
|
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}<br/>`;
|
||||||
|
params.forEach((param) => {
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
|
||||||
|
});
|
||||||
|
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 (
|
||||||
|
// <div style={{ textAlign: 'center', padding: '50px', color: '#999' }}>
|
||||||
|
// {selectedCompanyName ? `${selectedCompanyName} 暂无隐患排名数据` : '暂无隐患排名数据'}
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: '20px 0 0 0',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: '17px',
|
||||||
|
color: '#000',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
隐患次数排名列表
|
||||||
|
</div>
|
||||||
|
<div style={{ textAlign: 'right', padding: '10px 20px' }}>
|
||||||
|
<span style={{ marginRight: '8px', fontSize: '14px', color: '#000' }}>选择公司:</span>
|
||||||
|
<Select
|
||||||
|
value={selectedCompany || undefined}
|
||||||
|
onChange={onCompanyChange}
|
||||||
|
style={{ width: 150 }}
|
||||||
|
allowClear
|
||||||
|
placeholder="全部公司"
|
||||||
|
>
|
||||||
|
{this.getCompanyOptions()}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
{/* 公司筛选器 - 与 TrainingContent 样式保持一致 */}
|
||||||
|
|
||||||
|
<div style={{ flex: 1, overflow: 'auto', padding: '0 20px 20px 20px' }}>
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||||
|
<thead>
|
||||||
|
<tr style={{ backgroundColor: '#f5f5f5', borderBottom: '2px solid #e8e8e8' }}>
|
||||||
|
<th style={{ padding: '12px 8px', textAlign: 'center', fontWeight: 'bold', color: '#000' }}>序号</th>
|
||||||
|
<th style={{ padding: '12px 8px', textAlign: 'center', fontWeight: 'bold', color: '#000' }}>
|
||||||
|
隐患名称
|
||||||
|
</th>
|
||||||
|
<th style={{ padding: '12px 8px', textAlign: 'center', fontWeight: 'bold', color: '#000' }}>数量</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{hiddenRanking.length > 0 ? (
|
||||||
|
hiddenRanking.map((item, index) => (
|
||||||
|
<tr
|
||||||
|
key={index}
|
||||||
|
style={{
|
||||||
|
borderBottom: '1px solid #e8e8e8',
|
||||||
|
backgroundColor: index % 2 === 0 ? '#fafafa' : '#fff',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<td style={{ padding: '10px 8px', textAlign: 'center', color: '#000' }}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
padding: '10px 8px',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '#000',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{index + 1}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td style={{ padding: '10px 8px', textAlign: 'center', color: '#000' }}>{item.hiddenName}</td>
|
||||||
|
<td style={{ padding: '10px 8px', textAlign: 'center', color: '#000' }}>
|
||||||
|
<span style={{ color: '#4285F4', fontWeight: 'bold' }}>{item.qty}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div style={{ textAlign: 'center', padding: '50px', color: '#999' }}>{'暂无隐患排名数据'}</div>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className={styles.trainingContentWrapper}>
|
||||||
|
<div className={styles.trainingGrid}>
|
||||||
|
{/* 第一行 - 柱状图(left)+ 排名列表(right) */}
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard} style={{ flex: '0 0 70%' }}>
|
||||||
|
{/* 添加公司筛选器(左上角,与 TrainingContent 样式一致) */}
|
||||||
|
<div id="hiddenBarChart" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.trainingCard} style={{ flex: '0 0 30%' }}>
|
||||||
|
{this.renderRankingList()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 第二行 - 隐患整改情况柱状图 */}
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="hiddenRectifyChart" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HiddenSolve;
|
||||||
@ -1,13 +1,18 @@
|
|||||||
// HomeContent.js
|
// HomeContent.js
|
||||||
import React, { useEffect, useRef } from 'react';
|
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 styles from './../fullinter.less';
|
||||||
import echarts from 'echarts';
|
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 {
|
class HomeContent extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.carouselRef = React.createRef();
|
this.sliderRef = React.createRef();
|
||||||
this.echartsInstances = {
|
this.echartsInstances = {
|
||||||
riskLevel: null,
|
riskLevel: null,
|
||||||
safeCheckChart: null,
|
safeCheckChart: null,
|
||||||
@ -16,6 +21,7 @@ class HomeContent extends React.Component {
|
|||||||
};
|
};
|
||||||
this.chartResizeHandlers = {};
|
this.chartResizeHandlers = {};
|
||||||
this.isUnmounted = false;
|
this.isUnmounted = false;
|
||||||
|
this.autoplayTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForElement = (elementId, maxRetries = 10) => {
|
waitForElement = (elementId, maxRetries = 10) => {
|
||||||
@ -111,46 +117,6 @@ class HomeContent extends React.Component {
|
|||||||
window.addEventListener('resize', resizeHandler);
|
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 () => {
|
safeCheckChart = async () => {
|
||||||
if (this.isUnmounted) return;
|
if (this.isUnmounted) return;
|
||||||
const elementExists = await this.waitForElement('safeCheckChart');
|
const elementExists = await this.waitForElement('safeCheckChart');
|
||||||
@ -164,48 +130,130 @@ class HomeContent extends React.Component {
|
|||||||
let safeCheckCharts = document.getElementById('safeCheckChart');
|
let safeCheckCharts = document.getElementById('safeCheckChart');
|
||||||
if (!safeCheckCharts) return;
|
if (!safeCheckCharts) return;
|
||||||
|
|
||||||
const barTopColor = ['#02c3f1', '#53e568', '#a154e9'];
|
const hiddenSummary = this.props.hiddenSummary || {};
|
||||||
const jobData = this.props.jobTodayTop3 || [];
|
|
||||||
|
|
||||||
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);
|
this.echartsInstances.safeCheckChart = echarts.init(safeCheckCharts);
|
||||||
let xdata = this.transformDat(jobData, barTopColor, 1);
|
|
||||||
|
|
||||||
this.echartsInstances.safeCheckChart.setOption({
|
this.echartsInstances.safeCheckChart.setOption({
|
||||||
title: {
|
title: {
|
||||||
text: '当日工作票排名前三家的数据',
|
text: '当月隐患统计',
|
||||||
textStyle: { fontSize: 16, color: '#fff' },
|
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}<br/>`;
|
||||||
|
params.forEach((param) => {
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
||||||
legend: {
|
legend: {
|
||||||
data: xdata.legendData,
|
data: legendData,
|
||||||
align: 'right',
|
right: 0,
|
||||||
right: 10,
|
top: '10%',
|
||||||
itemGap: 16,
|
itemGap: 12,
|
||||||
itemWidth: 18,
|
itemWidth: 16,
|
||||||
itemHeight: 10,
|
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: [
|
xAxis: [
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: xdata.companyNames,
|
data: ['当月隐患'],
|
||||||
axisLine: { lineStyle: { color: '#3eb2e8' } },
|
axisLine: { lineStyle: { color: '#c4c6c9' } },
|
||||||
axisLabel: { textStyle: { color: '#fff' } },
|
axisLabel: { textStyle: { color: '#000', fontSize: 14 } },
|
||||||
|
axisTick: { show: false },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
name: '数量',
|
||||||
|
nameTextStyle: { color: '#000' },
|
||||||
axisLine: { show: false },
|
axisLine: { show: false },
|
||||||
axisLabel: { textStyle: { color: '#fff' } },
|
axisTick: { show: false },
|
||||||
splitLine: { lineStyle: { color: '#4784e8' } },
|
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 = () => {
|
const resizeHandler = () => {
|
||||||
@ -230,9 +278,33 @@ class HomeContent extends React.Component {
|
|||||||
const dangerOperationCharts = document.getElementById('dangerOperationChart');
|
const dangerOperationCharts = document.getElementById('dangerOperationChart');
|
||||||
if (!dangerOperationCharts) return;
|
if (!dangerOperationCharts) return;
|
||||||
|
|
||||||
const linkData = this.props.linkSum || [];
|
const linkData = this.props.jobTodayQty || [];
|
||||||
if (linkData.length === 0) return;
|
|
||||||
|
|
||||||
|
// 如果没有数据,显示“暂无数据”提示
|
||||||
|
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);
|
this.echartsInstances.dangerOperation = echarts.init(dangerOperationCharts);
|
||||||
const xAxisData = linkData.map((item) => item.name);
|
const xAxisData = linkData.map((item) => item.name);
|
||||||
const seriesData = linkData.map((item) => item.qty);
|
const seriesData = linkData.map((item) => item.qty);
|
||||||
@ -253,10 +325,10 @@ class HomeContent extends React.Component {
|
|||||||
formatter: function (params) {
|
formatter: function (params) {
|
||||||
const color = params[0].color;
|
const color = params[0].color;
|
||||||
return `<div style="display: flex; align-items: center;">
|
return `<div style="display: flex; align-items: center;">
|
||||||
<span style="display: inline-block; width: 12px; height: 12px; background-color: ${color}; margin-right: 8px;"></span>
|
<span style="display: inline-block; width: 12px; height: 12px; background-color: ${color}; margin-right: 8px;"></span>
|
||||||
<span>${params[0].name}:</span>
|
<span>${params[0].name}:</span>
|
||||||
<span style="font-weight: bold; margin-left: 8px; font-size: 16px;">${params[0].value}</span>
|
<span style="font-weight: bold; margin-left: 8px; font-size: 16px;">${params[0].value}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
},
|
},
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.5)',
|
backgroundColor: 'rgba(255, 255, 255, 0.5)',
|
||||||
borderColor: '#FFFFFF',
|
borderColor: '#FFFFFF',
|
||||||
@ -268,7 +340,7 @@ class HomeContent extends React.Component {
|
|||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: xAxisData,
|
data: xAxisData,
|
||||||
axisLine: { lineStyle: { color: '#000' } },
|
axisLine: { lineStyle: { color: '#c4c6c9' } },
|
||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
axisLabel: { textStyle: { color: '#000' }, rotate: 30, interval: 0 },
|
axisLabel: { textStyle: { color: '#000' }, rotate: 30, interval: 0 },
|
||||||
},
|
},
|
||||||
@ -374,17 +446,17 @@ class HomeContent extends React.Component {
|
|||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: ['培训人次', '培训场次'],
|
data: ['培训人次', '培训场次'],
|
||||||
orient: 'vertical', // 垂直排列
|
orient: 'vertical',
|
||||||
right: 0, // 水平居中
|
right: 0,
|
||||||
top: 'middle', // 垂直居中
|
top: 'middle',
|
||||||
itemGap: 16, // 图例项间隔
|
itemGap: 16,
|
||||||
itemWidth: 18,
|
itemWidth: 18,
|
||||||
itemHeight: 12,
|
itemHeight: 12,
|
||||||
textStyle: { color: '#000', fontSize: 14 },
|
textStyle: { color: '#000', fontSize: 14 },
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: '5%',
|
left: '5%',
|
||||||
right: '5%', // 为右侧垂直图例留出空间
|
right: '5%',
|
||||||
top: '18%',
|
top: '18%',
|
||||||
bottom: '5%',
|
bottom: '5%',
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
@ -393,8 +465,8 @@ class HomeContent extends React.Component {
|
|||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: listNAME,
|
data: listNAME,
|
||||||
axisLine: { show: false }, // 隐藏x轴线
|
axisLine: { lineStyle: { color: '#c4c6c9' } },
|
||||||
axisTick: { show: false }, // 隐藏x轴刻度线
|
axisTick: { show: false },
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
textStyle: { color: '#000' },
|
textStyle: { color: '#000' },
|
||||||
rotate: listNAME.length > 4 ? 15 : 0,
|
rotate: listNAME.length > 4 ? 15 : 0,
|
||||||
@ -405,16 +477,16 @@ class HomeContent extends React.Component {
|
|||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
show: true, // 显示y轴
|
show: true,
|
||||||
axisLine: { show: false }, // 隐藏y轴线
|
axisLine: { show: false },
|
||||||
axisTick: { show: false }, // 隐藏y轴刻度线
|
axisTick: { show: false },
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
show: true, // 显示数值标签
|
show: true,
|
||||||
textStyle: { color: '#000' },
|
textStyle: { color: '#000' },
|
||||||
},
|
},
|
||||||
splitLine: { show: false }, // 隐藏横向网格线
|
splitLine: { show: false },
|
||||||
name: '', // 不显示单位名称
|
name: '',
|
||||||
nameTextStyle: { show: false }, // 隐藏单位文字
|
nameTextStyle: { show: false },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
@ -424,8 +496,8 @@ class HomeContent extends React.Component {
|
|||||||
data: monthPersonCount,
|
data: monthPersonCount,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
normal: {
|
normal: {
|
||||||
color: '#4285F4', // 蓝色
|
color: '#4285F4',
|
||||||
barBorderRadius: 12, // 柱体圆角
|
barBorderRadius: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
@ -442,8 +514,8 @@ class HomeContent extends React.Component {
|
|||||||
data: monthRecordCount,
|
data: monthRecordCount,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
normal: {
|
normal: {
|
||||||
color: '#ffe066', // 黄色
|
color: '#ffe066',
|
||||||
barBorderRadius: 12, // 柱体圆角(上左、上右、下右、下左)
|
barBorderRadius: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
@ -468,6 +540,28 @@ class HomeContent extends React.Component {
|
|||||||
window.addEventListener('resize', resizeHandler);
|
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 = () => {
|
initAllCharts = () => {
|
||||||
if (this.isUnmounted) return;
|
if (this.isUnmounted) return;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -500,26 +594,52 @@ class HomeContent extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handlePrev = () => {
|
handlePrev = () => {
|
||||||
if (this.carouselRef.current) {
|
if (this.sliderRef.current) {
|
||||||
this.carouselRef.current.prev();
|
this.sliderRef.current.slickPrev();
|
||||||
|
const nextIndex = this.props.currentMediaIndex - 1;
|
||||||
|
if (nextIndex >= 0) {
|
||||||
|
this.props.onCarouselChange?.(nextIndex);
|
||||||
|
}
|
||||||
|
// 鼠标点击时重启自动播放计时器
|
||||||
|
this.restartAutoplay();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleNext = () => {
|
handleNext = () => {
|
||||||
if (this.carouselRef.current) {
|
if (this.sliderRef.current) {
|
||||||
this.carouselRef.current.next();
|
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) => {
|
handleDotClick = (index) => {
|
||||||
this.props.onDotClick?.(index);
|
this.props.onDotClick?.(index);
|
||||||
if (this.carouselRef.current) {
|
if (this.sliderRef.current) {
|
||||||
this.carouselRef.current.goTo(index);
|
this.sliderRef.current.slickGoTo(index);
|
||||||
}
|
}
|
||||||
|
// 点击圆点时重启自动播放
|
||||||
|
this.restartAutoplay();
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCarouselChange = (current) => {
|
||||||
|
this.props.onCarouselChange?.(current);
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -528,12 +648,20 @@ class HomeContent extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
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 (
|
if (
|
||||||
prevProps.riskTypeRate !== this.props.riskTypeRate ||
|
prevProps.riskTypeRate !== this.props.riskTypeRate ||
|
||||||
prevProps.jobTodayTop3 !== this.props.jobTodayTop3 ||
|
prevProps.hiddenSummary !== this.props.hiddenSummary ||
|
||||||
prevProps.linkSum !== this.props.linkSum ||
|
prevProps.jobTodayQty !== this.props.jobTodayQty ||
|
||||||
prevProps.taskTop3 !== this.props.taskTop3 ||
|
prevProps.taskTop3 !== this.props.taskTop3 ||
|
||||||
prevProps.trainingData !== this.props.trainingData // 新增
|
prevProps.trainingData !== this.props.trainingData
|
||||||
) {
|
) {
|
||||||
this.disposeAllCharts();
|
this.disposeAllCharts();
|
||||||
this.initAllCharts();
|
this.initAllCharts();
|
||||||
@ -542,18 +670,170 @@ class HomeContent extends React.Component {
|
|||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.isUnmounted = true;
|
this.isUnmounted = true;
|
||||||
|
this.stopAutoplay();
|
||||||
this.disposeAllCharts();
|
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 (
|
||||||
|
<div style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
|
<Icon type="loading" style={{ fontSize: '32px' }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentAnnouncement) return null;
|
||||||
|
|
||||||
|
const { TITLE, ABSTRACT, START, END, CONTENT, Nav_Files = [] } = currentAnnouncement;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* 标题 */}
|
||||||
|
<div style={{ textAlign: 'center', fontSize: '16px', color: '#333', marginBottom: '16px', fontWeight: 'bold' }}>
|
||||||
|
{TITLE}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 摘要 */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#676767',
|
||||||
|
wordBreak: 'break-all',
|
||||||
|
wordWrap: 'break-word',
|
||||||
|
marginBottom: '12px',
|
||||||
|
lineHeight: '1.5',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ABSTRACT}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 日期 */}
|
||||||
|
<div style={{ fontSize: '12px', color: '#999', marginBottom: '16px', textAlign: 'center' }}>
|
||||||
|
{START} 至 {END}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 分割线 */}
|
||||||
|
<div style={{ borderTop: '1px solid #e8e8e8', marginBottom: '16px' }}></div>
|
||||||
|
|
||||||
|
{/* 正文内容 */}
|
||||||
|
<div
|
||||||
|
className={styles.announcementContent}
|
||||||
|
dangerouslySetInnerHTML={{ __html: CONTENT || '' }}
|
||||||
|
style={{
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '1.6',
|
||||||
|
color: '#333',
|
||||||
|
marginBottom: '16px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 附件列表 */}
|
||||||
|
{Nav_Files && Nav_Files.length > 0 && (
|
||||||
|
<div style={{ marginTop: '16px' }}>
|
||||||
|
<div style={{ fontWeight: 'bold', marginBottom: '12px', fontSize: '14px', color: '#333' }}>
|
||||||
|
附件 ({Nav_Files.length}个)
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
|
{Nav_Files.map((file, index) => (
|
||||||
|
<div
|
||||||
|
key={file.ID || index}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
padding: '8px 12px',
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
borderRadius: '4px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: '14px', color: '#333', flex: 1 }}>
|
||||||
|
<Icon type="paper-clip" style={{ marginRight: '8px', color: '#1890ff' }} />
|
||||||
|
{file.Nav_ImgFile?.FILE_NAME || '未知文件'}
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => this.handlePreview(file)}
|
||||||
|
style={{ padding: '0 8px' }}
|
||||||
|
>
|
||||||
|
<Icon type="eye" />
|
||||||
|
预览
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => this.handleDownload(file)}
|
||||||
|
style={{ padding: '0 8px' }}
|
||||||
|
>
|
||||||
|
<Icon type="download" />
|
||||||
|
下载
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
riskTypeRate = [],
|
riskTypeRate = [],
|
||||||
riskTotal = [],
|
|
||||||
mediaList = [],
|
|
||||||
announcementList = [],
|
|
||||||
currentMediaIndex = 0,
|
currentMediaIndex = 0,
|
||||||
|
hiddenSummary = {},
|
||||||
|
trainingData = {}, // 新增 props
|
||||||
|
onAnnouncementClick,
|
||||||
|
announcementModalVisible,
|
||||||
|
onAnnouncementModalClose,
|
||||||
} = this.props;
|
} = 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div className={styles.homeContentWrapper}>
|
<div className={styles.homeContentWrapper}>
|
||||||
<Row className={styles.homeContentRow} gutter={0}>
|
<Row className={styles.homeContentRow} gutter={0}>
|
||||||
@ -581,12 +861,18 @@ class HomeContent extends React.Component {
|
|||||||
<Row className={styles.riskRow}>
|
<Row className={styles.riskRow}>
|
||||||
<Col span={8} className={styles.hiddenStatsCol}>
|
<Col span={8} className={styles.hiddenStatsCol}>
|
||||||
<div className={styles.hiddenTitle}>年度隐患数据</div>
|
<div className={styles.hiddenTitle}>年度隐患数据</div>
|
||||||
{riskTotal.map((item, index) => (
|
<div className={styles.hiddenStatItem}>
|
||||||
<div key={index} className={styles.hiddenStatItem}>
|
<div className={styles.hiddenStatName}>年度重大隐患数</div>
|
||||||
<div className={styles.hiddenStatName}>{item.name}</div>
|
<div className={styles.hiddenStatValue}>{hiddenSummary.majorQty}</div>
|
||||||
<div className={styles.hiddenStatValue}>{item.value}</div>
|
</div>
|
||||||
</div>
|
<div className={styles.hiddenStatItem}>
|
||||||
))}
|
<div className={styles.hiddenStatName}>年度一般隐患数量</div>
|
||||||
|
<div className={styles.hiddenStatValue}>{hiddenSummary.generalQty}</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.hiddenStatItem}>
|
||||||
|
<div className={styles.hiddenStatName}>未整改隐患数量</div>
|
||||||
|
<div className={styles.hiddenStatValue}>{hiddenSummary.unfinishQty}</div>
|
||||||
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={16} className={styles.chartCol}>
|
<Col span={16} className={styles.chartCol}>
|
||||||
<div id="safeCheckChart" className={styles.chartContainer}></div>
|
<div id="safeCheckChart" className={styles.chartContainer}></div>
|
||||||
@ -598,47 +884,55 @@ class HomeContent extends React.Component {
|
|||||||
{/* 中间区域 */}
|
{/* 中间区域 */}
|
||||||
<Col span={10} className={styles.middleCol}>
|
<Col span={10} className={styles.middleCol}>
|
||||||
<div className={styles.sloganCard}>
|
<div className={styles.sloganCard}>
|
||||||
<div className={styles.sloganText}>安全方针:以人为本、关注健康、依法治企、安全发展。</div>
|
<div className={styles.sloganContent} dangerouslySetInnerHTML={{ __html: trainingData.TITLE }} />
|
||||||
<div className={styles.sloganSubText}>安全理念:一切风险皆可控,一切事故皆可防!</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.carouselCard}>
|
<div className={styles.carouselCard}>
|
||||||
<div className={styles.carouselWrapper}>
|
<div className={styles.carouselWrapper}>
|
||||||
<Carousel
|
{trainingData.listVideoImg && trainingData.listVideoImg[0] ? (
|
||||||
ref={this.carouselRef}
|
trainingData.listVideoImg[0].TYPE == 10 ? (
|
||||||
autoplay
|
<video
|
||||||
autoplaySpeed={5000}
|
src={configc.picServerHost + trainingData.listVideoImg[0].FILE_PATH}
|
||||||
effect="fade"
|
className={styles.mediaVideo}
|
||||||
dots={false}
|
autoplay={videoConfig.autoPlay ? 'autoplay' : undefined}
|
||||||
pauseOnHover={true}
|
loop={videoConfig.loop ? 'loop' : undefined}
|
||||||
afterChange={this.handleCarouselChange}
|
muted={videoConfig.muted ? 'muted' : undefined}
|
||||||
className={styles.carousel}
|
controls={videoConfig.controls ? 'controls' : undefined}
|
||||||
>
|
playsInline
|
||||||
{mediaList.map((item, index) => (
|
|
||||||
<div key={index} className={styles.carouselItem}>
|
|
||||||
{item.type === 'video' ? (
|
|
||||||
<video src={item.url} className={styles.mediaVideo} autoPlay muted loop playsInline />
|
|
||||||
) : (
|
|
||||||
<img src={item.url} alt={`轮播图 ${index + 1}`} className={styles.mediaImage} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Carousel>
|
|
||||||
<div className={styles.customArrowLeft} onClick={this.handlePrev}>
|
|
||||||
<Icon type="left" style={{ fontSize: '24px', color: 'rgba(0, 0, 0, 0.5)' }} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.customArrowRight} onClick={this.handleNext}>
|
|
||||||
<Icon type="right" style={{ fontSize: '24px', color: 'rgba(0, 0, 0, 0.5)' }} />
|
|
||||||
</div>
|
|
||||||
<div className={styles.customDots}>
|
|
||||||
{mediaList.map((_, index) => (
|
|
||||||
<span
|
|
||||||
key={index}
|
|
||||||
className={`${styles.dot} ${currentMediaIndex === index ? styles.activeDot : ''}`}
|
|
||||||
onClick={() => this.handleDotClick(index)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
) : (
|
||||||
</div>
|
<div>
|
||||||
|
<Slider ref={this.sliderRef} {...slickSettings} className={styles.carousel}>
|
||||||
|
{trainingData.listVideoImg?.map((item, index) => (
|
||||||
|
<div key={index} className={styles.carouselItem}>
|
||||||
|
<img
|
||||||
|
src={configc.picServerHost + item.FILE_PATH}
|
||||||
|
alt={`轮播图 ${index + 1}`}
|
||||||
|
className={styles.mediaImage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
<div className={styles.customArrowLeft} onClick={this.handlePrev}>
|
||||||
|
<Icon type="left" style={{ fontSize: '24px', color: 'rgba(0, 0, 0, 0.5)' }} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.customArrowRight} onClick={this.handleNext}>
|
||||||
|
<Icon type="right" style={{ fontSize: '24px', color: 'rgba(0, 0, 0, 0.5)' }} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.customDots}>
|
||||||
|
{trainingData.listVideoImg?.map((_, index) => (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className={`${styles.dot} ${currentMediaIndex === index ? styles.activeDot : ''}`}
|
||||||
|
onClick={() => this.handleDotClick(index)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<div></div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -648,23 +942,21 @@ class HomeContent extends React.Component {
|
|||||||
<Icon type="sound" className={styles.announcementIcon} />
|
<Icon type="sound" className={styles.announcementIcon} />
|
||||||
<span>公司公告</span>
|
<span>公司公告</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={styles.announcementCount}>共 {announcementList.length} 条公告</span>
|
<span className={styles.announcementCount}>共 {trainingData.listAnnourcement?.length || 0} 条公告</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.announcementList}>
|
<div className={styles.announcementList}>
|
||||||
{announcementList.length > 0 ? (
|
{trainingData.listAnnourcement?.length > 0 ? (
|
||||||
<ul className={styles.announcementUl}>
|
<ul className={styles.announcementUl}>
|
||||||
{announcementList.map((item, index) => (
|
{trainingData.listAnnourcement.map((item, index) => (
|
||||||
<li
|
<li
|
||||||
key={item.id || index}
|
key={item.id || index}
|
||||||
className={styles.announcementItem}
|
className={styles.announcementItem}
|
||||||
onClick={() => item.url && window.open(item.url)}
|
onClick={() => onAnnouncementClick && onAnnouncementClick(item)}
|
||||||
>
|
>
|
||||||
<span className={styles.announcementItemTitle} title={item.title}>
|
<span className={styles.announcementItemTitle} title={item.TITLE}>
|
||||||
{item.title}
|
{item.TITLE}
|
||||||
</span>
|
|
||||||
<span className={styles.announcementItemTime}>
|
|
||||||
{item.publishTime || item.createTime || item.date}
|
|
||||||
</span>
|
</span>
|
||||||
|
<span className={styles.announcementItemTime}>{item.START}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -684,14 +976,13 @@ class HomeContent extends React.Component {
|
|||||||
<Row className={styles.riskRow}>
|
<Row className={styles.riskRow}>
|
||||||
<Col span={8} className={styles.trainingStatsCol}>
|
<Col span={8} className={styles.trainingStatsCol}>
|
||||||
<div className={styles.trainingTitle}>年度培训数据</div>
|
<div className={styles.trainingTitle}>年度培训数据</div>
|
||||||
{this.props.trainingData?.listNAME?.map((name, index) => (
|
{trainingData?.listNAME?.map((name, index) => (
|
||||||
<div key={index} className={styles.trainingStatItem}>
|
<div key={index} className={styles.trainingStatItem}>
|
||||||
<div className={styles.trainingStatName}>{name}</div>
|
<div className={styles.trainingStatName}>{name}</div>
|
||||||
<div className={styles.trainingStatValue}>{this.props.trainingData?.YearCount?.[index] || 0}</div>
|
<div className={styles.trainingStatValue}>{trainingData?.YearCount?.[index] || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{/* 如果 listNAME 为空,显示默认提示 */}
|
{(!trainingData?.listNAME || trainingData.listNAME.length === 0) && (
|
||||||
{(!this.props.trainingData?.listNAME || this.props.trainingData.listNAME.length === 0) && (
|
|
||||||
<div style={{ textAlign: 'center', color: '#999', padding: '20px 0' }}>暂无培训数据</div>
|
<div style={{ textAlign: 'center', color: '#999', padding: '20px 0' }}>暂无培训数据</div>
|
||||||
)}
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
@ -708,6 +999,21 @@ class HomeContent extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
{/* 公告详情弹窗 */}
|
||||||
|
<Modal
|
||||||
|
title="公告详情"
|
||||||
|
visible={announcementModalVisible}
|
||||||
|
onCancel={onAnnouncementModalClose}
|
||||||
|
footer={[
|
||||||
|
<Button key="close" onClick={onAnnouncementModalClose}>
|
||||||
|
关闭
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
width="80%"
|
||||||
|
bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }}
|
||||||
|
>
|
||||||
|
{this.renderAnnouncementModal()}
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
515
src/layout/FullOther/RiskControl.js
Normal file
515
src/layout/FullOther/RiskControl.js
Normal file
@ -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}<br/>`;
|
||||||
|
params.forEach((param) => {
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
|
||||||
|
});
|
||||||
|
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}<br/>`;
|
||||||
|
params.forEach((param) => {
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}<br/>`;
|
||||||
|
});
|
||||||
|
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 <div style={{ textAlign: 'center', padding: '50px', color: '#999' }}>暂无风险数据</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格列配置(与 trainingContent 样式保持一致)
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '公司名称',
|
||||||
|
dataIndex: 'companyName',
|
||||||
|
key: 'companyName',
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
render: (text) => <strong>{text}</strong>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '重大风险',
|
||||||
|
dataIndex: 'majorCount',
|
||||||
|
key: 'majorCount',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (text) => <span style={{ color: '#c92a2a', fontWeight: 'bold' }}>{text}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '较大风险',
|
||||||
|
dataIndex: 'largerCount',
|
||||||
|
key: 'largerCount',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (text) => <span style={{ color: '#ffa94d', fontWeight: 'bold' }}>{text}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '一般风险',
|
||||||
|
dataIndex: 'generalCount',
|
||||||
|
key: 'generalCount',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (text) => <span style={{ color: '#ffe066', fontWeight: 'bold' }}>{text}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '低风险',
|
||||||
|
dataIndex: 'lowCount',
|
||||||
|
key: 'lowCount',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (text) => <span style={{ color: '#4285F4', fontWeight: 'bold' }}>{text}</span>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '小计',
|
||||||
|
dataIndex: 'totalCount',
|
||||||
|
key: 'totalCount',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (text) => <strong style={{ color: '#000', fontSize: '14px' }}>{text}</strong>,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: '20px 0 0 0',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: '17px',
|
||||||
|
color: '#000',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
各家公司的风险统计情况
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: 1, overflow: 'auto', padding: '10px' }}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={tableData}
|
||||||
|
pagination={false}
|
||||||
|
scroll={scrollConfig}
|
||||||
|
size="small"
|
||||||
|
bordered
|
||||||
|
className={styles.certificateTable}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className={styles.trainingContentWrapper}>
|
||||||
|
<div className={styles.trainingGrid}>
|
||||||
|
{/* 第一行 - 表格 + 堆叠柱状图 */}
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>{this.renderRiskTable()}</div>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="stackBarChart" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 第二行 - 风险类别柱状图 */}
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="typeBarChart" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RiskControl;
|
||||||
@ -226,7 +226,7 @@ class TrainingContent extends React.Component {
|
|||||||
if (companyNames.length === 0) {
|
if (companyNames.length === 0) {
|
||||||
this.echartsInstances.chart2.setOption({
|
this.echartsInstances.chart2.setOption({
|
||||||
title: {
|
title: {
|
||||||
text: '本年度各公司培训分析',
|
text: '各公司月度培训分析',
|
||||||
x: 'center',
|
x: 'center',
|
||||||
y: '25%',
|
y: '25%',
|
||||||
textStyle: { fontSize: 16, color: '#999' },
|
textStyle: { fontSize: 16, color: '#999' },
|
||||||
@ -247,7 +247,7 @@ class TrainingContent extends React.Component {
|
|||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: '本年度各公司培训分析',
|
text: '各公司月度培训分析',
|
||||||
x: 'center',
|
x: 'center',
|
||||||
y: '5%',
|
y: '5%',
|
||||||
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
|
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
|
||||||
@ -693,7 +693,7 @@ class TrainingContent extends React.Component {
|
|||||||
color: '#000',
|
color: '#000',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
特种作业操作证统计
|
各公司证件统计
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1, overflow: 'auto' }}>{this.renderCertificateTable()}</div>
|
<div style={{ flex: 1, overflow: 'auto' }}>{this.renderCertificateTable()}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,14 +2,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'dva';
|
import { connect } from 'dva';
|
||||||
import { withRouter } from 'dva/router';
|
import { withRouter } from 'dva/router';
|
||||||
import { Icon, Row, Col } from 'antd';
|
import { Icon, Row, Col, message } from 'antd';
|
||||||
import styles from './fullinter.less';
|
import styles from './fullinter.less';
|
||||||
import logo from '../assets/layout/headerno-logo-new.png';
|
import logo from '../assets/layout/headerno-logo-new.png';
|
||||||
import debounce from 'lodash.debounce';
|
import debounce from 'lodash.debounce';
|
||||||
import storage from '../utils/storage';
|
import storage from '../utils/storage';
|
||||||
import { initFilter } from '../utils/common';
|
import { initFilter, extendRule } from '../utils/common';
|
||||||
import HomeContent from './FullOther/HomeContent';
|
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 getScale = () => {
|
||||||
const width = 1920,
|
const width = 1920,
|
||||||
@ -25,13 +29,9 @@ class FullScreen extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
nowDate: '',
|
nowDate: '',
|
||||||
riskTypeRate: [],
|
riskTypeRate: [],
|
||||||
riskTotal: [
|
riskTotal: [],
|
||||||
{ name: '年度重大隐患数', value: 1 },
|
jobTodayQty: [],
|
||||||
{ name: '年度一般隐患数量', value: 89 },
|
hiddenSummary: [],
|
||||||
{ name: '未整改隐患数量', value: 18 },
|
|
||||||
],
|
|
||||||
linkSum: [],
|
|
||||||
jobTodayTop3: [],
|
|
||||||
taskTop3: [],
|
taskTop3: [],
|
||||||
scale: getScale(),
|
scale: getScale(),
|
||||||
configBanner: ['首页', '风险管控', '隐患治理', '班组建设', '危险作业', '安全培训'],
|
configBanner: ['首页', '风险管控', '隐患治理', '班组建设', '危险作业', '安全培训'],
|
||||||
@ -39,14 +39,17 @@ class FullScreen extends React.Component {
|
|||||||
currentMediaIndex: 0,
|
currentMediaIndex: 0,
|
||||||
announcementList: [],
|
announcementList: [],
|
||||||
activeTab: '首页',
|
activeTab: '首页',
|
||||||
// 新增:培训数据
|
// 安全培训相关数据
|
||||||
trainingData: {
|
trainingData: {
|
||||||
listNAME: [],
|
listNAME: [],
|
||||||
YearCount: [],
|
YearCount: [],
|
||||||
MonthRecordCount: [],
|
MonthRecordCount: [],
|
||||||
MonthPersonCount: [],
|
MonthPersonCount: [],
|
||||||
|
listAnnourcement: [],
|
||||||
|
TITLE: '',
|
||||||
|
listVideoImg: [],
|
||||||
|
playSet: [],
|
||||||
},
|
},
|
||||||
// 新增:安全培训页面专用数据
|
|
||||||
trainingSubData: {
|
trainingSubData: {
|
||||||
listNAME: [],
|
listNAME: [],
|
||||||
YearCount: [],
|
YearCount: [],
|
||||||
@ -61,12 +64,29 @@ class FullScreen extends React.Component {
|
|||||||
},
|
},
|
||||||
trainingSubIDCard: [],
|
trainingSubIDCard: [],
|
||||||
trainingSubBSType: {},
|
trainingSubBSType: {},
|
||||||
companyData: [],
|
trainingCompanyData: [], // 安全培训专用公司数据
|
||||||
// 新增:当前选择的月份
|
selectedTrainingMonth: new Date().getMonth() + 1,
|
||||||
selectedMonth: new Date().getMonth() + 1, // 默认当前月份,1-12
|
selectedTrainingCompany: '',
|
||||||
selectedCompany: '',
|
|
||||||
|
// 隐患治理相关数据
|
||||||
|
hiddenSubData: {}, // 隐患治理数据
|
||||||
|
hiddenCompanyData: [], // 隐患治理专用公司数据
|
||||||
|
selectedHiddenCompany: '', // 隐患治理选中的公司
|
||||||
|
|
||||||
|
// 其他页面数据
|
||||||
|
riskSubData: {},
|
||||||
|
classSubData: {},
|
||||||
|
dangerSubData: {},
|
||||||
|
dangerCompanyData: [],
|
||||||
|
selectedDangerCompany: '',
|
||||||
|
|
||||||
|
announcementDetail: null,
|
||||||
|
announcementDetailLoading: false,
|
||||||
|
announcementModalVisible: false,
|
||||||
|
currentAnnouncement: null,
|
||||||
};
|
};
|
||||||
this.isUnmounted = false;
|
this.isUnmounted = false;
|
||||||
|
this.dataTimer = null; // 新增:用于数据定时请求
|
||||||
}
|
}
|
||||||
|
|
||||||
setScale = debounce(() => {
|
setScale = debounce(() => {
|
||||||
@ -81,15 +101,56 @@ class FullScreen extends React.Component {
|
|||||||
document.exitFullscreen?.();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
5 * 60 * 1000
|
||||||
|
); // 5分钟 = 300000毫秒
|
||||||
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.isUnmounted = false;
|
this.isUnmounted = false;
|
||||||
window.addEventListener('resize', this.setScale);
|
window.addEventListener('resize', this.setScale);
|
||||||
this.getHomeDataArray();
|
this.getHomeDataArray();
|
||||||
this.getYearPXData();
|
this.getYearPXData();
|
||||||
this.loadMediaFiles();
|
// 启动定时刷新
|
||||||
this.getAnnouncementData();
|
this.startDataRefreshTimer();
|
||||||
|
|
||||||
this.timer = setInterval(() => {
|
this.timer = setInterval(() => {
|
||||||
if (!this.isUnmounted) this.setState({ nowDate: this.getDate() });
|
if (!this.isUnmounted) this.setState({ nowDate: this.getDate() });
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -97,24 +158,56 @@ class FullScreen extends React.Component {
|
|||||||
document.addEventListener('fullscreenchange', this.handleFullscreenChange);
|
document.addEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnnouncementData = () => {
|
getAnnouncementDetail = (announcement) => {
|
||||||
this.setState({
|
return new Promise((resolve, reject) => {
|
||||||
announcementList: [
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
{ id: 1, title: '关于2024年安全生产月活动的通知', publishTime: '2024-06-01', url: '' },
|
const json = initFilter(orgId);
|
||||||
{ id: 2, title: '公司第三季度安全培训安排', publishTime: '2024-05-28', url: '' },
|
extendRule(json, 'ID', 1, announcement.ID);
|
||||||
{ id: 3, title: '关于开展安全隐患排查整治工作的通知', publishTime: '2024-05-20', url: '' },
|
json.Include = ['Nav_Orgs', 'Nav_Orgs.Nav_OrgSub', 'Nav_Files.Nav_ImgFile'];
|
||||||
{ id: 4, title: '安全生产标准化建设阶段性总结', publishTime: '2024-05-15', url: '' },
|
|
||||||
{ id: 5, title: '关于表彰2024年第一季度安全生产先进单位的决定', publishTime: '2024-05-10', url: '' },
|
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);
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
loadMediaFiles = () => {
|
handleAnnouncementClick = async (announcement) => {
|
||||||
|
try {
|
||||||
|
const detail = await this.getAnnouncementDetail(announcement);
|
||||||
|
this.setState({
|
||||||
|
currentAnnouncement: detail,
|
||||||
|
announcementModalVisible: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取公告详情失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleAnnouncementModalClose = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
mediaList: [
|
announcementModalVisible: false,
|
||||||
{ type: 'image', url: 'http://10.2.7.18:28028//WZ_Images/static/smyzw@2x.png' },
|
currentAnnouncement: null,
|
||||||
{ type: 'video', url: 'http://10.2.7.18:28028/WZ_Images/Img_JFSC/welcom/1.mp4' },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,17 +222,31 @@ class FullScreen extends React.Component {
|
|||||||
handleTabClick = (name) => {
|
handleTabClick = (name) => {
|
||||||
this.setState({ activeTab: name });
|
this.setState({ activeTab: name });
|
||||||
if (name === '首页') {
|
if (name === '首页') {
|
||||||
|
this.getHomeDataArray();
|
||||||
this.getYearPXData();
|
this.getYearPXData();
|
||||||
this.loadMediaFiles();
|
|
||||||
this.getAnnouncementData();
|
|
||||||
}
|
}
|
||||||
// 当点击安全培训tab时,获取对应数据
|
// 安全培训 - 参考原有逻辑
|
||||||
if (name === '安全培训') {
|
if (name === '安全培训') {
|
||||||
|
this.getTrainingCompanyData(); // 先获取公司列表
|
||||||
this.getHomeSESubYearData();
|
this.getHomeSESubYearData();
|
||||||
this.getHomeSESubYearMonthData();
|
this.getHomeSESubYearMonthData();
|
||||||
this.getHomeSESubIDCardData();
|
this.getHomeSESubIDCardData();
|
||||||
// this.getBSTypeMonthData();
|
}
|
||||||
this.getCompanyData();
|
// 隐患治理 - 参考安全培训的调用顺序
|
||||||
|
if (name === '隐患治理') {
|
||||||
|
this.getHiddenCompanyData(); // 先获取公司列表(与安全培训独立)
|
||||||
|
}
|
||||||
|
// 风险管控
|
||||||
|
if (name === '风险管控') {
|
||||||
|
this.getRiskSubData();
|
||||||
|
}
|
||||||
|
// 班组建设
|
||||||
|
if (name === '班组建设') {
|
||||||
|
this.getClassSubData();
|
||||||
|
}
|
||||||
|
// 危险作业
|
||||||
|
if (name === '危险作业') {
|
||||||
|
this.getDangerCompanyData();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,6 +258,9 @@ class FullScreen extends React.Component {
|
|||||||
this.isUnmounted = true;
|
this.isUnmounted = true;
|
||||||
window.removeEventListener('resize', this.setScale);
|
window.removeEventListener('resize', this.setScale);
|
||||||
if (this.timer) clearInterval(this.timer);
|
if (this.timer) clearInterval(this.timer);
|
||||||
|
// 清除数据刷新定时器
|
||||||
|
if (this.dataTimer) clearInterval(this.dataTimer);
|
||||||
|
|
||||||
document.removeEventListener('fullscreenchange', this.handleFullscreenChange);
|
document.removeEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,14 +289,15 @@ class FullScreen extends React.Component {
|
|||||||
if (ret && !this.isUnmounted) {
|
if (ret && !this.isUnmounted) {
|
||||||
this.setState({
|
this.setState({
|
||||||
riskTypeRate: ret.riskTypeRate || [],
|
riskTypeRate: ret.riskTypeRate || [],
|
||||||
linkSum: ret.linkSum || [],
|
jobTodayQty: ret.jobTodayQty || [],
|
||||||
jobTodayTop3: ret.jobTodayTop3 || [],
|
hiddenSummary: ret.hiddenSummary || [],
|
||||||
taskTop3: ret.taskTop3 || [],
|
taskTop3: ret.taskTop3 || [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getYearPXData = () => {
|
getYearPXData = () => {
|
||||||
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
const json = initFilter(orgId);
|
const json = initFilter(orgId);
|
||||||
@ -202,29 +313,63 @@ class FullScreen extends React.Component {
|
|||||||
YearCount: ret.YearCount || [],
|
YearCount: ret.YearCount || [],
|
||||||
MonthRecordCount: ret.MonthRecordCount || [],
|
MonthRecordCount: ret.MonthRecordCount || [],
|
||||||
MonthPersonCount: ret.MonthPersonCount || [],
|
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 = () => {
|
getHomeSESubYearData = () => {
|
||||||
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
const json = initFilter(orgId);
|
const json = initFilter(orgId);
|
||||||
const currentYear = new Date().getFullYear(); // 获取当前年份
|
const currentYear = new Date().getFullYear();
|
||||||
json.Parameter1 = currentYear.toString(); // 设置为当前年份
|
json.Parameter1 = currentYear.toString();
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'app/getDataByPost',
|
type: 'app/getDataByPost',
|
||||||
payload: json,
|
payload: json,
|
||||||
url: 'BI/BIHeadSE/HomeSESubYear',
|
url: 'BI/BIHeadSE/HomeSESubYear',
|
||||||
onComplete: (ret) => {
|
onComplete: (ret) => {
|
||||||
if (ret && !this.isUnmounted) {
|
if (ret && !this.isUnmounted) {
|
||||||
// ret 是数组格式: [{ CName: "邦泰", PCount: 0, RCount: 13 }, ...]
|
|
||||||
const listNAME = ret.map((item) => item.CName);
|
const listNAME = ret.map((item) => item.CName);
|
||||||
const MonthPersonCount = ret.map((item) => item.PCount); // 培训人次
|
const MonthPersonCount = ret.map((item) => item.PCount);
|
||||||
const MonthRecordCount = ret.map((item) => item.RCount); // 培训场次
|
const MonthRecordCount = ret.map((item) => item.RCount);
|
||||||
// YearCount 暂时用 PCount 的总和或保持为空数组
|
|
||||||
const YearCount = ret.map((item) => item.PCount);
|
const YearCount = ret.map((item) => item.PCount);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -239,38 +384,34 @@ class FullScreen extends React.Component {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// 新增:处理月份变化
|
|
||||||
handleMonthChange = (month) => {
|
handleTrainingMonthChange = (month) => {
|
||||||
this.setState({ selectedMonth: month }, () => {
|
this.setState({ selectedTrainingMonth: month }, () => {
|
||||||
// 重新获取月份数据
|
|
||||||
this.getHomeSESubYearMonthData();
|
this.getHomeSESubYearMonthData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
handleCompanyChange = (company) => {
|
|
||||||
console.log(company, '11111');
|
handleTrainingCompanyChange = (company) => {
|
||||||
this.setState({ selectedCompany: company }, () => {
|
this.setState({ selectedTrainingCompany: company }, () => {
|
||||||
// 重新获取月份数据
|
|
||||||
this.getBSTypeMonthData();
|
this.getBSTypeMonthData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// 获取安全培训页面数据
|
|
||||||
getHomeSESubYearMonthData = () => {
|
getHomeSESubYearMonthData = () => {
|
||||||
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
const json = initFilter(orgId);
|
const json = initFilter(orgId);
|
||||||
const currentYear = new Date().getFullYear(); // 获取当前年份
|
const currentYear = new Date().getFullYear();
|
||||||
json.Parameter1 = currentYear.toString(); // 设置为当前年份
|
json.Parameter1 = currentYear.toString();
|
||||||
json.Parameter2 = this.state.selectedMonth.toString(); // 月份
|
json.Parameter2 = this.state.selectedTrainingMonth.toString();
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'app/getDataByPost',
|
type: 'app/getDataByPost',
|
||||||
payload: json,
|
payload: json,
|
||||||
url: 'BI/BIHeadSE/HomeSESubYear',
|
url: 'BI/BIHeadSE/HomeSESubYear',
|
||||||
onComplete: (ret) => {
|
onComplete: (ret) => {
|
||||||
if (ret && !this.isUnmounted) {
|
if (ret && !this.isUnmounted) {
|
||||||
// ret 是数组格式: [{ CName: "邦泰", PCount: 0, RCount: 13 }, ...]
|
|
||||||
const listNAME = ret.map((item) => item.CName);
|
const listNAME = ret.map((item) => item.CName);
|
||||||
const MonthPersonCount = ret.map((item) => item.PCount); // 培训人次
|
const MonthPersonCount = ret.map((item) => item.PCount);
|
||||||
const MonthRecordCount = ret.map((item) => item.RCount); // 培训场次
|
const MonthRecordCount = ret.map((item) => item.RCount);
|
||||||
// YearCount 暂时用 PCount 的总和或保持为空数组
|
|
||||||
const YearCount = ret.map((item) => item.PCount);
|
const YearCount = ret.map((item) => item.PCount);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -285,6 +426,7 @@ class FullScreen extends React.Component {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getHomeSESubIDCardData = () => {
|
getHomeSESubIDCardData = () => {
|
||||||
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
const json = initFilter(orgId);
|
const json = initFilter(orgId);
|
||||||
@ -292,9 +434,9 @@ class FullScreen extends React.Component {
|
|||||||
const year = currentDate.getFullYear();
|
const year = currentDate.getFullYear();
|
||||||
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
|
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
|
||||||
const day = String(currentDate.getDate()).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({
|
this.props.dispatch({
|
||||||
type: 'app/getDataByPost',
|
type: 'app/getDataByPost',
|
||||||
payload: json,
|
payload: json,
|
||||||
@ -308,6 +450,7 @@ class FullScreen extends React.Component {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getBSTypeMonthData = () => {
|
getBSTypeMonthData = () => {
|
||||||
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
const json = initFilter(orgId);
|
const json = initFilter(orgId);
|
||||||
@ -315,11 +458,11 @@ class FullScreen extends React.Component {
|
|||||||
const year = currentDate.getFullYear();
|
const year = currentDate.getFullYear();
|
||||||
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
|
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
|
||||||
const day = String(currentDate.getDate()).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;
|
||||||
if (this.state.selectedCompany) {
|
if (this.state.selectedTrainingCompany) {
|
||||||
json.Parameter2 = this.state.selectedCompany;
|
json.Parameter2 = this.state.selectedTrainingCompany;
|
||||||
}
|
}
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'app/getDataByPost',
|
type: 'app/getDataByPost',
|
||||||
@ -334,7 +477,10 @@ class FullScreen extends React.Component {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
getCompanyData = () => {
|
|
||||||
|
// ==================== 隐患治理相关方法 ====================
|
||||||
|
// 获取隐患治理的公司列表(独立于安全培训)
|
||||||
|
getHiddenCompanyData = () => {
|
||||||
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
const json = initFilter(orgId);
|
const json = initFilter(orgId);
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
@ -343,18 +489,155 @@ class FullScreen extends React.Component {
|
|||||||
url: 'FM/Organization/OrderPaged',
|
url: 'FM/Organization/OrderPaged',
|
||||||
onComplete: (ret) => {
|
onComplete: (ret) => {
|
||||||
if (ret && !this.isUnmounted) {
|
if (ret && !this.isUnmounted) {
|
||||||
//加载页面默认为绑泰
|
// 默认选中邦泰公司
|
||||||
const defaultCompany = ret.find((company) => company.ID === '00500000-0000-0000-0000-000000000000');
|
const defaultCompany = ret.find((company) => company.ID === '00500000-0000-0000-0000-000000000000');
|
||||||
this.setState({
|
this.setState({
|
||||||
companyData: ret,
|
hiddenCompanyData: ret,
|
||||||
});
|
});
|
||||||
// 如果找到了默认公司,设置 selectedCompany
|
if (defaultCompany && !this.state.selectedHiddenCompany) {
|
||||||
if (defaultCompany && !this.state.selectedCompany) {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedCompany: defaultCompany.ID,
|
selectedHiddenCompany: defaultCompany.ID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.getBSTypeMonthData();
|
this.getHiddenSubData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 隐患治理公司筛选变化处理
|
||||||
|
handleHiddenCompanyChange = (company) => {
|
||||||
|
this.setState({ selectedHiddenCompany: company }, () => {
|
||||||
|
this.getHiddenSubData();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getHiddenSubData = () => {
|
||||||
|
// 以下是真实的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) {
|
||||||
|
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 = () => {
|
||||||
|
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 = () => {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
dangerSubData: filteredData,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -363,32 +646,61 @@ class FullScreen extends React.Component {
|
|||||||
renderOtherTabContent = () => {
|
renderOtherTabContent = () => {
|
||||||
const {
|
const {
|
||||||
activeTab,
|
activeTab,
|
||||||
trainingData,
|
|
||||||
trainingSubData,
|
trainingSubData,
|
||||||
trainingSubDataMonth,
|
|
||||||
selectedMonth,
|
|
||||||
selectedCompany,
|
|
||||||
trainingSubBSType,
|
trainingSubBSType,
|
||||||
companyData,
|
trainingCompanyData,
|
||||||
|
trainingSubDataMonth,
|
||||||
|
selectedTrainingMonth,
|
||||||
|
selectedTrainingCompany,
|
||||||
trainingSubIDCard,
|
trainingSubIDCard,
|
||||||
|
riskSubData,
|
||||||
|
classSubData,
|
||||||
|
hiddenSubData,
|
||||||
|
hiddenCompanyData,
|
||||||
|
selectedHiddenCompany,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
// 如果是安全培训,显示专门的培训页面
|
|
||||||
if (activeTab === '安全培训') {
|
if (activeTab === '安全培训') {
|
||||||
return (
|
return (
|
||||||
<TrainingContent
|
<TrainingContent
|
||||||
trainingData={trainingData}
|
|
||||||
trainingSubData={trainingSubData}
|
trainingSubData={trainingSubData}
|
||||||
trainingSubBSType={trainingSubBSType}
|
trainingSubBSType={trainingSubBSType}
|
||||||
companyData={companyData}
|
companyData={trainingCompanyData}
|
||||||
trainingSubDataMonth={trainingSubDataMonth}
|
trainingSubDataMonth={trainingSubDataMonth}
|
||||||
selectedMonth={selectedMonth}
|
selectedMonth={selectedTrainingMonth}
|
||||||
selectedCompany={selectedCompany}
|
selectedCompany={selectedTrainingCompany}
|
||||||
onMonthChange={this.handleMonthChange}
|
onMonthChange={this.handleTrainingMonthChange}
|
||||||
onCompanyChange={this.handleCompanyChange}
|
onCompanyChange={this.handleTrainingCompanyChange}
|
||||||
trainingSubIDCard={trainingSubIDCard}
|
trainingSubIDCard={trainingSubIDCard}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (activeTab === '风险管控') {
|
||||||
|
return <RiskControl riskSubData={riskSubData} />;
|
||||||
|
}
|
||||||
|
if (activeTab === '隐患治理') {
|
||||||
|
return (
|
||||||
|
<HiddenSolve
|
||||||
|
hiddenSubData={hiddenSubData}
|
||||||
|
companyData={hiddenCompanyData}
|
||||||
|
selectedCompany={selectedHiddenCompany}
|
||||||
|
onCompanyChange={this.handleHiddenCompanyChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (activeTab === '班组建设') {
|
||||||
|
return <ClassBuild classSubData={classSubData} />;
|
||||||
|
}
|
||||||
|
if (activeTab === '危险作业') {
|
||||||
|
return (
|
||||||
|
<DangerJob
|
||||||
|
dangerSubData={this.state.dangerSubData}
|
||||||
|
companyData={this.state.dangerCompanyData}
|
||||||
|
selectedCompany={this.state.selectedDangerCompany}
|
||||||
|
onCompanyChange={this.handleDangerCompanyChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className={styles.otherTabContent}>
|
<div className={styles.otherTabContent}>
|
||||||
<div style={{ textAlign: 'center', color: '#fff' }}>
|
<div style={{ textAlign: 'center', color: '#fff' }}>
|
||||||
@ -408,13 +720,16 @@ class FullScreen extends React.Component {
|
|||||||
activeTab,
|
activeTab,
|
||||||
riskTypeRate,
|
riskTypeRate,
|
||||||
riskTotal,
|
riskTotal,
|
||||||
jobTodayTop3,
|
hiddenSummary,
|
||||||
linkSum,
|
jobTodayQty,
|
||||||
taskTop3,
|
taskTop3,
|
||||||
mediaList,
|
mediaList,
|
||||||
announcementList,
|
announcementList,
|
||||||
currentMediaIndex,
|
currentMediaIndex,
|
||||||
trainingData,
|
trainingData,
|
||||||
|
announcementModalVisible,
|
||||||
|
currentAnnouncement,
|
||||||
|
announcementDetailLoading,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -470,15 +785,20 @@ class FullScreen extends React.Component {
|
|||||||
<HomeContent
|
<HomeContent
|
||||||
riskTypeRate={riskTypeRate}
|
riskTypeRate={riskTypeRate}
|
||||||
riskTotal={riskTotal}
|
riskTotal={riskTotal}
|
||||||
jobTodayTop3={jobTodayTop3}
|
hiddenSummary={hiddenSummary}
|
||||||
linkSum={linkSum}
|
jobTodayQty={jobTodayQty}
|
||||||
taskTop3={taskTop3}
|
taskTop3={taskTop3}
|
||||||
mediaList={mediaList}
|
mediaList={mediaList}
|
||||||
announcementList={announcementList}
|
announcementList={announcementList}
|
||||||
currentMediaIndex={currentMediaIndex}
|
currentMediaIndex={currentMediaIndex}
|
||||||
onCarouselChange={this.handleCarouselChange}
|
onCarouselChange={this.handleCarouselChange}
|
||||||
onDotClick={this.handleDotClick}
|
onDotClick={this.handleDotClick}
|
||||||
trainingData={trainingData} // 新增传递
|
trainingData={trainingData}
|
||||||
|
onAnnouncementClick={this.handleAnnouncementClick}
|
||||||
|
announcementModalVisible={announcementModalVisible}
|
||||||
|
currentAnnouncement={currentAnnouncement}
|
||||||
|
announcementDetailLoading={announcementDetailLoading}
|
||||||
|
onAnnouncementModalClose={this.handleAnnouncementModalClose}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
this.renderOtherTabContent()
|
this.renderOtherTabContent()
|
||||||
|
|||||||
@ -281,7 +281,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #1e2a3a;
|
background-color: #909399;
|
||||||
margin: 10px 20px;
|
margin: 10px 20px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -313,7 +313,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #1e2a3a;
|
background-color: #909399;
|
||||||
margin: 10px 20px;
|
margin: 10px 20px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -492,38 +492,59 @@
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 轮播图控件
|
|
||||||
.customArrowLeft,
|
.customArrowLeft,
|
||||||
.customArrowRight {
|
.customArrowRight {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
width: 40px;
|
width: 44px;
|
||||||
height: 60px;
|
height: 64px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
transition: all 0.3s ease;
|
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 {
|
&:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
transform: translateY(-50%) scale(1.1);
|
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 {
|
&:active {
|
||||||
transform: translateY(-50%) scale(0.95);
|
transform: translateY(-50%) scale(0.98);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.customArrowLeft {
|
.customArrowLeft {
|
||||||
left: 20px;
|
left: 16px;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customArrowRight {
|
.customArrowRight {
|
||||||
right: 20px;
|
right: 16px;
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customDots {
|
.customDots {
|
||||||
@ -691,19 +712,89 @@
|
|||||||
white-space: nowrap;
|
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 效果
|
// hover 效果
|
||||||
.ant-table-tbody > tr:hover > td {
|
.ant-table-tbody > tr:hover > td {
|
||||||
background-color: #e6f7ff;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
16
yarn.lock
16
yarn.lock
@ -10651,7 +10651,7 @@
|
|||||||
"recompose" "^0.27.1"
|
"recompose" "^0.27.1"
|
||||||
"shallowequal" "^1.0.2"
|
"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=="
|
"integrity" "sha512-cPGfgFfwi+VCZjk73buu14pYkYBR1b/SRMSYqkLDdhSEHnSwcuYTPu6/Bh6ZphJFIk80XLvbSe2azfcRzNF+Xg=="
|
||||||
"resolved" "https://registry.npmmirror.com/react-dom/-/react-dom-16.8.2.tgz"
|
"resolved" "https://registry.npmmirror.com/react-dom/-/react-dom-16.8.2.tgz"
|
||||||
"version" "16.8.2"
|
"version" "16.8.2"
|
||||||
@ -10789,9 +10789,19 @@
|
|||||||
"prop-types" "^15.6.1"
|
"prop-types" "^15.6.1"
|
||||||
"warning" "^4.0.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":
|
"react-slick@~0.25.2":
|
||||||
"integrity" "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw=="
|
"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"
|
"version" "0.25.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"classnames" "^2.2.5"
|
"classnames" "^2.2.5"
|
||||||
@ -10834,7 +10844,7 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/react-websocket/-/react-websocket-2.0.1.tgz"
|
"resolved" "https://registry.npmmirror.com/react-websocket/-/react-websocket-2.0.1.tgz"
|
||||||
"version" "2.0.1"
|
"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=="
|
"integrity" "sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw=="
|
||||||
"resolved" "https://registry.npmmirror.com/react/-/react-16.8.2.tgz"
|
"resolved" "https://registry.npmmirror.com/react/-/react-16.8.2.tgz"
|
||||||
"version" "16.8.2"
|
"version" "16.8.2"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user