安全培训
This commit is contained in:
parent
be924056a0
commit
32c619a778
@ -290,7 +290,7 @@ class HomeContent extends React.Component {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: seriesData,
|
data: seriesData,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
normal: { color: '#4285F4', borderRadius: 12 },
|
normal: { color: '#4285F4', barBorderRadius: 12 },
|
||||||
emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' },
|
emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' },
|
||||||
},
|
},
|
||||||
label: { show: true, position: 'top', textStyle: { color: '#000', fontSize: 12 }, formatter: '{c}' },
|
label: { show: true, position: 'top', textStyle: { color: '#000', fontSize: 12 }, formatter: '{c}' },
|
||||||
@ -323,60 +323,141 @@ class HomeContent extends React.Component {
|
|||||||
let backLogCharts = document.getElementById('backLogChart');
|
let backLogCharts = document.getElementById('backLogChart');
|
||||||
if (!backLogCharts) return;
|
if (!backLogCharts) return;
|
||||||
|
|
||||||
const taskData = this.props.taskTop3 || [];
|
// 使用培训数据
|
||||||
if (taskData.length === 0) return;
|
const { trainingData } = this.props;
|
||||||
|
const listNAME = trainingData?.listNAME || [];
|
||||||
const barTopColor = [
|
const monthRecordCount = trainingData?.MonthRecordCount || []; // 培训场次
|
||||||
['#75baf3', '#2177d5'],
|
const monthPersonCount = trainingData?.MonthPersonCount || []; // 培训人次
|
||||||
['#ffa94d', '#f76707'],
|
|
||||||
['#99ca6e', '#48a447'],
|
|
||||||
];
|
|
||||||
|
|
||||||
|
if (listNAME.length === 0) {
|
||||||
this.echartsInstances.backLogChart = echarts.init(backLogCharts);
|
this.echartsInstances.backLogChart = echarts.init(backLogCharts);
|
||||||
let xdata = this.transformDat(taskData, barTopColor, 2);
|
|
||||||
const legendColors = barTopColor.map((gradient) => gradient[0]);
|
|
||||||
|
|
||||||
this.echartsInstances.backLogChart.setOption({
|
this.echartsInstances.backLogChart.setOption({
|
||||||
title: {
|
title: {
|
||||||
text: '各事项排名前三名统计',
|
text: '当月工作培训统计数量',
|
||||||
|
x: 'center',
|
||||||
|
y: 'center',
|
||||||
|
textStyle: { fontSize: 16, color: '#999' },
|
||||||
|
},
|
||||||
|
graphic: {
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'middle',
|
||||||
|
style: {
|
||||||
|
text: '暂无培训数据',
|
||||||
|
fill: '#999',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.echartsInstances.backLogChart = echarts.init(backLogCharts);
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
text: '当月工作培训统计数量',
|
||||||
x: 'center',
|
x: 'center',
|
||||||
y: '5%',
|
y: '5%',
|
||||||
textStyle: { fontSize: 16, color: '#000' },
|
textStyle: { fontSize: 16, color: '#000' },
|
||||||
},
|
},
|
||||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
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: {
|
legend: {
|
||||||
data: xdata.legendData.map((name, index) => ({
|
data: ['培训人次', '培训场次'],
|
||||||
name: name,
|
orient: 'vertical', // 垂直排列
|
||||||
itemStyle: { color: legendColors[index], borderWidth: 0 },
|
right: 0, // 水平居中
|
||||||
textStyle: { color: '#000', fontSize: 14 },
|
top: 'middle', // 垂直居中
|
||||||
})),
|
itemGap: 16, // 图例项间隔
|
||||||
align: 'right',
|
|
||||||
top: 10,
|
|
||||||
right: 10,
|
|
||||||
itemGap: 16,
|
|
||||||
itemWidth: 18,
|
itemWidth: 18,
|
||||||
itemHeight: 12,
|
itemHeight: 12,
|
||||||
|
textStyle: { color: '#000', fontSize: 14 },
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '5%',
|
||||||
|
right: '5%', // 为右侧垂直图例留出空间
|
||||||
|
top: '18%',
|
||||||
|
bottom: '5%',
|
||||||
|
containLabel: true,
|
||||||
},
|
},
|
||||||
color: legendColors,
|
|
||||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
|
||||||
xAxis: [
|
xAxis: [
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: xdata.companyNames,
|
data: listNAME,
|
||||||
axisLine: { lineStyle: { color: '#3eb2e8' } },
|
axisLine: { show: false }, // 隐藏x轴线
|
||||||
axisLabel: { textStyle: { color: '#000' } },
|
axisTick: { show: false }, // 隐藏x轴刻度线
|
||||||
|
axisLabel: {
|
||||||
|
textStyle: { color: '#000' },
|
||||||
|
rotate: listNAME.length > 4 ? 15 : 0,
|
||||||
|
interval: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: 'value',
|
||||||
axisLine: { show: false },
|
show: true, // 显示y轴
|
||||||
axisLabel: { textStyle: { color: '#000' } },
|
axisLine: { show: false }, // 隐藏y轴线
|
||||||
splitLine: { lineStyle: { color: '#4784e8' } },
|
axisTick: { show: false }, // 隐藏y轴刻度线
|
||||||
|
axisLabel: {
|
||||||
|
show: true, // 显示数值标签
|
||||||
|
textStyle: { color: '#000' },
|
||||||
|
},
|
||||||
|
splitLine: { show: false }, // 隐藏横向网格线
|
||||||
|
name: '', // 不显示单位名称
|
||||||
|
nameTextStyle: { show: false }, // 隐藏单位文字
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series: xdata.series,
|
series: [
|
||||||
});
|
{
|
||||||
|
name: '培训人次',
|
||||||
|
type: 'bar',
|
||||||
|
data: monthPersonCount,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#4285F4', // 蓝色
|
||||||
|
barBorderRadius: 12, // 柱体圆角
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { color: '#4285F4', fontSize: 12 },
|
||||||
|
formatter: '{c}',
|
||||||
|
},
|
||||||
|
barWidth: '35%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '培训场次',
|
||||||
|
type: 'bar',
|
||||||
|
data: monthRecordCount,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#ffe066', // 黄色
|
||||||
|
barBorderRadius: 12, // 柱体圆角(上左、上右、下右、下左)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { color: '#ffe066', fontSize: 12 },
|
||||||
|
formatter: '{c}',
|
||||||
|
},
|
||||||
|
barWidth: '35%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.echartsInstances.backLogChart.setOption(option);
|
||||||
|
|
||||||
const resizeHandler = () => {
|
const resizeHandler = () => {
|
||||||
if (this.echartsInstances.backLogChart && !this.isUnmounted) {
|
if (this.echartsInstances.backLogChart && !this.isUnmounted) {
|
||||||
@ -451,7 +532,8 @@ class HomeContent extends React.Component {
|
|||||||
prevProps.riskTypeRate !== this.props.riskTypeRate ||
|
prevProps.riskTypeRate !== this.props.riskTypeRate ||
|
||||||
prevProps.jobTodayTop3 !== this.props.jobTodayTop3 ||
|
prevProps.jobTodayTop3 !== this.props.jobTodayTop3 ||
|
||||||
prevProps.linkSum !== this.props.linkSum ||
|
prevProps.linkSum !== this.props.linkSum ||
|
||||||
prevProps.taskTop3 !== this.props.taskTop3
|
prevProps.taskTop3 !== this.props.taskTop3 ||
|
||||||
|
prevProps.trainingData !== this.props.trainingData // 新增
|
||||||
) {
|
) {
|
||||||
this.disposeAllCharts();
|
this.disposeAllCharts();
|
||||||
this.initAllCharts();
|
this.initAllCharts();
|
||||||
@ -602,12 +684,16 @@ 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>
|
||||||
{riskTotal.map((item, index) => (
|
{this.props.trainingData?.listNAME?.map((name, index) => (
|
||||||
<div key={index} className={styles.trainingStatItem}>
|
<div key={index} className={styles.trainingStatItem}>
|
||||||
<div className={styles.trainingStatName}>{item.name}</div>
|
<div className={styles.trainingStatName}>{name}</div>
|
||||||
<div className={styles.trainingStatValue}>{item.value}</div>
|
<div className={styles.trainingStatValue}>{this.props.trainingData?.YearCount?.[index] || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{/* 如果 listNAME 为空,显示默认提示 */}
|
||||||
|
{(!this.props.trainingData?.listNAME || this.props.trainingData.listNAME.length === 0) && (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999', padding: '20px 0' }}>暂无培训数据</div>
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={16} className={styles.chartCol}>
|
<Col span={16} className={styles.chartCol}>
|
||||||
<div id="backLogChart" className={styles.chartContainer}></div>
|
<div id="backLogChart" className={styles.chartContainer}></div>
|
||||||
|
|||||||
623
src/layout/FullOther/TrainingContent.js
Normal file
623
src/layout/FullOther/TrainingContent.js
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
// TrainingContent.js - 安全培训页面组件
|
||||||
|
import React from 'react';
|
||||||
|
import { Row, Col, Select } from 'antd';
|
||||||
|
import styles from './../fullinter.less';
|
||||||
|
import echarts from 'echarts';
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
class TrainingContent extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.echartsInstances = {
|
||||||
|
chart1: null, // 各部门培训统计(柱状图)
|
||||||
|
chart2: null, // 各部门培训完成率
|
||||||
|
chart3: null, // 培训类型分布
|
||||||
|
chart4: 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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图表1: 各部门培训统计(柱状图)- 使用 trainingSubData 数据
|
||||||
|
renderChart1 = async () => {
|
||||||
|
if (this.isUnmounted) return;
|
||||||
|
const elementExists = await this.waitForElement('trainingChart1');
|
||||||
|
if (!elementExists || this.isUnmounted) return;
|
||||||
|
|
||||||
|
if (this.echartsInstances.chart1) {
|
||||||
|
this.echartsInstances.chart1.dispose();
|
||||||
|
this.echartsInstances.chart1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartDom = document.getElementById('trainingChart1');
|
||||||
|
if (!chartDom) return;
|
||||||
|
|
||||||
|
this.echartsInstances.chart1 = echarts.init(chartDom);
|
||||||
|
|
||||||
|
// 使用 props 传入的 trainingSubData 数据(来自 getHomeSESubYear 接口)
|
||||||
|
const { trainingSubData } = this.props;
|
||||||
|
const companyNames = trainingSubData?.listNAME || []; // CName 数组
|
||||||
|
const personCounts = trainingSubData?.MonthPersonCount || []; // PCount 数组(培训人次)
|
||||||
|
const recordCounts = trainingSubData?.MonthRecordCount || []; // RCount 数组(培训场次)
|
||||||
|
|
||||||
|
// 如果没有数据,显示暂无数据提示
|
||||||
|
if (companyNames.length === 0) {
|
||||||
|
this.echartsInstances.chart1.setOption({
|
||||||
|
title: {
|
||||||
|
text: '本年度各公司培训分析',
|
||||||
|
x: 'center',
|
||||||
|
y: 'center',
|
||||||
|
textStyle: { fontSize: 16, color: '#999' },
|
||||||
|
},
|
||||||
|
graphic: {
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'middle',
|
||||||
|
style: {
|
||||||
|
text: '暂无培训数据',
|
||||||
|
fill: '#999',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const unit = param.seriesName === '培训人次' ? '人次' : '场次';
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}${unit}<br/>`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['培训人次', '培训场次'],
|
||||||
|
right: '3%', // 靠右
|
||||||
|
top: '5%', // 垂直居中
|
||||||
|
itemGap: 16,
|
||||||
|
itemWidth: 18,
|
||||||
|
itemHeight: 12,
|
||||||
|
textStyle: { color: '#000', fontSize: 14 },
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '5%',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
show: true,
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
textStyle: { color: '#000' },
|
||||||
|
},
|
||||||
|
splitLine: { show: false },
|
||||||
|
name: '',
|
||||||
|
nameTextStyle: { show: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '培训人次',
|
||||||
|
type: 'bar',
|
||||||
|
data: personCounts,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#4285F4', // 蓝色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { color: '#4285F4', fontSize: 12 },
|
||||||
|
formatter: (params) => `${params.value}`,
|
||||||
|
},
|
||||||
|
barWidth: '35%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '培训场次',
|
||||||
|
type: 'bar',
|
||||||
|
data: recordCounts,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#ffe066', // 黄色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { color: '#d4a000', fontSize: 12 },
|
||||||
|
formatter: (params) => `${params.value}`,
|
||||||
|
},
|
||||||
|
barWidth: '35%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.echartsInstances.chart1.setOption(option);
|
||||||
|
this.setupResizeHandler('chart1', this.renderChart1);
|
||||||
|
};
|
||||||
|
// 生成月份选项(1-12月)
|
||||||
|
getMonthOptions = () => {
|
||||||
|
const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
|
||||||
|
return months.map((month, index) => (
|
||||||
|
<Option key={index + 1} value={index + 1}>
|
||||||
|
{month}
|
||||||
|
</Option>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图表2: 各部门培训完成率(横向柱状图)
|
||||||
|
renderChart2 = async () => {
|
||||||
|
if (this.isUnmounted) return;
|
||||||
|
const elementExists = await this.waitForElement('trainingChart2');
|
||||||
|
if (!elementExists || this.isUnmounted) return;
|
||||||
|
|
||||||
|
if (this.echartsInstances.chart2) {
|
||||||
|
this.echartsInstances.chart2.dispose();
|
||||||
|
this.echartsInstances.chart2 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartDom = document.getElementById('trainingChart2');
|
||||||
|
if (!chartDom) return;
|
||||||
|
|
||||||
|
this.echartsInstances.chart2 = echarts.init(chartDom);
|
||||||
|
|
||||||
|
// 使用 props 传入的 trainingSubData 数据(来自 getHomeSESubYear 接口)
|
||||||
|
const { trainingSubDataMonth } = this.props;
|
||||||
|
const companyNames = trainingSubDataMonth?.listNAME || []; // CName 数组
|
||||||
|
const personCounts = trainingSubDataMonth?.MonthPersonCount || []; // PCount 数组(培训人次)
|
||||||
|
const recordCounts = trainingSubDataMonth?.MonthRecordCount || []; // RCount 数组(培训场次)
|
||||||
|
|
||||||
|
// 如果没有数据,显示暂无数据提示
|
||||||
|
if (companyNames.length === 0) {
|
||||||
|
this.echartsInstances.chart2.setOption({
|
||||||
|
title: {
|
||||||
|
text: '本年度各公司培训分析',
|
||||||
|
x: 'center',
|
||||||
|
y: 'center',
|
||||||
|
textStyle: { fontSize: 16, color: '#999' },
|
||||||
|
},
|
||||||
|
graphic: {
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'middle',
|
||||||
|
style: {
|
||||||
|
text: '暂无培训数据',
|
||||||
|
fill: '#999',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const unit = param.seriesName === '培训人次' ? '人次' : '场次';
|
||||||
|
result += `${param.marker}${param.seriesName}: ${param.value}${unit}<br/>`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['培训人次', '培训场次'],
|
||||||
|
right: '3%', // 靠右
|
||||||
|
top: '5%', // 垂直居中
|
||||||
|
itemGap: 16,
|
||||||
|
itemWidth: 18,
|
||||||
|
itemHeight: 12,
|
||||||
|
textStyle: { color: '#000', fontSize: 14 },
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '5%',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
show: true,
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
textStyle: { color: '#000' },
|
||||||
|
},
|
||||||
|
splitLine: { show: false },
|
||||||
|
name: '',
|
||||||
|
nameTextStyle: { show: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '培训人次',
|
||||||
|
type: 'bar',
|
||||||
|
data: personCounts,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#4285F4', // 蓝色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { color: '#4285F4', fontSize: 12 },
|
||||||
|
formatter: (params) => `${params.value}`,
|
||||||
|
},
|
||||||
|
barWidth: '35%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '培训场次',
|
||||||
|
type: 'bar',
|
||||||
|
data: recordCounts,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: '#ffe066', // 黄色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { color: '#d4a000', fontSize: 12 },
|
||||||
|
formatter: (params) => `${params.value}`,
|
||||||
|
},
|
||||||
|
barWidth: '35%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.echartsInstances.chart2.setOption(option);
|
||||||
|
this.setupResizeHandler('chart2', this.renderChart2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图表3: 培训类型分布(饼图)
|
||||||
|
renderChart3 = async () => {
|
||||||
|
if (this.isUnmounted) return;
|
||||||
|
const elementExists = await this.waitForElement('trainingChart3');
|
||||||
|
if (!elementExists || this.isUnmounted) return;
|
||||||
|
|
||||||
|
if (this.echartsInstances.chart3) {
|
||||||
|
this.echartsInstances.chart3.dispose();
|
||||||
|
this.echartsInstances.chart3 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartDom = document.getElementById('trainingChart3');
|
||||||
|
if (!chartDom) return;
|
||||||
|
|
||||||
|
this.echartsInstances.chart3 = echarts.init(chartDom);
|
||||||
|
|
||||||
|
// 培训类型分布数据
|
||||||
|
const trainingTypes = [
|
||||||
|
{ name: '安全法规培训', value: 35 },
|
||||||
|
{ name: '操作规程培训', value: 28 },
|
||||||
|
{ name: '应急演练培训', value: 20 },
|
||||||
|
{ name: '职业健康培训', value: 12 },
|
||||||
|
{ name: '其他培训', value: 5 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
text: '培训类型分布',
|
||||||
|
x: 'center',
|
||||||
|
y: '5%',
|
||||||
|
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function (params) {
|
||||||
|
return `${params.name}: ${params.value}场 (${params.percent}%)`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: '5%',
|
||||||
|
top: 'middle',
|
||||||
|
itemGap: 12,
|
||||||
|
itemWidth: 14,
|
||||||
|
itemHeight: 10,
|
||||||
|
textStyle: { color: '#333', fontSize: 11 },
|
||||||
|
formatter: function (name) {
|
||||||
|
const item = trainingTypes.find((t) => t.name === name);
|
||||||
|
return `${name} ${item?.value || 0}场`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
color: ['#4285F4', '#66bb6a', '#ffa94d', '#ab47bc', '#ef5350'],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '培训场次',
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['45%', '70%'],
|
||||||
|
center: ['40%', '55%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'outside',
|
||||||
|
formatter: '{b}: {d}%',
|
||||||
|
textStyle: { fontSize: 11, color: '#333' },
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: { show: true, fontSize: 14, fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
labelLine: { length: 8, length2: 8, smooth: true },
|
||||||
|
data: trainingTypes,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.echartsInstances.chart3.setOption(option);
|
||||||
|
this.setupResizeHandler('chart3', this.renderChart3);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图表4: 月度培训对比(分组柱状图)
|
||||||
|
renderChart4 = async () => {
|
||||||
|
if (this.isUnmounted) return;
|
||||||
|
const elementExists = await this.waitForElement('trainingChart4');
|
||||||
|
if (!elementExists || this.isUnmounted) return;
|
||||||
|
|
||||||
|
if (this.echartsInstances.chart4) {
|
||||||
|
this.echartsInstances.chart4.dispose();
|
||||||
|
this.echartsInstances.chart4 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartDom = document.getElementById('trainingChart4');
|
||||||
|
if (!chartDom) return;
|
||||||
|
|
||||||
|
this.echartsInstances.chart4 = echarts.init(chartDom);
|
||||||
|
|
||||||
|
// 近6个月各部门培训场次对比
|
||||||
|
const months = ['1月', '2月', '3月', '4月', '5月', '6月'];
|
||||||
|
const departments = ['安全部', '生产部', '技术部'];
|
||||||
|
|
||||||
|
const seriesData = [
|
||||||
|
{ name: '安全部', data: [5, 6, 7, 8, 7, 9], color: '#4285F4' },
|
||||||
|
{ name: '生产部', data: [8, 9, 10, 11, 12, 13], color: '#66bb6a' },
|
||||||
|
{ name: '技术部', data: [4, 5, 6, 6, 7, 8], color: '#ffa94d' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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: seriesData.map((s) => s.name),
|
||||||
|
orient: 'horizontal',
|
||||||
|
left: 'center',
|
||||||
|
top: '15%',
|
||||||
|
itemGap: 20,
|
||||||
|
itemWidth: 18,
|
||||||
|
itemHeight: 12,
|
||||||
|
textStyle: { color: '#333', fontSize: 12 },
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '8%',
|
||||||
|
right: '5%',
|
||||||
|
top: '25%',
|
||||||
|
bottom: '8%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: months,
|
||||||
|
axisLine: { lineStyle: { color: '#666' } },
|
||||||
|
axisLabel: { textStyle: { color: '#333' } },
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: '培训场次',
|
||||||
|
axisLabel: { textStyle: { color: '#666' } },
|
||||||
|
splitLine: { lineStyle: { color: '#e0e0e0' } },
|
||||||
|
},
|
||||||
|
series: seriesData.map((s) => ({
|
||||||
|
name: s.name,
|
||||||
|
type: 'bar',
|
||||||
|
data: s.data,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
color: s.color,
|
||||||
|
barBorderRadius: [4, 4, 0, 0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
textStyle: { fontSize: 10 },
|
||||||
|
formatter: '{c}',
|
||||||
|
},
|
||||||
|
barWidth: '25%',
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.echartsInstances.chart4.setOption(option);
|
||||||
|
this.setupResizeHandler('chart4', this.renderChart4);
|
||||||
|
};
|
||||||
|
|
||||||
|
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.renderChart1();
|
||||||
|
this.renderChart2();
|
||||||
|
this.renderChart3();
|
||||||
|
this.renderChart4();
|
||||||
|
}, 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) {
|
||||||
|
// 当 trainingSubData 或 trainingSubDataMonth 变化时重新渲染图表
|
||||||
|
if (
|
||||||
|
prevProps.trainingSubData !== this.props.trainingSubData ||
|
||||||
|
prevProps.trainingSubDataMonth !== this.props.trainingSubDataMonth ||
|
||||||
|
prevProps.selectedMonth !== this.props.selectedMonth
|
||||||
|
) {
|
||||||
|
// this.renderChart1();
|
||||||
|
this.renderChart2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.isUnmounted = true;
|
||||||
|
this.disposeAllCharts();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { selectedMonth = 1, onMonthChange } = this.props;
|
||||||
|
return (
|
||||||
|
<div className={styles.trainingContentWrapper}>
|
||||||
|
<div className={styles.trainingGrid}>
|
||||||
|
{/* 第一行 - 图表1和图表2 */}
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="trainingChart1" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
{/* 添加月份选择器 */}
|
||||||
|
<div className={styles.monthSelectorWrapper}>
|
||||||
|
<span className={styles.monthSelectorLabel}>选择月份:</span>
|
||||||
|
<Select
|
||||||
|
value={selectedMonth}
|
||||||
|
onChange={onMonthChange}
|
||||||
|
style={{ width: 120 }}
|
||||||
|
className={styles.monthSelect}
|
||||||
|
>
|
||||||
|
{this.getMonthOptions()}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div id="trainingChart2" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* 第二行 - 图表3和图表4 */}
|
||||||
|
<div className={styles.trainingRow}>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="trainingChart3" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.trainingCard}>
|
||||||
|
<div id="trainingChart4" className={styles.trainingChartContainer}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TrainingContent;
|
||||||
@ -9,6 +9,7 @@ import debounce from 'lodash.debounce';
|
|||||||
import storage from '../utils/storage';
|
import storage from '../utils/storage';
|
||||||
import { initFilter } from '../utils/common';
|
import { initFilter } from '../utils/common';
|
||||||
import HomeContent from './FullOther/HomeContent';
|
import HomeContent from './FullOther/HomeContent';
|
||||||
|
import TrainingContent from './FullOther/TrainingContent'; // 新增导入
|
||||||
|
|
||||||
const getScale = () => {
|
const getScale = () => {
|
||||||
const width = 1920,
|
const width = 1920,
|
||||||
@ -38,6 +39,28 @@ class FullScreen extends React.Component {
|
|||||||
currentMediaIndex: 0,
|
currentMediaIndex: 0,
|
||||||
announcementList: [],
|
announcementList: [],
|
||||||
activeTab: '首页',
|
activeTab: '首页',
|
||||||
|
// 新增:培训数据
|
||||||
|
trainingData: {
|
||||||
|
listNAME: [],
|
||||||
|
YearCount: [],
|
||||||
|
MonthRecordCount: [],
|
||||||
|
MonthPersonCount: [],
|
||||||
|
},
|
||||||
|
// 新增:安全培训页面专用数据
|
||||||
|
trainingSubData: {
|
||||||
|
listNAME: [],
|
||||||
|
YearCount: [],
|
||||||
|
MonthRecordCount: [],
|
||||||
|
MonthPersonCount: [],
|
||||||
|
},
|
||||||
|
trainingSubDataMonth: {
|
||||||
|
listNAME: [],
|
||||||
|
YearCount: [],
|
||||||
|
MonthRecordCount: [],
|
||||||
|
MonthPersonCount: [],
|
||||||
|
},
|
||||||
|
// 新增:当前选择的月份
|
||||||
|
selectedMonth: new Date().getMonth() + 1, // 默认当前月份,1-12
|
||||||
};
|
};
|
||||||
this.isUnmounted = false;
|
this.isUnmounted = false;
|
||||||
}
|
}
|
||||||
@ -58,7 +81,8 @@ class FullScreen extends React.Component {
|
|||||||
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.loadMediaFiles();
|
this.loadMediaFiles();
|
||||||
this.getAnnouncementData();
|
this.getAnnouncementData();
|
||||||
|
|
||||||
@ -100,6 +124,11 @@ class FullScreen extends React.Component {
|
|||||||
|
|
||||||
handleTabClick = (name) => {
|
handleTabClick = (name) => {
|
||||||
this.setState({ activeTab: name });
|
this.setState({ activeTab: name });
|
||||||
|
// 当点击安全培训tab时,获取对应数据
|
||||||
|
if (name === '安全培训') {
|
||||||
|
this.getHomeSESubYearData();
|
||||||
|
this.getHomeSESubYearMonthData();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleFullscreenChange = () => {
|
handleFullscreenChange = () => {
|
||||||
@ -146,9 +175,112 @@ class FullScreen extends React.Component {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
getYearPXData = () => {
|
||||||
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
|
const json = initFilter(orgId);
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'app/getDataByPost',
|
||||||
|
payload: json,
|
||||||
|
url: 'BI/BIHeadSE/SEtrInfo',
|
||||||
|
onComplete: (ret) => {
|
||||||
|
if (ret && !this.isUnmounted) {
|
||||||
|
this.setState({
|
||||||
|
trainingData: {
|
||||||
|
listNAME: ret.listNAME || [],
|
||||||
|
YearCount: ret.YearCount || [],
|
||||||
|
MonthRecordCount: ret.MonthRecordCount || [],
|
||||||
|
MonthPersonCount: ret.MonthPersonCount || [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 获取安全培训页面数据
|
||||||
|
getHomeSESubYearData = () => {
|
||||||
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
|
const json = initFilter(orgId);
|
||||||
|
const currentYear = new Date().getFullYear(); // 获取当前年份
|
||||||
|
json.Parameter1 = currentYear.toString(); // 设置为当前年份
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'app/getDataByPost',
|
||||||
|
payload: json,
|
||||||
|
url: 'BI/BIHeadSE/HomeSESubYear',
|
||||||
|
onComplete: (ret) => {
|
||||||
|
if (ret && !this.isUnmounted) {
|
||||||
|
// ret 是数组格式: [{ CName: "邦泰", PCount: 0, RCount: 13 }, ...]
|
||||||
|
const listNAME = ret.map((item) => item.CName);
|
||||||
|
const MonthPersonCount = ret.map((item) => item.PCount); // 培训人次
|
||||||
|
const MonthRecordCount = ret.map((item) => item.RCount); // 培训场次
|
||||||
|
// YearCount 暂时用 PCount 的总和或保持为空数组
|
||||||
|
const YearCount = ret.map((item) => item.PCount);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
trainingSubData: {
|
||||||
|
listNAME: listNAME,
|
||||||
|
YearCount: YearCount,
|
||||||
|
MonthRecordCount: MonthRecordCount,
|
||||||
|
MonthPersonCount: MonthPersonCount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 新增:处理月份变化
|
||||||
|
handleMonthChange = (month) => {
|
||||||
|
this.setState({ selectedMonth: month }, () => {
|
||||||
|
// 重新获取月份数据
|
||||||
|
this.getHomeSESubYearMonthData();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 获取安全培训页面数据
|
||||||
|
getHomeSESubYearMonthData = () => {
|
||||||
|
const orgId = storage('lacal').getItem('webOrgId')?.val;
|
||||||
|
const json = initFilter(orgId);
|
||||||
|
const currentYear = new Date().getFullYear(); // 获取当前年份
|
||||||
|
json.Parameter1 = currentYear.toString(); // 设置为当前年份
|
||||||
|
json.Parameter2 = this.state.selectedMonth.toString(); // 月份
|
||||||
|
this.props.dispatch({
|
||||||
|
type: 'app/getDataByPost',
|
||||||
|
payload: json,
|
||||||
|
url: 'BI/BIHeadSE/HomeSESubYear',
|
||||||
|
onComplete: (ret) => {
|
||||||
|
if (ret && !this.isUnmounted) {
|
||||||
|
// ret 是数组格式: [{ CName: "邦泰", PCount: 0, RCount: 13 }, ...]
|
||||||
|
const listNAME = ret.map((item) => item.CName);
|
||||||
|
const MonthPersonCount = ret.map((item) => item.PCount); // 培训人次
|
||||||
|
const MonthRecordCount = ret.map((item) => item.RCount); // 培训场次
|
||||||
|
// YearCount 暂时用 PCount 的总和或保持为空数组
|
||||||
|
const YearCount = ret.map((item) => item.PCount);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
trainingSubDataMonth: {
|
||||||
|
listNAME: listNAME,
|
||||||
|
YearCount: YearCount,
|
||||||
|
MonthRecordCount: MonthRecordCount,
|
||||||
|
MonthPersonCount: MonthPersonCount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
renderOtherTabContent = () => {
|
renderOtherTabContent = () => {
|
||||||
const { activeTab } = this.state;
|
const { activeTab, trainingData, trainingSubData, trainingSubDataMonth, selectedMonth } = this.state;
|
||||||
|
// 如果是安全培训,显示专门的培训页面
|
||||||
|
if (activeTab === '安全培训') {
|
||||||
|
return (
|
||||||
|
<TrainingContent
|
||||||
|
trainingData={trainingData}
|
||||||
|
trainingSubData={trainingSubData}
|
||||||
|
trainingSubDataMonth={trainingSubDataMonth}
|
||||||
|
selectedMonth={selectedMonth}
|
||||||
|
onMonthChange={this.handleMonthChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className={styles.otherTabContent}>
|
<div className={styles.otherTabContent}>
|
||||||
<div style={{ textAlign: 'center', color: '#fff' }}>
|
<div style={{ textAlign: 'center', color: '#fff' }}>
|
||||||
@ -174,6 +306,7 @@ class FullScreen extends React.Component {
|
|||||||
mediaList,
|
mediaList,
|
||||||
announcementList,
|
announcementList,
|
||||||
currentMediaIndex,
|
currentMediaIndex,
|
||||||
|
trainingData,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -237,6 +370,7 @@ class FullScreen extends React.Component {
|
|||||||
currentMediaIndex={currentMediaIndex}
|
currentMediaIndex={currentMediaIndex}
|
||||||
onCarouselChange={this.handleCarouselChange}
|
onCarouselChange={this.handleCarouselChange}
|
||||||
onDotClick={this.handleDotClick}
|
onDotClick={this.handleDotClick}
|
||||||
|
trainingData={trainingData} // 新增传递
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
this.renderOtherTabContent()
|
this.renderOtherTabContent()
|
||||||
|
|||||||
@ -590,3 +590,86 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// fullinter.less - 在文件末尾添加以下样式
|
||||||
|
|
||||||
|
// ========== TrainingContent 组件样式 ==========
|
||||||
|
.trainingContentWrapper {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 10px 10px 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trainingGrid {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trainingRow {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trainingCard {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0; // 防止内容溢出
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
transition: box-shadow 0.3s ease;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.trainingChartContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
// 月份选择器样式
|
||||||
|
.monthSelectorWrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: 20px;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthSelectorLabel {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monthSelect {
|
||||||
|
.ant-select-selector {
|
||||||
|
border-radius: 16px !important;
|
||||||
|
border-color: #4285f4 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #66bb6a !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user