This commit is contained in:
何美荣 2026-05-25 11:08:53 +08:00
commit 05692eba30
16 changed files with 1086 additions and 163 deletions

BIN
src/assets/layout/dt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
src/assets/layout/dt@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

BIN
src/assets/layout/kqmj.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
src/assets/layout/scgm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -64,6 +64,7 @@ class SE018EditPage extends React.Component {
extendInclude(json, 'Nav_Users.Nav_Files.Nav_ImgFile'); extendInclude(json, 'Nav_Users.Nav_Files.Nav_ImgFile');
extendInclude(json, 'Nav_Files.Nav_ImgFile'); extendInclude(json, 'Nav_Files.Nav_ImgFile');
extendInclude(json, 'Nav_Papers'); extendInclude(json, 'Nav_Papers');
extendInclude(json, 'Nav_UserApro');
json.IgnoreDataRule = true; json.IgnoreDataRule = true;
this.props.dispatch({ this.props.dispatch({
type: 'app/getDataByPost', type: 'app/getDataByPost',
@ -79,7 +80,8 @@ class SE018EditPage extends React.Component {
updateData(data) { updateData(data) {
let isApprove = false; let isApprove = false;
// if (data.Nav_Notify.Nav_TrainCheckType.NAME == "笔试") { // if (data.Nav_Notify.Nav_TrainCheckType.NAME == "笔试") {
if (data.Nav_Notify.CHECKTYPE == 51 || data.Nav_Notify.CHECKTYPE == 56 || data.Nav_Notify.CHECKTYPE == 81) { if ((data.Nav_Notify.CHECKTYPE == 51 || data.Nav_Notify.CHECKTYPE == 56 || data.Nav_Notify.CHECKTYPE == 81) && data.STATUS > 1) {
//data.STATUS > 1 签到的时候 看到分数为0 怪怪的
this.state.isExam = true; this.state.isExam = true;
this.state.isShow = "table-cell"; this.state.isShow = "table-cell";
} }
@ -88,8 +90,11 @@ class SE018EditPage extends React.Component {
data.Nav_Users = data.Nav_Users.filter(item => item.STATUS == 0); data.Nav_Users = data.Nav_Users.filter(item => item.STATUS == 0);
var passRateThis = 0 var passRateThis = 0
var userAll = data.Nav_Users.filter(e => e.STATUS == 0 && e.ENABLE_STATUS == 0 && e.EXAMINATION_RESULTS != null) // var userAll = data.Nav_Users.filter(e => e.STATUS == 0 && e.ENABLE_STATUS == 0 && e.EXAMINATION_RESULTS != null)
var userPass = data.Nav_Users.filter(e => e.STATUS == 0 && e.ENABLE_STATUS == 0 && e.EXAMINATION_RESULTS != null && e.EXAMINATION_RESULTS == 1) // var userPass = data.Nav_Users.filter(e => e.STATUS == 0 && e.ENABLE_STATUS == 0 && e.EXAMINATION_RESULTS != null && e.EXAMINATION_RESULTS == 1)
var userAll = data.Nav_Users.filter(e => e.OK)
var userPass = data.Nav_Users.filter(e => e.OK && e.EXAMINATION_RESULTS == 1)
if (userAll && userAll.length > 0 && userPass && userPass.length > 0) { if (userAll && userAll.length > 0 && userPass && userPass.length > 0) {
passRateThis = (userPass.length / userAll.length).toFixed(4) * 100; passRateThis = (userPass.length / userAll.length).toFixed(4) * 100;
} }
@ -226,7 +231,7 @@ class SE018EditPage extends React.Component {
{ {
this.state.isView && <> this.state.isView && <>
<ReactToPrint <ReactToPrint
trigger={() => <Button type={'primary'} icon={'printer'} >打印</Button>} trigger={() => <Button type={'primary'} style={{ marginLeft: '8px' }} icon={'printer'} >打印</Button>}
content={() => this.componentRef} content={() => this.componentRef}
/> />
<Button style={{ marginLeft: '8px' }} onClick={() => this.onTableBtnExport()} icon="export" >导出</Button> <Button style={{ marginLeft: '8px' }} onClick={() => this.onTableBtnExport()} icon="export" >导出</Button>
@ -337,12 +342,13 @@ class SE018EditPage extends React.Component {
</td> </td>
</tr> </tr>
<tr> <tr>
<td colSpan={4} rowSpan={1} className={styles.fontBold}>审阅意见</td> <td colSpan={4} className={styles.fontBold}>审阅意见</td>
<td colSpan={20} rowSpan={1} > {
{ data && data.STATUS > 2 && data.DT_APPROVE ? <td colSpan={20}>
data && data.STATUS > 2 && '已阅' <p>审阅人{showUserSign(data.Nav_UserApro, config.picServerHost)}</p>
} <p>审阅时间{data.DT_APPROVE}</p>
</td> </td> : <td colSpan={20} ></td>
}
</tr> </tr>
<tr> <tr>
<td colSpan={4} rowSpan={1} className={styles.fontBold}>合格率</td> <td colSpan={4} rowSpan={1} className={styles.fontBold}>合格率</td>
@ -381,12 +387,12 @@ class SE018EditPage extends React.Component {
{this.state.isExam && <td colSpan={4} rowSpan={1}>{it?.SCORE}</td>} {this.state.isExam && <td colSpan={4} rowSpan={1}>{it?.SCORE}</td>}
{/* if (data.Nav_Notify.CHECKTYPE == 51 || data.Nav_Notify.CHECKTYPE == 56 || data.Nav_Notify.CHECKTYPE == 81) { */} {/* if (data.Nav_Notify.CHECKTYPE == 51 || data.Nav_Notify.CHECKTYPE == 56 || data.Nav_Notify.CHECKTYPE == 81) { */}
{/* 线上考核 51 看试卷 56 81 看附件 */} {/* 线上考核 51 看试卷 56 81 看附件 showUserSign(user, imgHost, width, height, extShowTimeText) */}
{ {
!signable && ( !signable && (
this.state.isShow && data.Nav_Notify.CHECKTYPE ? ( this.state.isShow && data.Nav_Notify.CHECKTYPE ? (
data.Nav_Notify.CHECKTYPE == 51 ? <td colSpan={4} rowSpan={1} ><div onClick={() => this.showDetailModal(it.RECORD_ID, it.USER_ID)}><Icon type="eye" style={{ color: "#005b9b", cursor: "pointer" }} /></div></td> data.Nav_Notify.CHECKTYPE == 51 ? <td colSpan={4} rowSpan={1} style={{ display: this.state.isShow }} ><div onClick={() => this.showDetailModal(it.RECORD_ID, it.USER_ID)}><Icon type="eye" style={{ color: "#005b9b", cursor: "pointer" }} /></div></td>
: <td colSpan={4} rowSpan={1} > {showFiles(it?.Nav_Files, config.picServerHost, this, false)}</td> : <td colSpan={4} rowSpan={1} style={{ display: this.state.isShow }} > {showFiles(it?.Nav_Files, config.picServerHost, this, false)}</td>
) )
: <td colSpan={4} rowSpan={1} style={{ display: this.state.isShow }}><div onClick={() => this.showDetailModal(it.RECORD_ID, it.USER_ID)}><Icon type="eye" style={{ color: "#005b9b", cursor: "pointer" }} /></div></td> : <td colSpan={4} rowSpan={1} style={{ display: this.state.isShow }}><div onClick={() => this.showDetailModal(it.RECORD_ID, it.USER_ID)}><Icon type="eye" style={{ color: "#005b9b", cursor: "pointer" }} /></div></td>
)} )}

View File

@ -218,7 +218,11 @@ class SE018PaperPage extends React.Component {
hasAnswered = false hasAnswered = false
else else
hasAnswered = true hasAnswered = true
if (!hasAnswered) {
if (this.props.data.tableKey == null || this.props.data.tableKey == '2') {
hasAnswered = true
}
}
var perPaper = [] var perPaper = []
if (isCourEdit == false) if (isCourEdit == false)
perPaper = papers //提供显示 perPaper = papers //提供显示
@ -641,16 +645,10 @@ class SE018PaperPage extends React.Component {
}, () => { }, () => {
this.getPapers(); this.getPapers();
}) })
}} }}
filterOption={(input, option) => filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} >
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{ {
this.state.UserList.map(usr => this.state.UserList.map(usr => <Option key={usr.ID} value={usr.ID}>{usr.NAME}</Option>)
<Option key={usr.ID} value={usr.ID}>{usr.NAME}</Option>
)
} }
</Select> </Select>
} }

View File

@ -17,11 +17,12 @@ export default {
let minutes = (Date.parse(stateData.TRAIN_END_TIME) - Date.parse(stateData.TRAIN_START_TIME)) / 1000 / 60 / 45; let minutes = (Date.parse(stateData.TRAIN_END_TIME) - Date.parse(stateData.TRAIN_START_TIME)) / 1000 / 60 / 45;
temps.push({ field: 'HOURS', value: minutes.toFixed(1) }) // setFieldValue("HOURS", minutes.toFixed(1)); temps.push({ field: 'HOURS', value: minutes.toFixed(1) }) // setFieldValue("HOURS", minutes.toFixed(1));
} }
// if (colInfo.FIELD_NAME == 'TRAIN_START_TIME' && stateData.TRAIN_START_TIME) { if (colInfo.FIELD_NAME == 'TRAIN_START_TIME' && stateData.TRAIN_START_TIME) {
// //默认自动处理 开始培训前5分钟开始签到 //默认自动处理 开始培训前5分钟开始签到
// var TRAIN_END_TIME = new Date(stateData.TRAIN_START_TIME); var TRAIN_END_TIME = new Date(stateData.TRAIN_START_TIME);
// temps.push({ field: 'DT_START_SIGN', value: new Date(TRAIN_END_TIME.setMinutes(TRAIN_END_TIME.getMinutes() - 5)) }) stateData.DT_START_SIGN = new Date(TRAIN_END_TIME.setMinutes(TRAIN_END_TIME.getMinutes() - 5))
// } temps.push({ field: 'DT_START_SIGN', value: stateData.DT_START_SIGN.Format('yyyy-MM-dd HH:mm:ss') })
}
if (temps && temps.length > 0) if (temps && temps.length > 0)
setFieldValueByBatch(temps); setFieldValueByBatch(temps);
} }

View File

@ -1,5 +1,5 @@
import { message } from "antd/lib/index"; import { message } from "antd/lib/index";
import { getPropertyData, extendInclude, extendRule, getDataFieldValue, setDataFieldValue, initFilter, empty } from "../../utils/common"; import { getPropertyData, extendInclude, extendRule, getDataFieldValue, setDataFieldValue, initFilter, initFilterGroup, extendGroupRule, extendFilterGroup, empty } from "../../utils/common";
import { connect } from 'dva'; import { connect } from 'dva';
/** /**
* @return {string} * @return {string}
@ -23,9 +23,36 @@ export default {
if (stateData.Nav_Users && stateData.Nav_Users.length > 0) { if (stateData.Nav_Users && stateData.Nav_Users.length > 0) {
stateData.Nav_Users.forEach(e => { stateData.Nav_Users.forEach(e => {
if (e.STATUS == 0 && e.EXAMINATION_RESULTS == undefined) if (e.STATUS == 0 && e.EXAMINATION_RESULTS == undefined && e.DT_SIGN != null && stateData.Nav_Notify && stateData.Nav_Notify.CHECKTYPE == 56) {
e.EXAMINATION_RESULTS = 1 e.EXAMINATION_RESULTS = 1 //默认合格 不考试 Nav_Notify.CHECKTYPE 56
}
}) })
} }
},
btnRefresh: (params) => {//params
let json = initFilter(params.login.OrgId);
extendRule(json, 'RECORD_ID', 1, params.stateData.ID);
params.dispatch({
type: 'app/getDataByPost',
url: 'SE/TrainRecordUser/Entities',
payload: json,
onComplete: (ret) => {
if (ret && ret.length > 0) {
params.stateData.Nav_Users.forEach(e => {
let r = ret.find(x => x.ID == e.ID)
if (e.DT_SIGN != r.DT_SIGN) {
e.DT_SIGN = r.DT_SIGN
e.STATUS = r.STATUS
e.OK = r.OK
e.EXAMINATION_RESULTS = r.EXAMINATION_RESULTS
if (e.DT_SIGN != null && e.EXAMINATION_RESULTS == null && params.stateData.Nav_Notify && params.stateData.Nav_Notify.CHECKTYPE == 56) {
e.EXAMINATION_RESULTS = 1 //有签到 默认 考核通过 不考试 Nav_Notify.CHECKTYPE 56
}
}
})
params.setState({ data: params.stateData });
}
}
});
} }
} }

View File

@ -1,6 +1,7 @@
// DangerJob.js - 危险作业页面组件 // DangerJob.js - 危险作业页面组件
import React from 'react'; import React from 'react';
import { Table, Select, Pagination } from 'antd'; import { Table, Select, Pagination, DatePicker } from 'antd';
import echarts from 'echarts';
import styles from './../fullinter.less'; import styles from './../fullinter.less';
const { Option } = Select; const { Option } = Select;
@ -10,9 +11,15 @@ class DangerJob extends React.Component {
super(props); super(props);
this.state = { this.state = {
currentPage: 1, currentPage: 1,
pageSize: 20, pageSize: 10,
}; };
this.echartsInstances = {
dangerOperationChart: null, // 当日工作票统计柱状图
};
this.chartResizeHandlers = {};
this.isUnmounted = false; this.isUnmounted = false;
// 添加缓存,用于比较数据是否真正变化
this.lastJobTodayQty = null;
} }
// 生成公司选项(与 TrainingContent 保持一致) // 生成公司选项(与 TrainingContent 保持一致)
@ -23,8 +30,6 @@ class DangerJob extends React.Component {
} }
return companyData.map((company, index) => ( return companyData.map((company, index) => (
<Option key={company.ID || index} value={company.ID}> <Option key={company.ID || index} value={company.ID}>
{' '}
{/* 使用 ID 作为 value与 TrainingContent 一致 */}
{company.NAME} {company.NAME}
</Option> </Option>
)); ));
@ -40,14 +45,222 @@ class DangerJob extends React.Component {
}); });
}; };
// 处理开始日期变化
startChange = (value) => {
const { onStartChange } = this.props;
this.setState({ currentPage: 1 }, () => {
if (onStartChange) {
onStartChange(value);
}
});
};
// 处理结束日期变化
endChange = (value) => {
const { onEndChange } = this.props;
this.setState({ currentPage: 1 }, () => {
if (onEndChange) {
onEndChange(value);
}
});
};
// 处理页码变化 // 处理页码变化
handlePageChange = (page, pageSize) => { handlePageChange = (page, pageSize) => {
this.setState({ currentPage: page, pageSize }); this.setState({ currentPage: page, pageSize });
}; };
// 处理每页条数变化 // 等待DOM元素加载完成
handleShowSizeChange = (current, size) => { waitForElement = (elementId, maxRetries = 10) => {
this.setState({ currentPage: 1, pageSize: size }); return new Promise((resolve) => {
let retries = 0;
const checkInterval = setInterval(() => {
const element = document.getElementById(elementId);
if (element || retries >= maxRetries) {
clearInterval(checkInterval);
resolve(!!element);
}
retries++;
}, 50);
});
};
// 设置resize监听
setupResizeHandler = (chartName, renderMethod) => {
const resizeHandler = () => {
if (this.echartsInstances[chartName] && !this.isUnmounted) {
this.echartsInstances[chartName].resize();
}
};
this.chartResizeHandlers[chartName] = resizeHandler;
window.addEventListener('resize', resizeHandler);
};
// 图表: 当日工作票统计柱状图
renderDangerOperationChart = async () => {
if (this.isUnmounted) return;
const elementExists = await this.waitForElement('dangerOperationChart');
if (!elementExists || this.isUnmounted) return;
if (this.echartsInstances.dangerOperationChart) {
this.echartsInstances.dangerOperationChart.dispose();
this.echartsInstances.dangerOperationChart = null;
}
const chartDom = document.getElementById('dangerOperationChart');
if (!chartDom) return;
this.echartsInstances.dangerOperationChart = echarts.init(chartDom);
const { jobTodayQty } = this.props;
let linkData = jobTodayQty || [];
// 如果没有数据,显示“暂无数据”提示
if (linkData.length === 0) {
this.echartsInstances.dangerOperationChart.setOption({
title: {
text: '当日工作票的统计数量',
x: 'center',
y: '25%',
textStyle: { fontSize: 16, color: '#999' },
},
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: '暂无数据',
fill: '#999',
fontSize: 14,
},
},
});
return;
}
// 有数据时正常渲染图表
const xAxisData = linkData.map((item) => item.name);
const seriesData = linkData.map((item) => item.qty);
const option = {
title: {
text: '当日工作票的统计数量',
x: 'center',
y: '5%',
textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' },
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function (params) {
const color = params[0].color;
return `<div style="display: flex; align-items: center;">
<span style="display: inline-block; width: 12px; height: 12px; background-color: ${color}; margin-right: 8px;"></span>
<span>${params[0].name}:</span>
<span style="font-weight: bold; margin-left: 8px; font-size: 16px;">${params[0].value}</span>
</div>`;
},
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderColor: '#4285F4',
borderWidth: 1,
textStyle: { color: '#000', fontSize: 14 },
},
grid: {
left: '8%',
right: '5%',
top: '18%',
bottom: '8%',
containLabel: true,
},
xAxis: [
{
type: 'category',
data: xAxisData,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
textStyle: { color: '#000' },
rotate: xAxisData.length > 4 ? 15 : 0,
interval: 0,
fontSize: 12,
},
},
],
yAxis: [
{
type: 'value',
show: true,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
show: true,
textStyle: { color: '#000' },
},
splitLine: { show: false },
name: '工作票数量',
nameTextStyle: { fontSize: 12 },
},
],
series: [
{
name: '危险作业数量',
type: 'bar',
data: seriesData,
itemStyle: {
normal: {
color: '#4285F4',
borderRadius: [12, 12, 0, 0],
},
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.3)',
},
},
label: {
show: true,
position: 'top',
textStyle: { color: '#4285F4', fontSize: 12, fontWeight: 'bold' },
formatter: (params) => `${params.value}`,
},
barWidth: '50%',
barMaxWidth: 60,
},
],
};
this.echartsInstances.dangerOperationChart.setOption(option);
this.setupResizeHandler('dangerOperationChart', this.renderDangerOperationChart);
};
// 初始化所有图表
initAllCharts = () => {
if (this.isUnmounted) return;
setTimeout(() => {
if (this.isUnmounted) return;
this.renderDangerOperationChart();
}, 100);
};
// 销毁所有图表
disposeAllCharts = () => {
Object.keys(this.echartsInstances).forEach((key) => {
if (this.echartsInstances[key]) {
try {
this.echartsInstances[key].dispose();
} catch (e) {
console.warn(`Dispose chart ${key} error:`, e);
}
this.echartsInstances[key] = null;
}
});
Object.keys(this.chartResizeHandlers).forEach((key) => {
if (this.chartResizeHandlers[key]) {
window.removeEventListener('resize', this.chartResizeHandlers[key]);
}
});
this.chartResizeHandlers = {};
}; };
// 获取过滤后的数据(添加类型检查和默认值) // 获取过滤后的数据(添加类型检查和默认值)
@ -88,33 +301,48 @@ class DangerJob extends React.Component {
}; };
}; };
// 添加 shouldComponentUpdate 优化性能
shouldComponentUpdate(nextProps, nextState) {
// 检查状态变化
if (this.state.currentPage !== nextState.currentPage || this.state.pageSize !== nextState.pageSize) {
return true;
}
// 检查 props 变化
if (
this.props.jobTodayQty !== nextProps.jobTodayQty ||
this.props.dangerSubData !== nextProps.dangerSubData ||
this.props.selectedCompany !== nextProps.selectedCompany ||
this.props.selectedStartDate !== nextProps.selectedStartDate ||
this.props.selectedEndDate !== nextProps.selectedEndDate ||
this.props.companyData !== nextProps.companyData
) {
return true;
}
return false;
}
// 渲染危险作业表格 // 渲染危险作业表格
renderDangerTable = () => { renderDangerTable = () => {
const { selectedCompany, companyData } = this.props; const { selectedCompany, companyData } = this.props;
const { currentPage, pageSize } = this.state; const { currentPage, pageSize } = this.state;
const { data: tableData, total } = this.getCurrentPageData(); const { data: tableData, total } = this.getCurrentPageData();
// 如果没有数据,返回空
if (!tableData || tableData.length === 0) { if (!tableData || tableData.length === 0) {
// 获取选中的公司名称用于显示
let companyName = '';
if (selectedCompany && companyData) {
const selectedCompanyObj = companyData.find((company) => company.ID === selectedCompany);
companyName = selectedCompanyObj?.NAME || '';
}
return ( return (
<div <div
style={{ style={{
textAlign: 'center', textAlign: 'center',
padding: '50px', padding: '50px',
color: '#999', color: '#999',
fontSize: '26px', fontSize: '20px',
display: 'flex', display: 'flex',
alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center',
flex: 1, flex: 1,
}} }}
> >
{selectedCompany ? `${companyName}暂无危险作业数据` : '暂无危险作业数据'} 暂无危险作业数据
</div> </div>
); );
} }
@ -196,10 +424,8 @@ class DangerJob extends React.Component {
}, },
]; ];
// 计算横向滚动宽度(如果列数过多) // 不设置 y 滚动,让表格自然高度,由外层容器控制滚动
const scrollX = columns.reduce((sum, col) => sum + (col.width || 120), 0); const scrollConfig = { x: columns.length * 100, y: 320 };
const scrollConfig = scrollX > 1200 ? { x: scrollX } : {};
// 表格数据转换添加唯一key // 表格数据转换添加唯一key
const dataSource = tableData.map((item, index) => ({ const dataSource = tableData.map((item, index) => ({
key: `${item.companyName}_${item.startDate}_${index}`, key: `${item.companyName}_${item.startDate}_${index}`,
@ -215,72 +441,72 @@ class DangerJob extends React.Component {
})); }));
return ( return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}> <>
<div <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
style={{ <div
textAlign: 'center', style={{
padding: '20px 0 0 0', textAlign: 'center',
fontWeight: 'bold', padding: '20px 0 0 0',
fontSize: '17px', fontWeight: 'bold',
color: '#000', fontSize: '17px',
}} color: '#000',
> }}
当日各公司危险作业清单 >
</div> 当日各公司危险作业清单
<div style={{ flex: 1, padding: '10px' }}> </div>
<Table <div style={{ flex: 1, minHeight: 0, padding: '10px' }}>
columns={columns} <Table
dataSource={dataSource} columns={columns}
pagination={false} dataSource={dataSource}
scroll={scrollConfig} pagination={false}
size="small" scroll={scrollConfig}
bordered size="middle"
className={styles.certificateTable} bordered
/> className={styles.certificateTable}
</div> />
{/* 分页组件 */} </div>
<div {/* 分页组件 */}
style={{ <div
display: 'flex', style={{
justifyContent: 'space-between', display: 'flex',
alignItems: 'center', justifyContent: 'space-between',
padding: '12px 16px', alignItems: 'center',
borderTop: '1px solid #f0f0f0', padding: '12px 16px',
backgroundColor: '#fff', borderTop: '1px solid #f0f0f0',
}} backgroundColor: '#fff',
> flexShrink: 0,
{/* 自定义每页条数选择器 */} }}
<div> >
<span style={{ marginRight: '8px' }}>每页显示</span> <div>
<Select <span style={{ marginRight: '8px' }}>每页显示</span>
value={pageSize} <Select
onChange={(size) => { value={pageSize}
this.setState({ currentPage: 1, pageSize: size }); onChange={(size) => {
}} this.setState({ currentPage: 1, pageSize: size });
style={{ width: 80 }} }}
> style={{ width: 80 }}
<Option value={10}>10</Option> >
<Option value={20}>20</Option> <Option value={10}>10</Option>
</Select> <Option value={20}>20</Option>
</Select>
</div>
<Pagination
current={currentPage}
pageSize={pageSize}
total={total}
showQuickJumper
showTotal={(total) => `${total} 条记录`}
onChange={this.handlePageChange}
/>
</div> </div>
<Pagination
current={currentPage}
pageSize={pageSize}
total={total}
// showSizeChanger
showQuickJumper
showTotal={(total) => `${total} 条记录`}
onChange={this.handlePageChange}
onShowSizeChange={false}
// pageSizeOptions={['10', '20']}
/>
</div> </div>
</div> </>
); );
}; };
componentDidMount() { componentDidMount() {
this.isUnmounted = false; this.isUnmounted = false;
this.initAllCharts();
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
@ -288,6 +514,13 @@ class DangerJob extends React.Component {
if (prevProps.selectedCompany !== this.props.selectedCompany) { if (prevProps.selectedCompany !== this.props.selectedCompany) {
this.setState({ currentPage: 1 }); this.setState({ currentPage: 1 });
} }
if (prevProps.selectedStartDate !== this.props.selectedStartDate) {
this.setState({ currentPage: 1 });
}
if (prevProps.selectedEndDate !== this.props.selectedEndDate) {
this.setState({ currentPage: 1 });
}
// 当数据变化时,如果当前页没有数据且不是第一页,重置到第一页 // 当数据变化时,如果当前页没有数据且不是第一页,重置到第一页
if (prevProps.dangerSubData !== this.props.dangerSubData) { if (prevProps.dangerSubData !== this.props.dangerSubData) {
const { currentPage } = this.state; const { currentPage } = this.state;
@ -298,21 +531,38 @@ class DangerJob extends React.Component {
this.setState({ currentPage: 1 }); this.setState({ currentPage: 1 });
} }
} }
// 检查 jobTodayQty 是否变化,只在数据真正变化时重新渲染图表
const prevJobTodayQty = prevProps.jobTodayQty;
const currentJobTodayQty = this.props.jobTodayQty;
const jobTodayQtyChanged = JSON.stringify(prevJobTodayQty) !== JSON.stringify(currentJobTodayQty);
if (jobTodayQtyChanged) {
this.renderDangerOperationChart();
}
} }
componentWillUnmount() { componentWillUnmount() {
this.isUnmounted = true; this.isUnmounted = true;
this.disposeAllCharts();
} }
disabledCurrentYearDate = (current) => {
if (!current) return false;
const currentYear = new Date().getFullYear();
const selectedYear = current.year();
// 只能选择当前年份
return selectedYear !== currentYear;
};
render() { render() {
const { companyData, selectedCompany } = this.props; const { companyData, selectedCompany, selectedStartDate, selectedEndDate } = this.props;
return ( return (
<div className={styles.trainingContentWrapper}> <div className={styles.trainingContentWrapper}>
<div className={styles.trainingGrid}> <div className={styles.trainingGrid}>
<div className={styles.trainingRow}> <div className={styles.trainingRow}>
<div className={styles.trainingCard}> <div className={styles.trainingCard}>
{/* 公司筛选器 */} {/* 筛选器 */}
<div className={styles.monthSelectorWrapper}> <div className={styles.monthSelectorWrapper}>
<span className={styles.monthSelectorLabel}>选择公司</span> <span className={styles.monthSelectorLabel}>选择公司</span>
<Select <Select
@ -325,10 +575,34 @@ class DangerJob extends React.Component {
> >
{this.getCompanyOptions()} {this.getCompanyOptions()}
</Select> </Select>
<span className={styles.monthSelectorLabel} style={{ margin: '0 20px' }}>
开始日期
</span>
<DatePicker
value={selectedStartDate}
format="YYYY-MM-DD"
onChange={this.startChange}
disabledDate={this.disabledCurrentYearDate}
/>
<span className={styles.monthSelectorLabel} style={{ margin: '0 20px' }}>
结束日期
</span>
<DatePicker
value={selectedEndDate}
format="YYYY-MM-DD"
onChange={this.endChange}
disabledDate={this.disabledCurrentYearDate}
/>
</div> </div>
{this.renderDangerTable()} {this.renderDangerTable()}
</div> </div>
</div> </div>
{/* 第二行 - 当日工作票统计柱状图 */}
<div className={styles.trainingRow}>
<div className={styles.trainingCard}>
<div id="dangerOperationChart" className={styles.trainingChartContainer}></div>
</div>
</div>
</div> </div>
</div> </div>
); );

View File

@ -575,7 +575,7 @@ class HomeContent extends React.Component {
if (this.isUnmounted) return; if (this.isUnmounted) return;
this.riskLevel(); this.riskLevel();
this.safeCheckChart(); this.safeCheckChart();
this.dangerOperation(); // this.dangerOperation();
this.backLog(); this.backLog();
}, 100); }, 100);
}; };
@ -939,6 +939,79 @@ class HomeContent extends React.Component {
{/* 右侧区域 */} {/* 右侧区域 */}
<Col span={7} className={styles.rightCol}> <Col span={7} className={styles.rightCol}>
<div className={styles.dangerCard}>
{/* <div id="dangerOperationChart" className={styles.fullChartContainer}></div> */}
<div className={styles.infoContainer}>
<div className={styles.infoTitle}>集团企业信息汇总</div>
<div className={styles.infoCard}>
<div className={styles.infoCardMark}>集团公司</div>
<div className={styles.infoCardTitle}>吐鲁番金源矿冶有限责任公司</div>
<div className={styles.infoCardDetail}>子公司数量{trainingData?.SumSubCount || '-'}</div>
</div>
<div className={styles.infoBanner}>
{trainingData.listSubTypeCount && Object.keys(trainingData.listSubTypeCount).length > 0 ? (
<>
<div className={styles.bannerScrollContainer} ref={this.bannerScrollRef}>
<div className={styles.bannerScrollContent}>
{Object.entries(trainingData.listSubTypeCount).map(([key, value], index) => (
<div key={index} className={styles.infoSpan}>
<span>{key}</span>
<span>{value}</span>
</div>
))}
</div>
</div>
</>
) : (
// 默认显示当没有数据时保持原有3个的样子
<div
className={styles.bannerScrollContent}
style={{ width: '100%', justifyContent: 'space-around' }}
>
<div className={styles.infoSpan}>
<span>露天矿</span>
<span>--</span>
</div>
<div className={styles.infoSpan}>
<span>地下矿</span>
<span>--</span>
</div>
<div className={styles.infoSpan}>
<span>尾矿库</span>
<span>--</span>
</div>
</div>
)}
</div>
<div className={styles.infoBottom}>
<div className={styles.infoBottomContent}>
<div className={styles.infoBottomImg}>
<img src={require('../../assets/layout/scgm.png')}></img>
</div>
<div className={styles.infoBottomContentRight}>
<div className={styles.infoBottomTitle}>生产规模</div>
<div className={styles.infoBottomNum}>
<span>{trainingData?.SumSubMode || '-'}</span>/
</div>
</div>
</div>
<div className={styles.infoBottomContent}>
<div className={styles.infoBottomImg}>
<img src={require('../../assets/layout/kqmj.png')}></img>
</div>
<div className={styles.infoBottomContentRight}>
<div className={styles.infoBottomTitle}>矿区面积</div>
<div className={styles.infoBottomNum}>
<span>{trainingData?.SumSubArea || '-'}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div className={styles.spacer}></div>
<div className={styles.riskCard}> <div className={styles.riskCard}>
<Row className={styles.riskRow}> <Row className={styles.riskRow}>
<Col span={8} className={styles.trainingStatsCol}> <Col span={8} className={styles.trainingStatsCol}>
@ -958,12 +1031,6 @@ class HomeContent extends React.Component {
</Col> </Col>
</Row> </Row>
</div> </div>
<div className={styles.spacer}></div>
<div className={styles.dangerCard}>
<div id="dangerOperationChart" className={styles.fullChartContainer}></div>
</div>
</Col> </Col>
</Row> </Row>
{/* 公告详情弹窗 */} {/* 公告详情弹窗 */}

View File

@ -14,6 +14,7 @@ import RiskControl from './FullOther/RiskControl';
import ClassBuild from './FullOther/ClassBuild'; import ClassBuild from './FullOther/ClassBuild';
import DangerJob from './FullOther/DangerJob'; import DangerJob from './FullOther/DangerJob';
import HiddenSolve from './FullOther/HiddenSolve'; import HiddenSolve from './FullOther/HiddenSolve';
import moment from 'moment'; // 添加这一行
const getScale = () => { const getScale = () => {
const width = 1920, const width = 1920,
@ -30,7 +31,7 @@ class FullScreen extends React.Component {
nowDate: '', nowDate: '',
riskTypeRate: [], riskTypeRate: [],
riskTotal: [], riskTotal: [],
jobTodayQty: [], // jobTodayQty: [],
hiddenSummary: [], hiddenSummary: [],
taskTop3: [], taskTop3: [],
scale: getScale(), scale: getScale(),
@ -77,8 +78,11 @@ class FullScreen extends React.Component {
riskSubData: {}, riskSubData: {},
classSubData: [], classSubData: [],
dangerSubData: {}, dangerSubData: {},
jobTodayQty: [],
dangerCompanyData: [], dangerCompanyData: [],
selectedDangerCompany: '', selectedDangerCompany: '',
selectedStartDate: moment(),
selectedEndDate: moment(),
announcementDetail: null, announcementDetail: null,
announcementDetailLoading: false, announcementDetailLoading: false,
@ -289,7 +293,7 @@ class FullScreen extends React.Component {
if (ret && !this.isUnmounted) { if (ret && !this.isUnmounted) {
this.setState({ this.setState({
riskTypeRate: ret.riskTypeRate || [], riskTypeRate: ret.riskTypeRate || [],
jobTodayQty: ret.jobTodayQty || [], // jobTodayQty: ret.jobTodayQty || [],
hiddenSummary: ret.hiddenSummary || [], hiddenSummary: ret.hiddenSummary || [],
taskTop3: ret.taskTop3 || [], taskTop3: ret.taskTop3 || [],
}); });
@ -308,16 +312,7 @@ class FullScreen extends React.Component {
onComplete: (ret) => { onComplete: (ret) => {
if (ret && !this.isUnmounted) { if (ret && !this.isUnmounted) {
this.setState({ this.setState({
trainingData: { trainingData: ret,
listNAME: ret.listNAME || [],
YearCount: ret.YearCount || [],
MonthRecordCount: ret.MonthRecordCount || [],
MonthPersonCount: ret.MonthPersonCount || [],
listAnnourcement: ret.listAnnourcement || [],
TITLE: ret.TITLE || '',
listVideoImg: ret.listVideoImg || [],
playSet: ret.playSet || [],
},
}); });
} }
}, },
@ -598,17 +593,29 @@ class FullScreen extends React.Component {
this.getDangerSubData(); this.getDangerSubData();
}); });
}; };
handleStartDateChange = (startDate) => {
this.setState({ selectedStartDate: startDate }, () => {
this.getDangerSubData();
});
};
handleEndDateChange = (endDate) => {
this.setState({ selectedEndDate: endDate }, () => {
this.getDangerSubData();
});
};
getDangerSubData = () => { getDangerSubData = () => {
const orgId = storage('lacal').getItem('webOrgId')?.val; const orgId = storage('lacal').getItem('webOrgId')?.val;
const json = initFilter(orgId); const json = initFilter(orgId);
json.Parameter1 = this.state.selectedStartDate.format('YYYY-MM-DD');
json.Parameter2 = this.state.selectedEndDate.format('YYYY-MM-DD');
this.props.dispatch({ this.props.dispatch({
type: 'app/getDataByPost', type: 'app/getDataByPost',
payload: json, payload: json,
url: 'BI/BIKanBanController/JobManage', url: 'BI/BIKanBanController/JobManage',
onComplete: (ret) => { onComplete: (ret) => {
if (ret && !this.isUnmounted) { if (ret && !this.isUnmounted) {
let filteredData = ret; let filteredData = ret.jobList;
if (this.state.selectedDangerCompany) { if (this.state.selectedDangerCompany) {
const selectedCompanyObj = this.state.dangerCompanyData?.find( const selectedCompanyObj = this.state.dangerCompanyData?.find(
(company) => company.ID === this.state.selectedDangerCompany (company) => company.ID === this.state.selectedDangerCompany
@ -621,6 +628,7 @@ class FullScreen extends React.Component {
this.setState({ this.setState({
dangerSubData: filteredData, dangerSubData: filteredData,
jobTodayQty: ret.jobTodayQty,
}); });
} }
}, },
@ -679,9 +687,14 @@ class FullScreen extends React.Component {
return ( return (
<DangerJob <DangerJob
dangerSubData={this.state.dangerSubData} dangerSubData={this.state.dangerSubData}
jobTodayQty={this.state.jobTodayQty}
companyData={this.state.dangerCompanyData} companyData={this.state.dangerCompanyData}
selectedCompany={this.state.selectedDangerCompany} selectedCompany={this.state.selectedDangerCompany}
onCompanyChange={this.handleDangerCompanyChange} onCompanyChange={this.handleDangerCompanyChange}
selectedStartDate={this.state.selectedStartDate}
onStartChange={this.handleStartDateChange}
selectedEndDate={this.state.selectedEndDate}
onEndChange={this.handleEndDateChange}
/> />
); );
} }
@ -770,7 +783,7 @@ class FullScreen extends React.Component {
riskTypeRate={riskTypeRate} riskTypeRate={riskTypeRate}
riskTotal={riskTotal} riskTotal={riskTotal}
hiddenSummary={hiddenSummary} hiddenSummary={hiddenSummary}
jobTodayQty={jobTodayQty} // jobTodayQty={jobTodayQty}
taskTop3={taskTop3} taskTop3={taskTop3}
mediaList={mediaList} mediaList={mediaList}
announcementList={announcementList} announcementList={announcementList}

View File

@ -162,6 +162,277 @@
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
} }
.infoContainer {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 20px;
height: 100%;
}
.infoTitle {
font-size: 20px;
font-weight: bold;
text-align: left;
color: #000;
margin-bottom: 10px;
}
.infoCard {
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
background: url('../assets/layout/dt.png') no-repeat center center;
background-size: cover;
border-radius: 10px;
padding: 28px 28px 36px 28px;
align-items: flex-start;
}
.infoCardMark {
position: absolute;
top: 0px;
right: 0px;
background-color: #1668fe;
padding: 6px 17px;
border-radius: 0 10px;
font-size: 14px;
color: #fff;
}
.infoCardTitle {
font-size: 20px;
color: #082a61;
font-weight: bold;
text-align: left;
margin-bottom: 14px;
}
.infoCardDetail {
font-size: 13px;
color: #1f69ff;
text-align: left;
background: rgba(255, 255, 255, 0.5);
border-radius: 13px;
padding: 4px 16px;
}
// 替换原有的 .infoBanner 样式
.infoBanner {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
margin: 20px 0;
position: relative;
width: 100%;
overflow: hidden;
}
// 滚动容器
.bannerScrollContainer {
flex: 1;
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
// 隐藏滚动条(可选,如果想要保持美观)
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 2px;
&:hover {
background: #999;
}
}
}
.bannerScrollContent {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around; // 保持平均分布
gap: 30px;
padding: 0 10px;
}
// 当项目数量 <= 3 时,每个项目平均分布
.infoBanner:not(.hasScroll) .infoSpan {
flex: 1;
}
// 修改 infoSpan 内部样式 - 保持固定宽度
.infoSpan {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
flex-shrink: 0; // 防止被压缩
width: 100px; // 固定宽度保持和3个时一样大
min-width: 100px;
> span:first-child {
color: #5f7291;
font-size: 18px;
font-weight: 500;
white-space: nowrap;
}
> span:last-child {
color: #000;
font-size: 27px;
font-weight: bold;
white-space: nowrap;
}
// 添加分割线效果
position: relative;
&:not(:last-child)::after {
content: '';
position: absolute;
right: -15px;
top: 50%;
transform: translateY(-50%);
width: 1px;
height: 40px;
background: linear-gradient(to bottom, transparent, #ccc, transparent);
}
}
// 左右箭头样式
.bannerArrowLeft,
.bannerArrowRight {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 4px;
transition: all 0.3s ease;
i,
.anticon {
font-size: 18px;
color: #fff;
}
&:hover {
background-color: rgba(0, 0, 0, 0.7);
transform: translateY(-50%) scale(1.05);
}
}
.bannerArrowLeft {
left: 0;
}
.bannerArrowRight {
right: 0;
}
// 修改 infoSpan 内部样式
.infoSpan {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px; // 标题和数值间距 10px
> span:first-child {
color: #5f7291;
font-size: 18px;
font-weight: 500;
}
> span:last-child {
color: #000;
font-size: 27px;
font-weight: bold;
}
}
.infoBottom {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
background-color: #f5f7fc;
border-radius: 10px;
// padding: 26px;
margin-top: 10px;
position: relative;
.infoBottomContent {
flex: 1;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 12px;
position: relative;
padding: 26px;
&:not(:last-child)::after {
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 2px;
height: 60px;
background: linear-gradient(to bottom, transparent, #e6e9f3, transparent);
}
}
}
.infoBottomImg {
width: 40px;
height: 40px;
flex-shrink: 0;
margin-right: 10px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.infoBottomContentRight {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.infoBottomTitle {
font-size: 18px;
color: #5f7291;
margin-bottom: 4px;
font-weight: 500;
white-space: nowrap;
}
.infoBottomNum {
font-size: 16px;
color: #1d6fe9;
white-space: nowrap;
span {
font-size: 20px;
font-weight: bold;
}
}
// 左侧和右侧卡片样式 // 左侧和右侧卡片样式
.riskCard { .riskCard {

View File

@ -2,7 +2,17 @@ import React, { useState, useEffect, useRef, Component } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import storage from '../utils/storage'; import storage from '../utils/storage';
import IconFont from '../utils/iconFont'; import IconFont from '../utils/iconFont';
import { initFilter, addRuleAndGroups, guid, extendInclude, extendRule, extend, extendOrder } from '../utils/common'; import {
initFilter,
addRuleAndGroups,
guid,
extendInclude,
extendRule,
extend,
extendOrder,
showFiles,
GetFileModel,
} from '../utils/common';
import './home.less'; import './home.less';
// 组件库 // 组件库
import { import {
@ -28,6 +38,7 @@ import FormPage from '../components/FormPage';
import backlog from '../assets/home/backlog.png'; import backlog from '../assets/home/backlog.png';
import backlog_i from '../assets/home/backlog_icon.png'; import backlog_i from '../assets/home/backlog_icon.png';
import styles from './dashboard.css'; import styles from './dashboard.css';
import styleshome from './annourcement.less';
import 'slick-carousel/slick/slick.css'; import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css'; import 'slick-carousel/slick/slick-theme.css';
import moment from 'moment'; import moment from 'moment';
@ -182,6 +193,13 @@ class Home extends React.Component {
}, },
taskSelects: [], taskSelects: [],
mineType: localStorage.getItem('currentMineType') === '20' ? '10' : localStorage.getItem('currentMineType'), mineType: localStorage.getItem('currentMineType') === '20' ? '10' : localStorage.getItem('currentMineType'),
annourcement: [],
announcementDetail: null, // 公告详情
announcementModalVisible: false, // 公告弹窗可见性
announcementDetailLoading: false, // 公告详情加载状态
fileForm: {
visible: false,
},
}; };
// this.enums = []; // this.enums = [];
this.reportType = ''; this.reportType = '';
@ -618,20 +636,20 @@ class Home extends React.Component {
}; };
//风险等级占比 //风险等级占比
riskLevel = () => { riskLevel = () => {
const userInfo = storage('lacal').getItem('webUserInfo').val;
let json = initFilter(this.props.login.OrgId); let json = initFilter(this.props.login.OrgId);
extendOrder(json, 'NUM', 0); extendOrder(json, 'NUM', 0);
extendRule(json, 'COLOR', 1, this.state.mineType); extendOrder(json, 'CREATE_TIME', 1);
this.props.dispatch({ this.props.dispatch({
type: 'app/getDataByPost', type: 'app/getDataByPost',
url: 'FM/RiskLevelProportion/OrderPaged', url: 'PF/PFAnnourcement/OrderPaged',
payload: json, payload: json,
onlyData: false, onlyData: false,
onComplete: (data) => { onComplete: (data) => {
if (data && data.IsSuccessful) { if (data && data.IsSuccessful) {
if (data.Data && data.Data.length > 0) { if (data.Data && data.Data.length > 0) {
console.log('风险等级占比', data.Data);
this.setState({ this.setState({
riskLevelPage: data.Data, annourcement: data.Data,
}); });
} }
} }
@ -1369,9 +1387,141 @@ class Home extends React.Component {
detailForm: detailForm, detailForm: detailForm,
}); });
}; };
// 添加获取公告详情的方法
getAnnouncementDetail = (announcement) => {
return new Promise((resolve, reject) => {
const orgId = storage('lacal').getItem('webOrgId')?.val;
const json = initFilter(orgId);
extendRule(json, 'ID', 1, announcement.ID);
json.Include = ['Nav_Orgs', 'Nav_Orgs.Nav_OrgSub', 'Nav_Files.Nav_ImgFile'];
this.setState({ announcementDetailLoading: true, announcementDetail: null });
this.props.dispatch({
type: 'app/getDataByPost',
payload: json,
url: 'PF/PFAnnourcement/Get',
onComplete: (ret) => {
if (ret) {
this.setState({
announcementDetail: ret,
announcementDetailLoading: false,
});
resolve(ret);
} else {
message.error('暂无公告详情');
this.setState({ announcementDetailLoading: false });
reject(new Error('获取公告详情失败'));
}
},
onError: (error) => {
this.setState({ announcementDetailLoading: false });
reject(error);
},
});
});
};
// 添加点击公告的处理方法
handleAnnouncementClick = async (announcement) => {
try {
const detail = await this.getAnnouncementDetail(announcement);
this.setState({
announcementModalVisible: true,
});
} catch (error) {
console.error('获取公告详情失败:', error);
message.error('获取公告详情失败');
}
};
// 添加关闭公告弹窗的方法
handleAnnouncementModalClose = () => {
this.setState({
announcementModalVisible: false,
announcementDetail: null,
});
};
// 添加渲染公告弹窗内容的方法
renderAnnouncementModal = () => {
const { announcementDetail, announcementDetailLoading } = this.state;
if (announcementDetailLoading) {
return (
<div style={{ textAlign: 'center', padding: '50px' }}>
<Icon type="loading" style={{ fontSize: '32px' }} />
</div>
);
}
if (!announcementDetail) return null;
const { TITLE, ABSTRACT, START, END, CONTENT, CREATE_USER_NAME, Nav_Files = [] } = announcementDetail;
return (
<div>
{/* 标题 */}
<div style={{ textAlign: 'center', fontSize: '18px', color: '#333', marginBottom: '16px', fontWeight: 'bold' }}>
{TITLE}
</div>
{/* 摘要 */}
{ABSTRACT && (
<div
style={{
fontSize: '14px',
color: '#676767',
wordBreak: 'break-all',
wordWrap: 'break-word',
marginBottom: '12px',
lineHeight: '1.5',
padding: '0 16px',
}}
>
{ABSTRACT}
</div>
)}
{/* 日期和发布人 */}
<div style={{ fontSize: '12px', color: '#999', marginBottom: '16px', textAlign: 'center' }}>
{START ? START.split(' ')[0] : '--'} {END ? END.split(' ')[0] : '--'}
{CREATE_USER_NAME && <span style={{ marginLeft: '20px' }}>发布人{CREATE_USER_NAME}</span>}
</div>
{/* 分割线 */}
<div style={{ borderTop: '1px solid #e8e8e8', marginBottom: '16px' }}></div>
{/* 正文内容 */}
<div
style={{
fontSize: '14px',
lineHeight: '1.8',
color: '#333',
marginBottom: '16px',
maxHeight: '60vh',
overflowY: 'auto',
padding: '0 8px',
}}
dangerouslySetInnerHTML={{ __html: CONTENT || '<div style="color:#999;text-align:center;">暂无内容</div>' }}
/>
{/* 附件列表 */}
{Nav_Files && Nav_Files.length > 0 && (
<div style={{ marginTop: '16px', borderTop: '1px solid #f0f0f0', paddingTop: '12px' }}>
<div style={{ fontWeight: 'bold', marginBottom: '12px', fontSize: '14px', color: '#333' }}>
附件 ({Nav_Files.length})
</div>
{showFiles(Nav_Files, config.picServerHost, this)}
{GetFileModel(Modal, FormPage, this, this.state.fileForm.visible)}
</div>
)}
</div>
);
};
render() { render() {
const { tableData } = this.state; const { tableData, annourcement } = this.state;
const thingsData = this.state.delayData; const thingsData = this.state.delayData;
const settings = { const settings = {
dots: false, dots: false,
@ -1431,6 +1581,20 @@ class Home extends React.Component {
size="small" size="small"
/> />
</Modal> </Modal>
<Modal
title="公告详情"
visible={this.state.announcementModalVisible}
onCancel={this.handleAnnouncementModalClose}
footer={[
<Button key="close" onClick={this.handleAnnouncementModalClose}>
关闭
</Button>,
]}
width="600px"
bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }}
>
{this.renderAnnouncementModal()}
</Modal>
<Modal <Modal
visible={this.state.scoreVisible} visible={this.state.scoreVisible}
title="标准化得分明细" title="标准化得分明细"
@ -1643,34 +1807,38 @@ class Home extends React.Component {
}} }}
> >
<div className="dashboard-div-style"> <div className="dashboard-div-style">
<div className="statistical-title-style"> <div className={styleshome.announcementCard}>
<div className="badge-style"> <div className={styleshome.announcementHeader}>
<Icon type="pie-chart" style={{ color: '#5f6ac2', marginRight: '4px' }}></Icon> <div className={styleshome.announcementTitle}>
风险等级占比 <Icon type="sound" className={styleshome.announcementIcon} />
<span>公司公告</span>
</div>
<span className={styleshome.announcementCount}> {annourcement?.length || 0} 条公告</span>
</div> </div>
{this.state.riskLevelPage.length > 0 ? ( <div className={styleshome.announcementList}>
<span {annourcement?.length > 0 ? (
style={{ <ul className={styleshome.announcementUl}>
fontSize: '14px', {annourcement.map((item, index) => (
float: 'right', <li
marginRight: '16px', key={item.id || index}
// marginTop: "4px", className={styleshome.announcementItem}
}} onClick={() => this.handleAnnouncementClick(item)}
> >
<div onClick={() => this.showDetailModal('BI013_RISKANALYSISMODEL')}> <span className={styleshome.announcementItemTitle} title={item.TITLE}>
<IconFont {item.TITLE}
type="icon-24gl-expand2" </span>
style={{ <span className={styleshome.announcementItemTime}>{item.START}</span>
fontSize: '18px', </li>
color: '#333333', ))}
cursor: 'pointer', </ul>
}} ) : (
></IconFont> <div className={styleshome.emptyAnnouncement}>
<Icon type="inbox" className={styleshome.emptyIcon} />
<span>暂无公告</span>
</div> </div>
</span> )}
) : null} </div>
</div> </div>
{this.state.riskLevelPage.length > 0 ? <RisiLevel riskLevelPage={this.state.riskLevelPage} /> : null}
</div> </div>
</div> </div>
</Col> </Col>

View File

@ -0,0 +1,98 @@
.announcementCard {
flex: 3;
background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
height: 100%;
}
.announcementHeader {
width: 100%;
padding: 8px 20px;
border-bottom: 2px solid #1890ff;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.announcementTitle {
display: flex;
align-items: center;
}
.announcementIcon {
font-size: 16px;
color: #1890ff;
margin-right: 8px;
}
.announcementTitle span {
font-size: 18px;
color: #333333;
}
.announcementCount {
font-size: 14px;
color: #999;
}
.announcementList {
flex: 1;
overflow-y: auto;
padding: 8px 0;
}
.announcementUl {
list-style: none;
margin: 0;
padding: 0;
}
.announcementItem {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.3s;
&:hover {
background-color: #f5f5f5;
}
}
.announcementItemTitle {
font-size: 14px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
text-align: left;
}
.announcementItemTime {
font-size: 14px;
color: #999;
margin-left: 16px;
white-space: nowrap;
}
.emptyAnnouncement {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
}
.emptyIcon {
font-size: 48px;
margin-bottom: 16px;
}