diff --git a/src/assets/layout/dt.png b/src/assets/layout/dt.png new file mode 100644 index 0000000..6f42e6a Binary files /dev/null and b/src/assets/layout/dt.png differ diff --git a/src/assets/layout/dt@2x.png b/src/assets/layout/dt@2x.png new file mode 100644 index 0000000..a62ba30 Binary files /dev/null and b/src/assets/layout/dt@2x.png differ diff --git a/src/assets/layout/kqmj.png b/src/assets/layout/kqmj.png new file mode 100644 index 0000000..cbe2963 Binary files /dev/null and b/src/assets/layout/kqmj.png differ diff --git a/src/assets/layout/kqmj@2x.png b/src/assets/layout/kqmj@2x.png new file mode 100644 index 0000000..524c018 Binary files /dev/null and b/src/assets/layout/kqmj@2x.png differ diff --git a/src/assets/layout/scgm.png b/src/assets/layout/scgm.png new file mode 100644 index 0000000..7dc54ca Binary files /dev/null and b/src/assets/layout/scgm.png differ diff --git a/src/assets/layout/scgm@2x.png b/src/assets/layout/scgm@2x.png new file mode 100644 index 0000000..6e8d25b Binary files /dev/null and b/src/assets/layout/scgm@2x.png differ diff --git a/src/components/CustomPages/SE/SE018EditPage.js b/src/components/CustomPages/SE/SE018EditPage.js index 6a2de54..b5b6c8f 100644 --- a/src/components/CustomPages/SE/SE018EditPage.js +++ b/src/components/CustomPages/SE/SE018EditPage.js @@ -64,6 +64,7 @@ class SE018EditPage extends React.Component { extendInclude(json, 'Nav_Users.Nav_Files.Nav_ImgFile'); extendInclude(json, 'Nav_Files.Nav_ImgFile'); extendInclude(json, 'Nav_Papers'); + extendInclude(json, 'Nav_UserApro'); json.IgnoreDataRule = true; this.props.dispatch({ type: 'app/getDataByPost', @@ -79,7 +80,8 @@ class SE018EditPage extends React.Component { updateData(data) { let isApprove = false; // 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.isShow = "table-cell"; } @@ -88,8 +90,11 @@ class SE018EditPage extends React.Component { data.Nav_Users = data.Nav_Users.filter(item => item.STATUS == 0); var passRateThis = 0 - 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 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 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) { passRateThis = (userPass.length / userAll.length).toFixed(4) * 100; } @@ -226,7 +231,7 @@ class SE018EditPage extends React.Component { { this.state.isView && <> } + trigger={() => } content={() => this.componentRef} /> @@ -337,12 +342,13 @@ class SE018EditPage extends React.Component { - 审阅意见 - - { - data && data.STATUS > 2 && '已阅' - } - + 审阅意见 + { + data && data.STATUS > 2 && data.DT_APPROVE ? +

审阅人:{showUserSign(data.Nav_UserApro, config.picServerHost)}

+

审阅时间:{data.DT_APPROVE}

+ : + } 合格率 @@ -381,12 +387,12 @@ class SE018EditPage extends React.Component { {this.state.isExam && {it?.SCORE}} {/* 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 && ( this.state.isShow && data.Nav_Notify.CHECKTYPE ? ( - data.Nav_Notify.CHECKTYPE == 51 ?
this.showDetailModal(it.RECORD_ID, it.USER_ID)}>
- : {showFiles(it?.Nav_Files, config.picServerHost, this, false)} + data.Nav_Notify.CHECKTYPE == 51 ?
this.showDetailModal(it.RECORD_ID, it.USER_ID)}>
+ : {showFiles(it?.Nav_Files, config.picServerHost, this, false)} ) :
this.showDetailModal(it.RECORD_ID, it.USER_ID)}>
)} diff --git a/src/components/CustomPages/SE/SE018PaperPage.js b/src/components/CustomPages/SE/SE018PaperPage.js index 3da5afa..1f3f5df 100644 --- a/src/components/CustomPages/SE/SE018PaperPage.js +++ b/src/components/CustomPages/SE/SE018PaperPage.js @@ -218,7 +218,11 @@ class SE018PaperPage extends React.Component { hasAnswered = false else hasAnswered = true - + if (!hasAnswered) { + if (this.props.data.tableKey == null || this.props.data.tableKey == '2') { + hasAnswered = true + } + } var perPaper = [] if (isCourEdit == false) perPaper = papers //提供显示 @@ -641,16 +645,10 @@ class SE018PaperPage extends React.Component { }, () => { this.getPapers(); }) - }} - filterOption={(input, option) => - option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 - } - > + filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} > { - this.state.UserList.map(usr => - - ) + this.state.UserList.map(usr => ) } } diff --git a/src/files/edit/SE014.js b/src/files/edit/SE014.js index d7c2041..3800045 100644 --- a/src/files/edit/SE014.js +++ b/src/files/edit/SE014.js @@ -17,11 +17,12 @@ export default { 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)); } - // if (colInfo.FIELD_NAME == 'TRAIN_START_TIME' && stateData.TRAIN_START_TIME) { - // //默认自动处理 开始培训前5分钟开始签到 - // 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)) }) - // } + if (colInfo.FIELD_NAME == 'TRAIN_START_TIME' && stateData.TRAIN_START_TIME) { + //默认自动处理 开始培训前5分钟开始签到 + var TRAIN_END_TIME = new Date(stateData.TRAIN_START_TIME); + 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) setFieldValueByBatch(temps); } diff --git a/src/files/edit/SE018.js b/src/files/edit/SE018.js index d53d72d..f50aae1 100644 --- a/src/files/edit/SE018.js +++ b/src/files/edit/SE018.js @@ -1,5 +1,5 @@ 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'; /** * @return {string} @@ -23,9 +23,36 @@ export default { if (stateData.Nav_Users && stateData.Nav_Users.length > 0) { stateData.Nav_Users.forEach(e => { - if (e.STATUS == 0 && e.EXAMINATION_RESULTS == undefined) - e.EXAMINATION_RESULTS = 1 + if (e.STATUS == 0 && e.EXAMINATION_RESULTS == undefined && e.DT_SIGN != null && stateData.Nav_Notify && stateData.Nav_Notify.CHECKTYPE == 56) { + 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 }); + } + } + }); } } diff --git a/src/layout/FullOther/DangerJob.js b/src/layout/FullOther/DangerJob.js index 27468a8..1f36159 100644 --- a/src/layout/FullOther/DangerJob.js +++ b/src/layout/FullOther/DangerJob.js @@ -1,6 +1,7 @@ // DangerJob.js - 危险作业页面组件 import React from 'react'; -import { Table, Select, Pagination } from 'antd'; +import { Table, Select, Pagination, DatePicker } from 'antd'; +import echarts from 'echarts'; import styles from './../fullinter.less'; const { Option } = Select; @@ -10,9 +11,15 @@ class DangerJob extends React.Component { super(props); this.state = { currentPage: 1, - pageSize: 20, + pageSize: 10, }; + this.echartsInstances = { + dangerOperationChart: null, // 当日工作票统计柱状图 + }; + this.chartResizeHandlers = {}; this.isUnmounted = false; + // 添加缓存,用于比较数据是否真正变化 + this.lastJobTodayQty = null; } // 生成公司选项(与 TrainingContent 保持一致) @@ -23,8 +30,6 @@ class DangerJob extends React.Component { } return companyData.map((company, index) => ( )); @@ -40,14 +45,222 @@ class DangerJob extends React.Component { }); }; + // 处理开始日期变化 + startChange = (value) => { + const { onStartChange } = this.props; + this.setState({ currentPage: 1 }, () => { + if (onStartChange) { + onStartChange(value); + } + }); + }; + + // 处理结束日期变化 + endChange = (value) => { + const { onEndChange } = this.props; + this.setState({ currentPage: 1 }, () => { + if (onEndChange) { + onEndChange(value); + } + }); + }; + // 处理页码变化 handlePageChange = (page, pageSize) => { this.setState({ currentPage: page, pageSize }); }; - // 处理每页条数变化 - handleShowSizeChange = (current, size) => { - this.setState({ currentPage: 1, pageSize: size }); + // 等待DOM元素加载完成 + waitForElement = (elementId, maxRetries = 10) => { + return new Promise((resolve) => { + let retries = 0; + const checkInterval = setInterval(() => { + const element = document.getElementById(elementId); + if (element || retries >= maxRetries) { + clearInterval(checkInterval); + resolve(!!element); + } + retries++; + }, 50); + }); + }; + + // 设置resize监听 + setupResizeHandler = (chartName, renderMethod) => { + const resizeHandler = () => { + if (this.echartsInstances[chartName] && !this.isUnmounted) { + this.echartsInstances[chartName].resize(); + } + }; + this.chartResizeHandlers[chartName] = resizeHandler; + window.addEventListener('resize', resizeHandler); + }; + + // 图表: 当日工作票统计柱状图 + renderDangerOperationChart = async () => { + if (this.isUnmounted) return; + const elementExists = await this.waitForElement('dangerOperationChart'); + if (!elementExists || this.isUnmounted) return; + + if (this.echartsInstances.dangerOperationChart) { + this.echartsInstances.dangerOperationChart.dispose(); + this.echartsInstances.dangerOperationChart = null; + } + + const chartDom = document.getElementById('dangerOperationChart'); + if (!chartDom) return; + + this.echartsInstances.dangerOperationChart = echarts.init(chartDom); + + const { jobTodayQty } = this.props; + let linkData = jobTodayQty || []; + + // 如果没有数据,显示“暂无数据”提示 + if (linkData.length === 0) { + this.echartsInstances.dangerOperationChart.setOption({ + title: { + text: '当日工作票的统计数量', + x: 'center', + y: '25%', + textStyle: { fontSize: 16, color: '#999' }, + }, + graphic: { + type: 'text', + left: 'center', + top: 'middle', + style: { + text: '暂无数据', + fill: '#999', + fontSize: 14, + }, + }, + }); + return; + } + + // 有数据时正常渲染图表 + const xAxisData = linkData.map((item) => item.name); + const seriesData = linkData.map((item) => item.qty); + + const option = { + title: { + text: '当日工作票的统计数量', + x: 'center', + y: '5%', + textStyle: { fontSize: 16, color: '#000', fontWeight: 'bold' }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { type: 'shadow' }, + formatter: function (params) { + const color = params[0].color; + return `
+ + ${params[0].name}: + ${params[0].value} +
`; + }, + backgroundColor: 'rgba(255, 255, 255, 0.9)', + borderColor: '#4285F4', + borderWidth: 1, + textStyle: { color: '#000', fontSize: 14 }, + }, + grid: { + left: '8%', + right: '5%', + top: '18%', + bottom: '8%', + containLabel: true, + }, + xAxis: [ + { + type: 'category', + data: xAxisData, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + textStyle: { color: '#000' }, + rotate: xAxisData.length > 4 ? 15 : 0, + interval: 0, + fontSize: 12, + }, + }, + ], + yAxis: [ + { + type: 'value', + show: true, + axisLine: { show: false }, + axisTick: { show: false }, + axisLabel: { + show: true, + textStyle: { color: '#000' }, + }, + splitLine: { show: false }, + name: '工作票数量', + nameTextStyle: { fontSize: 12 }, + }, + ], + series: [ + { + name: '危险作业数量', + type: 'bar', + data: seriesData, + itemStyle: { + normal: { + color: '#4285F4', + borderRadius: [12, 12, 0, 0], + }, + emphasis: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.3)', + }, + }, + label: { + show: true, + position: 'top', + textStyle: { color: '#4285F4', fontSize: 12, fontWeight: 'bold' }, + formatter: (params) => `${params.value}`, + }, + barWidth: '50%', + barMaxWidth: 60, + }, + ], + }; + + this.echartsInstances.dangerOperationChart.setOption(option); + this.setupResizeHandler('dangerOperationChart', this.renderDangerOperationChart); + }; + + // 初始化所有图表 + initAllCharts = () => { + if (this.isUnmounted) return; + setTimeout(() => { + if (this.isUnmounted) return; + this.renderDangerOperationChart(); + }, 100); + }; + + // 销毁所有图表 + disposeAllCharts = () => { + Object.keys(this.echartsInstances).forEach((key) => { + if (this.echartsInstances[key]) { + try { + this.echartsInstances[key].dispose(); + } catch (e) { + console.warn(`Dispose chart ${key} error:`, e); + } + this.echartsInstances[key] = null; + } + }); + + Object.keys(this.chartResizeHandlers).forEach((key) => { + if (this.chartResizeHandlers[key]) { + window.removeEventListener('resize', this.chartResizeHandlers[key]); + } + }); + this.chartResizeHandlers = {}; }; // 获取过滤后的数据(添加类型检查和默认值) @@ -88,33 +301,48 @@ class DangerJob extends React.Component { }; }; + // 添加 shouldComponentUpdate 优化性能 + shouldComponentUpdate(nextProps, nextState) { + // 检查状态变化 + if (this.state.currentPage !== nextState.currentPage || this.state.pageSize !== nextState.pageSize) { + return true; + } + // 检查 props 变化 + if ( + this.props.jobTodayQty !== nextProps.jobTodayQty || + this.props.dangerSubData !== nextProps.dangerSubData || + this.props.selectedCompany !== nextProps.selectedCompany || + this.props.selectedStartDate !== nextProps.selectedStartDate || + this.props.selectedEndDate !== nextProps.selectedEndDate || + this.props.companyData !== nextProps.companyData + ) { + return true; + } + return false; + } + // 渲染危险作业表格 renderDangerTable = () => { const { selectedCompany, companyData } = this.props; const { currentPage, pageSize } = this.state; const { data: tableData, total } = this.getCurrentPageData(); + // 如果没有数据,返回空 if (!tableData || tableData.length === 0) { - // 获取选中的公司名称用于显示 - let companyName = ''; - if (selectedCompany && companyData) { - const selectedCompanyObj = companyData.find((company) => company.ID === selectedCompany); - companyName = selectedCompanyObj?.NAME || ''; - } return (
- {selectedCompany ? `${companyName}暂无危险作业数据` : '暂无危险作业数据'} + 暂无危险作业数据
); } @@ -196,10 +424,8 @@ class DangerJob extends React.Component { }, ]; - // 计算横向滚动宽度(如果列数过多) - const scrollX = columns.reduce((sum, col) => sum + (col.width || 120), 0); - const scrollConfig = scrollX > 1200 ? { x: scrollX } : {}; - + // 不设置 y 滚动,让表格自然高度,由外层容器控制滚动 + const scrollConfig = { x: columns.length * 100, y: 320 }; // 表格数据转换(添加唯一key) const dataSource = tableData.map((item, index) => ({ key: `${item.companyName}_${item.startDate}_${index}`, @@ -215,72 +441,72 @@ class DangerJob extends React.Component { })); return ( -
-
- 当日各公司危险作业清单 -
-
- - - {/* 分页组件 */} -
- {/* 自定义每页条数选择器 */} -
- 每页显示: - + <> +
+
+ 当日各公司危险作业清单 +
+
+
+ + {/* 分页组件 */} +
+
+ 每页显示: + +
+ `共 ${total} 条记录`} + onChange={this.handlePageChange} + />
- `共 ${total} 条记录`} - onChange={this.handlePageChange} - onShowSizeChange={false} - // pageSizeOptions={['10', '20']} - /> - + ); }; componentDidMount() { this.isUnmounted = false; + this.initAllCharts(); } componentDidUpdate(prevProps) { @@ -288,6 +514,13 @@ class DangerJob extends React.Component { if (prevProps.selectedCompany !== this.props.selectedCompany) { this.setState({ currentPage: 1 }); } + if (prevProps.selectedStartDate !== this.props.selectedStartDate) { + this.setState({ currentPage: 1 }); + } + if (prevProps.selectedEndDate !== this.props.selectedEndDate) { + this.setState({ currentPage: 1 }); + } + // 当数据变化时,如果当前页没有数据且不是第一页,重置到第一页 if (prevProps.dangerSubData !== this.props.dangerSubData) { const { currentPage } = this.state; @@ -298,21 +531,38 @@ class DangerJob extends React.Component { this.setState({ currentPage: 1 }); } } + + // 检查 jobTodayQty 是否变化,只在数据真正变化时重新渲染图表 + const prevJobTodayQty = prevProps.jobTodayQty; + const currentJobTodayQty = this.props.jobTodayQty; + const jobTodayQtyChanged = JSON.stringify(prevJobTodayQty) !== JSON.stringify(currentJobTodayQty); + + if (jobTodayQtyChanged) { + this.renderDangerOperationChart(); + } } componentWillUnmount() { this.isUnmounted = true; + this.disposeAllCharts(); } + disabledCurrentYearDate = (current) => { + if (!current) return false; + const currentYear = new Date().getFullYear(); + const selectedYear = current.year(); + // 只能选择当前年份 + return selectedYear !== currentYear; + }; render() { - const { companyData, selectedCompany } = this.props; + const { companyData, selectedCompany, selectedStartDate, selectedEndDate } = this.props; return (
- {/* 公司筛选器 */} + {/* 筛选器 */}
选择公司: + + 开始日期: + + + + 结束日期: + +
{this.renderDangerTable()}
+ {/* 第二行 - 当日工作票统计柱状图 */} +
+
+
+
+
); diff --git a/src/layout/FullOther/HomeContent.js b/src/layout/FullOther/HomeContent.js index e5fa090..4c5d5d9 100644 --- a/src/layout/FullOther/HomeContent.js +++ b/src/layout/FullOther/HomeContent.js @@ -575,7 +575,7 @@ class HomeContent extends React.Component { if (this.isUnmounted) return; this.riskLevel(); this.safeCheckChart(); - this.dangerOperation(); + // this.dangerOperation(); this.backLog(); }, 100); }; @@ -939,6 +939,79 @@ class HomeContent extends React.Component { {/* 右侧区域 */}
+
+ {/*
*/} +
+
集团企业信息汇总
+
+
集团公司
+
吐鲁番金源矿冶有限责任公司
+
子公司数量:{trainingData?.SumSubCount || '-'}家
+
+
+ {trainingData.listSubTypeCount && Object.keys(trainingData.listSubTypeCount).length > 0 ? ( + <> +
+
+ {Object.entries(trainingData.listSubTypeCount).map(([key, value], index) => ( +
+ {key} + {value}家 +
+ ))} +
+
+ + ) : ( + // 默认显示(当没有数据时,保持原有3个的样子) +
+
+ 露天矿 + --家 +
+
+ 地下矿 + --家 +
+
+ 尾矿库 + --家 +
+
+ )} +
+
+
+
+ +
+
+
生产规模
+
+ {trainingData?.SumSubMode || '-'}吨/年 +
+
+
+
+
+ +
+
+
矿区面积
+
+ {trainingData?.SumSubArea || '-'}平方公里 +
+
+
+
+
+
+ +
+
@@ -958,12 +1031,6 @@ class HomeContent extends React.Component { - -
- -
-
-
{/* 公告详情弹窗 */} diff --git a/src/layout/FullScreenInter.js b/src/layout/FullScreenInter.js index 3a184d9..3be5407 100644 --- a/src/layout/FullScreenInter.js +++ b/src/layout/FullScreenInter.js @@ -14,6 +14,7 @@ import RiskControl from './FullOther/RiskControl'; import ClassBuild from './FullOther/ClassBuild'; import DangerJob from './FullOther/DangerJob'; import HiddenSolve from './FullOther/HiddenSolve'; +import moment from 'moment'; // 添加这一行 const getScale = () => { const width = 1920, @@ -30,7 +31,7 @@ class FullScreen extends React.Component { nowDate: '', riskTypeRate: [], riskTotal: [], - jobTodayQty: [], + // jobTodayQty: [], hiddenSummary: [], taskTop3: [], scale: getScale(), @@ -77,8 +78,11 @@ class FullScreen extends React.Component { riskSubData: {}, classSubData: [], dangerSubData: {}, + jobTodayQty: [], dangerCompanyData: [], selectedDangerCompany: '', + selectedStartDate: moment(), + selectedEndDate: moment(), announcementDetail: null, announcementDetailLoading: false, @@ -289,7 +293,7 @@ class FullScreen extends React.Component { if (ret && !this.isUnmounted) { this.setState({ riskTypeRate: ret.riskTypeRate || [], - jobTodayQty: ret.jobTodayQty || [], + // jobTodayQty: ret.jobTodayQty || [], hiddenSummary: ret.hiddenSummary || [], taskTop3: ret.taskTop3 || [], }); @@ -308,16 +312,7 @@ class FullScreen extends React.Component { onComplete: (ret) => { if (ret && !this.isUnmounted) { this.setState({ - trainingData: { - listNAME: ret.listNAME || [], - YearCount: ret.YearCount || [], - MonthRecordCount: ret.MonthRecordCount || [], - MonthPersonCount: ret.MonthPersonCount || [], - listAnnourcement: ret.listAnnourcement || [], - TITLE: ret.TITLE || '', - listVideoImg: ret.listVideoImg || [], - playSet: ret.playSet || [], - }, + trainingData: ret, }); } }, @@ -598,17 +593,29 @@ class FullScreen extends React.Component { this.getDangerSubData(); }); }; + handleStartDateChange = (startDate) => { + this.setState({ selectedStartDate: startDate }, () => { + this.getDangerSubData(); + }); + }; + handleEndDateChange = (endDate) => { + this.setState({ selectedEndDate: endDate }, () => { + this.getDangerSubData(); + }); + }; getDangerSubData = () => { const orgId = storage('lacal').getItem('webOrgId')?.val; const json = initFilter(orgId); + json.Parameter1 = this.state.selectedStartDate.format('YYYY-MM-DD'); + json.Parameter2 = this.state.selectedEndDate.format('YYYY-MM-DD'); this.props.dispatch({ type: 'app/getDataByPost', payload: json, url: 'BI/BIKanBanController/JobManage', onComplete: (ret) => { if (ret && !this.isUnmounted) { - let filteredData = ret; + let filteredData = ret.jobList; if (this.state.selectedDangerCompany) { const selectedCompanyObj = this.state.dangerCompanyData?.find( (company) => company.ID === this.state.selectedDangerCompany @@ -621,6 +628,7 @@ class FullScreen extends React.Component { this.setState({ dangerSubData: filteredData, + jobTodayQty: ret.jobTodayQty, }); } }, @@ -679,9 +687,14 @@ class FullScreen extends React.Component { return ( ); } @@ -770,7 +783,7 @@ class FullScreen extends React.Component { riskTypeRate={riskTypeRate} riskTotal={riskTotal} hiddenSummary={hiddenSummary} - jobTodayQty={jobTodayQty} + // jobTodayQty={jobTodayQty} taskTop3={taskTop3} mediaList={mediaList} announcementList={announcementList} diff --git a/src/layout/fullinter.less b/src/layout/fullinter.less index acb7f3f..72bd5ae 100644 --- a/src/layout/fullinter.less +++ b/src/layout/fullinter.less @@ -162,6 +162,277 @@ flex-direction: column; 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 { diff --git a/src/routes/Home.js b/src/routes/Home.js index ea8c55a..189eaf7 100644 --- a/src/routes/Home.js +++ b/src/routes/Home.js @@ -2,7 +2,17 @@ import React, { useState, useEffect, useRef, Component } from 'react'; import { connect } from 'dva'; import storage from '../utils/storage'; 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 { @@ -28,6 +38,7 @@ import FormPage from '../components/FormPage'; import backlog from '../assets/home/backlog.png'; import backlog_i from '../assets/home/backlog_icon.png'; import styles from './dashboard.css'; +import styleshome from './annourcement.less'; import 'slick-carousel/slick/slick.css'; import 'slick-carousel/slick/slick-theme.css'; import moment from 'moment'; @@ -182,6 +193,13 @@ class Home extends React.Component { }, taskSelects: [], mineType: localStorage.getItem('currentMineType') === '20' ? '10' : localStorage.getItem('currentMineType'), + annourcement: [], + announcementDetail: null, // 公告详情 + announcementModalVisible: false, // 公告弹窗可见性 + announcementDetailLoading: false, // 公告详情加载状态 + fileForm: { + visible: false, + }, }; // this.enums = []; this.reportType = ''; @@ -618,20 +636,20 @@ class Home extends React.Component { }; //风险等级占比 riskLevel = () => { - const userInfo = storage('lacal').getItem('webUserInfo').val; let json = initFilter(this.props.login.OrgId); extendOrder(json, 'NUM', 0); - extendRule(json, 'COLOR', 1, this.state.mineType); + extendOrder(json, 'CREATE_TIME', 1); this.props.dispatch({ type: 'app/getDataByPost', - url: 'FM/RiskLevelProportion/OrderPaged', + url: 'PF/PFAnnourcement/OrderPaged', payload: json, onlyData: false, onComplete: (data) => { if (data && data.IsSuccessful) { if (data.Data && data.Data.length > 0) { + console.log('风险等级占比', data.Data); this.setState({ - riskLevelPage: data.Data, + annourcement: data.Data, }); } } @@ -1369,9 +1387,141 @@ class Home extends React.Component { 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 ( +
+ +
+ ); + } + + if (!announcementDetail) return null; + + const { TITLE, ABSTRACT, START, END, CONTENT, CREATE_USER_NAME, Nav_Files = [] } = announcementDetail; + + return ( +
+ {/* 标题 */} +
+ {TITLE} +
+ + {/* 摘要 */} + {ABSTRACT && ( +
+ {ABSTRACT} +
+ )} + + {/* 日期和发布人 */} +
+ {START ? START.split(' ')[0] : '--'} 至 {END ? END.split(' ')[0] : '--'} + {CREATE_USER_NAME && 发布人:{CREATE_USER_NAME}} +
+ + {/* 分割线 */} +
+ + {/* 正文内容 */} +
暂无内容
' }} + /> + + {/* 附件列表 */} + {Nav_Files && Nav_Files.length > 0 && ( +
+
+ 附件 ({Nav_Files.length}个) +
+ {showFiles(Nav_Files, config.picServerHost, this)} + {GetFileModel(Modal, FormPage, this, this.state.fileForm.visible)} +
+ )} +
+ ); + }; render() { - const { tableData } = this.state; + const { tableData, annourcement } = this.state; const thingsData = this.state.delayData; const settings = { dots: false, @@ -1431,6 +1581,20 @@ class Home extends React.Component { size="small" /> + + 关闭 + , + ]} + width="600px" + bodyStyle={{ padding: '20px', maxHeight: '70vh', overflowY: 'auto' }} + > + {this.renderAnnouncementModal()} +
-
-
- - 风险等级占比 +
+
+
+ + 公司公告 +
+ 共 {annourcement?.length || 0} 条公告
- {this.state.riskLevelPage.length > 0 ? ( - -
this.showDetailModal('BI013_RISKANALYSISMODEL')}> - +
+ {annourcement?.length > 0 ? ( +
    + {annourcement.map((item, index) => ( +
  • this.handleAnnouncementClick(item)} + > + + {item.TITLE} + + {item.START} +
  • + ))} +
+ ) : ( +
+ + 暂无公告
- - ) : null} + )} +
- {this.state.riskLevelPage.length > 0 ? : null}
diff --git a/src/routes/annourcement.less b/src/routes/annourcement.less new file mode 100644 index 0000000..07e34a0 --- /dev/null +++ b/src/routes/annourcement.less @@ -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; +}