// 项目管理分析页面 let layer; let expenditureTrendChart = null; let expenditureProportionChart = null; let projectRiskChart = null; let riskAnalysisChart = null; let civilSpecialtyChart = null; let electricalSpecialtyChart = null; let bidCode = parent.parent.$('#bidPro').val(); // 获取当天日期 function getTodayDate() { const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); return year + '-' + month + '-' + day; } // 获取当月第一天和最后一天(保留用于其他功能) function getCurrentMonthRange() { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth(); const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const startDate = formatDate(firstDay); const endDate = formatDate(lastDay); return { startDate, endDate }; } const today = getTodayDate(); let queryParams = { projectId: bidCode, startTime: today, endTime: today, }; // 调试信息:确认日期范围已设置为当天 console.log('日期范围已设置为当天:', queryParams.startTime, '~', queryParams.endTime); // 页面初始化 $(document).ready(function () { // 等待layui加载完成 layui.use(['laydate', 'layer'], function () { layer = layui.layer; initDateRange(); // 初始化项目信息模块的图表 initRiskAnalysisChart(); initCivilSpecialtyChart(); initElectricalSpecialtyChart(); // 初始化其他模块的图表 initExpenditureTrendChart(); initExpenditureProportionChart(); initProjectRiskChart(); initWarningTable(); initFileUploadHandler(); // 页面初始化时,按默认当天日期加载一次接口数据 refreshAllModules(); // 窗口大小改变时重新调整图表 window.addEventListener('resize', debounce(() => { if (riskAnalysisChart) riskAnalysisChart.resize(); if (civilSpecialtyChart) civilSpecialtyChart.resize(); if (electricalSpecialtyChart) electricalSpecialtyChart.resize(); if (expenditureTrendChart) expenditureTrendChart.resize(); if (expenditureProportionChart) expenditureProportionChart.resize(); if (projectRiskChart) projectRiskChart.resize(); }, 300)); }); }); // 防抖函数 function debounce(fn, delay) { let t = null; return function () { clearTimeout(t); t = setTimeout(() => fn.apply(this, arguments), delay); }; } // 获取左上角工序进度 function getProcessProgress() { const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProProgress' + '?bidCode=' + (bidCode || '') + '&startTime=' + (queryParams.startTime || '') + '&endTime=' + (queryParams.endTime || ''); ajaxRequestGet(url, 'GET', true, function () { }, function (result) { if (result && result.data) { updateProcessProgress(result.data); } else if (result) { updateProcessProgress(result); } }, aqEnnable); } // 获取左上角风险分析饼图数据 function getRiskAnalysisChartData() { const url = commonUrl + 'screen/largeScreen/sjNewProManage/getRiskData' + '?bidCode=' + (bidCode || '') + '&startTime=' + (queryParams.startTime || '') + '&endTime=' + (queryParams.endTime || ''); ajaxRequestGet(url, 'GET', true, function () { }, function (result) { if (result) { updateRiskAnalysisChart(result.data); } else if (result && result.data) { updateRiskAnalysisChart(result.data); } }, aqEnnable); } // 获取右上侧折线图数据 // 获取土建和电气专业图表数据 function getLineChartData() { const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProgressList' + '?bidCode=' + (bidCode || '') + '&startTime=' + (queryParams.startTime || '') + '&endTime=' + (queryParams.endTime || ''); ajaxRequestGet(url, 'GET', true, function () { }, function (result) { if (result && result.data) { updateSpecialtyCharts(result.data); } else if (result) { updateSpecialtyCharts(result); } }, aqEnnable); } // 获取项目支出趋势和项目支出占比 function getExpenditureTrendAndProportion() { const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProjectCost' + '?bidCode=' + (bidCode || '') + '&startTime=' + (queryParams.startTime || '') + '&endTime=' + (queryParams.endTime || ''); ajaxRequestGet(url, 'GET', true, function () { }, function (result) { if (result && result.data) { updateExpenditureTrendChart(result.data); updateExpenditureProportionChart(result.data); } else if (result) { updateExpenditureTrendChart(result); updateExpenditureProportionChart(result); } }, aqEnnable); } // 获取项目风险饼图 function getProjectRiskChartData() { const url = commonUrl + 'screen/largeScreen/sjNewProManage/getProjectRisk' + '?bidCode=' + (bidCode || '') + '&startTime=' + (queryParams.startTime || '') + '&endTime=' + (queryParams.endTime || ''); ajaxRequestGet(url, 'GET', true, function () { }, function (result) { if (result && result.data) { updateProjectRiskChart(result.data); } else if (result) { updateProjectRiskChart(result.data); } }, aqEnnable); } // 获取分析预警 function getAnalysisWarning(txType) { // txType: "全部"传空字符串,其他传对应的中文 const txTypeParam = txType === 'all' ? '' : (txType === 'progress' ? '进度' : txType === 'cost' ? '成本' : txType === 'risk' ? '风险' : ''); const url = commonUrl + 'screen/largeScreen/sjNewProManage/getWarnList' + '?bidCode=' + (bidCode || '') + '&startTime=' + (queryParams.startTime || '') + '&endTime=' + (queryParams.endTime || '') + (txTypeParam ? '&txType=' + txTypeParam : ''); ajaxRequestGet(url, 'GET', true, function () { }, function (result) { console.log(result, '分析预警'); if (result && Array.isArray(result)) { updateWarningTable(result); } else if (result && result.data && Array.isArray(result.data)) { updateWarningTable(result.data); } else if (result && result.rows && Array.isArray(result.rows)) { updateWarningTable(result.rows); } }, aqEnnable); } // 更新工序进度数据 function updateProcessProgress(data) { if (!data) return; // 土建工序进度 // bdGx: 本段工序(土建总工序) // bdYwcGx: 本段已完成工序(土建已完成) const civilTotal = data.bdGx || '0'; const civilCompleted = data.bdYwcGx || '0'; $('#civilTotal').text(civilTotal); $('#civilCompleted').text(civilCompleted); // 电气工序进度 // dqGx: 当前工序(电气总工序) // dqYwcGx: 当前已完成工序(电气已完成) const electricalTotal = data.dqGx || '0'; const electricalCompleted = data.dqYwcGx || '0'; $('#electricalTotal').text(electricalTotal); $('#electricalCompleted').text(electricalCompleted); } // 刷新所有模块数据 function refreshAllModules() { // 调用工序进度接口 getProcessProgress(); // 调用风险分析饼图数据接口 getRiskAnalysisChartData(); // 调用土建和电气专业图表数据接口 getLineChartData(); // 调用项目支出趋势和项目支出占比接口 getExpenditureTrendAndProportion(); // 调用项目风险饼图接口 getProjectRiskChartData(); // 调用分析预警接口 getAnalysisWarning(currentWarningFilter); } // 初始化日期范围选择器(逻辑与工程质量分析保持一致) function initDateRange() { const laydate = layui.laydate; // 初始显示为当天范围 $('#dateRange').val(queryParams.startTime + ' ~ ' + queryParams.endTime); laydate.render({ elem: '#dateRange', type: 'date', range: true, format: 'yyyy-MM-dd', theme: 'dark', // laydate 内部使用 "起始 - 结束" 作为分隔符 value: queryParams.startTime + ' - ' + queryParams.endTime, done: function (value) { const resetToToday = function () { const today = getTodayDate(); queryParams.startTime = today; queryParams.endTime = today; $('#dateRange').val(today + ' ~ ' + today); refreshAllModules(); }; if (value && value.trim() !== '') { const dates = value.split(' - '); if (dates.length === 2) { const startDate = dates[0].trim(); const endDateStr = dates[1].trim(); // 回显到输入框(使用 ~,与质量分析一致) $('#dateRange').val(startDate + ' ~ ' + endDateStr); // 更新查询参数 queryParams.startTime = startDate; queryParams.endTime = endDateStr; // 日期变化后,重新拉取接口数据 refreshAllModules(); } else { resetToToday(); } } else { resetToToday(); } } }); } // 格式化日期 function formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return year + '-' + month + '-' + day; } // 查询数据 function queryData() { const dateRange = $('#dateRange').val(); if (!dateRange) { layui.layer.msg('请选择日期范围', { icon: 0 }); return; } // 点击查询按钮时,基于当前日期范围刷新接口数据 refreshAllModules(); layui.layer.msg('查询成功', { icon: 1, time: 1000 }); } // 加载数据 function loadData(dateRange) { // 模拟数据加载 // 实际应该调用API updateCharts(); } // 初始化风险分析饼图 function initRiskAnalysisChart() { riskAnalysisChart = echarts.init(document.getElementById('riskAnalysisChart')); const option = { tooltip: { trigger: 'item', backgroundColor: 'rgba(19, 51, 55, 0.9)', borderColor: 'rgba(0, 254, 252, 0.5)', borderWidth: 1, textStyle: { color: '#fff' }, formatter: '{b}: {c}个 ({d}%)' }, legend: { orient: 'vertical', right: 10, top: 'center', textStyle: { color: '#fff', fontSize: 12 }, itemWidth: 12, itemHeight: 8, itemGap: 10, formatter: function (name) { // 从图表实例中动态获取最新数据 if (!riskAnalysisChart) return name; const option = riskAnalysisChart.getOption(); if (option && option.series && option.series[0] && option.series[0].data) { const item = option.series[0].data.find(d => d.name === name); return name + ' ' + (item ? item.value : '0'); } return name + ' 0'; } }, series: [ { name: '风险分析', type: 'pie', radius: ['40%', '70%'], center: ['35%', '50%'], avoidLabelOverlap: false, itemStyle: { borderRadius: 5, borderColor: 'rgba(13, 34, 37, 0.8)', borderWidth: 2 }, label: { show: false }, emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold', color: '#fff' } }, data: [ { value: 0, name: '二级风险', itemStyle: { color: '#4A90E2' } }, { value: 0, name: '三级风险', itemStyle: { color: '#1CFFA3' } }, { value: 0, name: '四级风险', itemStyle: { color: '#00FEFC' } }, { value: 0, name: '五级风险', itemStyle: { color: '#FF9C65' } } ] } ], graphic: [ { type: 'text', left: '50%', top: '5%', style: { text: '今日风险数量 0个', textAlign: 'center', fill: '#fff', fontSize: 13 } } ] }; riskAnalysisChart.setOption(option); } // 更新风险分析饼图数据 function updateRiskAnalysisChart(data) { if (!riskAnalysisChart || !data) return; // 获取各风险等级的数量 const twoRisk = parseInt(data.twoRisk || 0); const threeRisk = parseInt(data.threeRisk || 0); const fourRisk = parseInt(data.fourRisk || 0); const fiveRisk = parseInt(data.fiveRisk || 0); // 计算总风险数量 const totalRisk = twoRisk + threeRisk + fourRisk + fiveRisk; // 计算百分比 let twoRiskPercent = 0; let threeRiskPercent = 0; let fourRiskPercent = 0; let fiveRiskPercent = 0; if (totalRisk > 0) { twoRiskPercent = (twoRisk / totalRisk * 100).toFixed(1); threeRiskPercent = (threeRisk / totalRisk * 100).toFixed(1); fourRiskPercent = (fourRisk / totalRisk * 100).toFixed(1); fiveRiskPercent = (fiveRisk / totalRisk * 100).toFixed(1); } // 更新图表数据 riskAnalysisChart.setOption({ series: [ { data: [ { value: twoRisk, name: '二级风险', itemStyle: { color: '#4A90E2' } }, { value: threeRisk, name: '三级风险', itemStyle: { color: '#1CFFA3' } }, { value: fourRisk, name: '四级风险', itemStyle: { color: '#00FEFC' } }, { value: fiveRisk, name: '五级风险', itemStyle: { color: '#FF9C65' } } ] } ], graphic: [ { type: 'text', left: '60%', top: '2%', style: { text: '今日风险数量 ' + totalRisk + '个', textAlign: 'center', fill: '#fff', fontSize: 13 } } ] }); } // 初始化土建专业双柱状图 function initCivilSpecialtyChart() { civilSpecialtyChart = echarts.init(document.getElementById('civilSpecialtyChart')); const option = { tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, backgroundColor: 'rgba(19, 51, 55, 0.9)', borderColor: 'rgba(0, 254, 252, 0.5)', borderWidth: 1, textStyle: { color: '#fff' }, formatter: function (params) { if (!params || !params.length) return ''; let html = `
${params[0].name}
`; params.forEach(p => { html += `
${p.seriesName} ${p.value}%
`; }); return html; } }, legend: { data: ['计划进度', '实际进度'], top: 8, right: 20, textStyle: { color: '#fff', fontSize: 12 }, itemWidth: 12, itemHeight: 8, itemGap: 15 }, grid: { top: '20%', left: '8%', right: '6%', bottom: '15%' }, xAxis: { type: 'category', data: [], axisLabel: { color: '#fff', fontSize: 10, rotate: 0 }, axisLine: { lineStyle: { color: '#5A6E71' } }, axisTick: { show: false } }, yAxis: { type: 'value', min: 0, max: 100, axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}%' }, axisLine: { lineStyle: { color: '#5A6E71' } }, splitLine: { lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' } } }, series: [ { name: '计划进度', type: 'bar', data: [], itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#4A90E2' }, { offset: 1, color: '#357ABD' } ]) }, barWidth: '30' }, { name: '实际进度', type: 'bar', data: [], itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#1CFFA3' }, { offset: 1, color: '#19CC8A' } ]) }, barWidth: '30' } ] }; civilSpecialtyChart.setOption(option); } // 初始化电气专业双柱状图 function initElectricalSpecialtyChart() { electricalSpecialtyChart = echarts.init(document.getElementById('electricalSpecialtyChart')); const option = { tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, backgroundColor: 'rgba(19, 51, 55, 0.9)', borderColor: 'rgba(0, 254, 252, 0.5)', borderWidth: 1, textStyle: { color: '#fff' }, formatter: function (params) { if (!params || !params.length) return ''; let html = `
${params[0].name}
`; params.forEach(p => { html += `
${p.seriesName} ${p.value}%
`; }); return html; } }, legend: { data: ['计划进度', '实际进度'], top: 8, right: 20, textStyle: { color: '#fff', fontSize: 12 }, itemWidth: 12, itemHeight: 8, itemGap: 15 }, grid: { top: '20%', left: '8%', right: '6%', bottom: '15%' }, xAxis: { type: 'category', data: [], axisLabel: { color: '#fff', fontSize: 10, rotate: 0 }, axisLine: { lineStyle: { color: '#5A6E71' } }, axisTick: { show: false } }, yAxis: { type: 'value', min: 0, max: 100, axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}%' }, axisLine: { lineStyle: { color: '#5A6E71' } }, splitLine: { lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' } } }, series: [ { name: '计划进度', type: 'bar', data: [], itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#4A90E2' }, { offset: 1, color: '#357ABD' } ]) }, barWidth: '30' }, { name: '实际进度', type: 'bar', data: [], itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#1CFFA3' }, { offset: 1, color: '#19CC8A' } ]) }, barWidth: '30' } ] }; electricalSpecialtyChart.setOption(option); } // 更新土建和电气专业图表数据 function updateSpecialtyCharts(data) { if (!data) return; // 更新土建专业图表 if (civilSpecialtyChart) { const bdGx = data.bdGx || []; const bdPlanProgress = (data.bdPlanProgress || []).map(val => parseFloat(val) || 0); const bdProgress = (data.bdProgress || []).map(val => parseFloat(val) || 0); civilSpecialtyChart.setOption({ xAxis: { data: bdGx }, series: [ { name: '计划进度', data: bdPlanProgress }, { name: '实际进度', data: bdProgress } ] }); } // 更新电气专业图表 if (electricalSpecialtyChart) { const dqGx = data.dqGx || []; const dqPlanProgress = (data.dqPlanProgress || []).map(val => parseFloat(val) || 0); const dqProgress = (data.dqProgress || []).map(val => parseFloat(val) || 0); electricalSpecialtyChart.setOption({ xAxis: { data: dqGx }, series: [ { name: '计划进度', data: dqPlanProgress }, { name: '实际进度', data: dqProgress } ] }); } } // 初始化项目支出趋势图表 function initExpenditureTrendChart() { expenditureTrendChart = echarts.init(document.getElementById('expenditureTrendChart')); const dates = ['XXXX-XX-01', 'XXXX-XX-02', 'XXXX-XX-03', 'XXXX-XX-04', 'XXXX-XX-05', 'XXXX-XX-06', 'XXXX-XX-07', 'XXXX-XX-08']; const expenditure = [48, 45, 42, 32, 35, 38, 30, 28]; const option = { tooltip: { trigger: 'axis', axisPointer: { type: 'line' }, backgroundColor: 'rgba(19, 51, 55, 0.9)', borderColor: 'rgba(0, 254, 252, 0.5)', borderWidth: 1, textStyle: { color: '#fff' } }, grid: { top: '15%', left: '8%', right: '6%', bottom: '15%' }, xAxis: { type: 'category', data: dates, axisLabel: { color: '#fff', fontSize: 11 }, axisLine: { lineStyle: { color: '#5A6E71' } }, axisTick: { show: false } }, yAxis: { type: 'value', min: 0, max: 60, interval: 10, axisLabel: { color: '#fff', fontSize: 11 }, axisLine: { lineStyle: { color: '#5A6E71' } }, splitLine: { lineStyle: { color: 'rgba(90, 110, 113, 0.3)', type: 'dashed' } } }, series: [ { type: 'line', data: expenditure, smooth: true, lineStyle: { width: 2, color: '#00FEFC' }, itemStyle: { color: '#00FEFC' }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: 'rgba(0, 254, 252, 0.4)' }, { offset: 1, color: 'rgba(0, 254, 252, 0.05)' } ]) } } ] }; expenditureTrendChart.setOption(option); } // 初始化项目支出占比图表(环形图) function initExpenditureProportionChart() { expenditureProportionChart = echarts.init(document.getElementById('expenditureProportionChart')); const option = { tooltip: { trigger: 'item', backgroundColor: 'rgba(19, 51, 55, 0.9)', borderColor: 'rgba(0, 254, 252, 0.5)', borderWidth: 1, textStyle: { color: '#fff' }, formatter: '{b}: {d}%' }, legend: { bottom: 10, left: 'center', textStyle: { color: '#fff', fontSize: 12 }, itemWidth: 12, itemHeight: 8, itemGap: 15 }, series: [ { name: '支出占比', type: 'pie', radius: ['50%', '70%'], center: ['50%', '45%'], avoidLabelOverlap: false, itemStyle: { borderRadius: 5, borderColor: 'rgba(13, 34, 37, 0.8)', borderWidth: 2 }, label: { show: true, formatter: '{b}\n{d}%', color: '#fff', fontSize: 12, fontWeight: 'bold' }, emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold' } }, data: [ { value: 0, name: '支出', itemStyle: { color: '#00FEFC' } }, { value: 0, name: '预算剩余', itemStyle: { color: '#FFFFFF' } } ] } ] }; expenditureProportionChart.setOption(option); } // 更新项目支出趋势图表 function updateExpenditureTrendChart(data) { if (!expenditureTrendChart || !data) return; // 格式化时间列表:从 "2026-01-01" 转换为 "01-01" const timeList = (data.timeList || []).map(dateStr => { if (!dateStr) return ''; const parts = dateStr.split('-'); if (parts.length >= 2) { return parts[1] + '-' + parts[2]; } return dateStr; }); // 转换数值列表为数字 const valueList = (data.valueList || []).map(val => parseFloat(val) || 0); // 确保两个数组长度一致,取最小长度 const minLength = Math.min(timeList.length, valueList.length); const finalTimeList = timeList.slice(0, minLength); const finalValueList = valueList.slice(0, minLength); // 计算最大值,用于设置y轴范围 const maxValue = finalValueList.length > 0 ? Math.max(...finalValueList, 0) : 0; const yAxisMax = maxValue > 0 ? Math.ceil(maxValue * 1.2) : 100; expenditureTrendChart.setOption({ xAxis: { data: finalTimeList }, yAxis: { max: yAxisMax, interval: Math.ceil(yAxisMax / 5) }, series: [ { data: finalValueList } ] }); } // 更新项目支出占比图表 function updateExpenditureProportionChart(data) { if (!expenditureProportionChart || !data) return; // allCost 是预算,proCount 是支出 const budget = parseFloat(data.proCount || 0); const expenditure = parseFloat(data.allCost || 0); const remaining = budget - expenditure; // 计算百分比 let expenditurePercent = 0; let remainingPercent = 0; if (budget > 0) { expenditurePercent = (expenditure / budget * 100).toFixed(1); remainingPercent = (remaining / budget * 100).toFixed(1); } expenditureProportionChart.setOption({ series: [ { data: [ { value: parseFloat(expenditurePercent), name: '支出', itemStyle: { color: '#00FEFC' } }, { value: parseFloat(remainingPercent), name: '预算剩余', itemStyle: { color: '#FFFFFF' } } ] } ] }); } // 初始化项目风险图表 function initProjectRiskChart() { projectRiskChart = echarts.init(document.getElementById('projectRiskChart')); const option = { tooltip: { trigger: 'item', backgroundColor: 'rgba(19, 51, 55, 0.9)', borderColor: 'rgba(0, 254, 252, 0.5)', borderWidth: 1, textStyle: { color: '#fff' }, formatter: function (params) { // params.data 包含数据项的所有信息,包括自定义属性 const actualCount = params.data.actualCount || 0; const percent = params.percent || 0; return params.name + ': ' + actualCount + '个 (' + percent.toFixed(1) + '%)'; } }, legend: { orient: 'vertical', right: 10, top: 'center', textStyle: { color: '#fff', fontSize: 12 }, itemWidth: 12, itemHeight: 8, itemGap: 10, formatter: function (name) { // 获取当前图表的数据 const currentOption = projectRiskChart.getOption(); const seriesData = currentOption.series && currentOption.series[0] && currentOption.series[0].data; if (seriesData) { const item = seriesData.find(d => d.name === name); if (item) { const actualCount = item.actualCount || 0; const percent = item.value || 0; return name + ' ' + actualCount + '个 ' + percent.toFixed(1) + '%'; } } return name; } }, series: [ { name: '项目风险', type: 'pie', roseType: 'area', radius: ['10%', '70%'], center: ['35%', '45%'], avoidLabelOverlap: false, itemStyle: { borderRadius: 5, borderColor: 'rgba(13, 34, 37, 0.8)', borderWidth: 2 }, label: { show: false }, emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold', color: '#fff' }, itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 254, 252, 0.5)' } }, data: [ { value: 0, actualCount: 0, name: '二级风险', itemStyle: { color: '#4A90E2' } }, { value: 0, actualCount: 0, name: '三级风险', itemStyle: { color: '#1CFFA3' } }, { value: 0, actualCount: 0, name: '四级风险', itemStyle: { color: '#00FEFC' } }, { value: 0, actualCount: 0, name: '五级风险', itemStyle: { color: '#FF9C65' } } ] } ] }; projectRiskChart.setOption(option); } // 更新项目风险图表 function updateProjectRiskChart(data) { if (!projectRiskChart || !data) return; // 获取各风险等级的数量 const twoRisk = parseInt(data.twoRisk || 0); const threeRisk = parseInt(data.threeRisk || 0); const fourRisk = parseInt(data.fourRisk || 0); const fiveRisk = parseInt(data.fiveRisk || 0); // 计算总风险数量(用于显示) const allRisk = parseInt(data.allRisk || 0); // 计算各风险等级的总数(用于计算百分比) const totalRiskCount = twoRisk + threeRisk + fourRisk + fiveRisk; // 计算百分比 let twoRiskPercent = 0; let threeRiskPercent = 0; let fourRiskPercent = 0; let fiveRiskPercent = 0; if (totalRiskCount > 0) { twoRiskPercent = (twoRisk / totalRiskCount * 100).toFixed(1); threeRiskPercent = (threeRisk / totalRiskCount * 100).toFixed(1); fourRiskPercent = (fourRisk / totalRiskCount * 100).toFixed(1); fiveRiskPercent = (fiveRisk / totalRiskCount * 100).toFixed(1); } // 更新图表数据,同时保存实际数量和百分比 projectRiskChart.setOption({ series: [ { data: [ { value: parseFloat(twoRiskPercent), actualCount: twoRisk, name: '二级风险', itemStyle: { color: '#4A90E2' } }, { value: parseFloat(threeRiskPercent), actualCount: threeRisk, name: '三级风险', itemStyle: { color: '#1CFFA3' } }, { value: parseFloat(fourRiskPercent), actualCount: fourRisk, name: '四级风险', itemStyle: { color: '#00FEFC' } }, { value: parseFloat(fiveRiskPercent), actualCount: fiveRisk, name: '五级风险', itemStyle: { color: '#FF9C65' } } ] }, ], graphic: [ { type: 'text', left: '60%', top: '5%', style: { text: '总风险数量 ' + allRisk + '个', textAlign: 'center', fill: '#fff', fontSize: 16 } } ] }); // 更新总风险数量显示 $('#totalRiskDisplay').text('总风险数量:' + allRisk + '个'); // 更新右侧统计数据 const xhRisk = data.xhRisk || '0'; const lastRiskNum = data.lastRiskNum || '0'; const lastTwoRisk = data.lastTwoRisk || '0'; $('#closedRisk').text(xhRisk); $('#remainingRisk').text(lastRiskNum); $('#remainingLevel2Risk').text(lastTwoRisk); } // 初始化分析预警表格 function initWarningTable() { // 初始化时调用接口获取数据 getAnalysisWarning(currentWarningFilter); } // 更新分析预警表格数据 function updateWarningTable(data) { if (!data || !Array.isArray(data)) { $('#warningTableBody').html('暂无预警'); return; } renderWarningTable(data); } // 渲染分析预警表格 function renderWarningTable(warnings) { const tbody = $('#warningTableBody'); tbody.empty(); if (!warnings || warnings.length === 0) { tbody.html('暂无预警'); return; } warnings.forEach((warning, index) => { // 根据接口返回的字段映射 const serialNumber = (index + 1).toString().padStart(2, '0'); const warningId = warning.id || warning.warnId || ''; const time = warning.txTime || warning.warnTime || warning.time || ''; const type = warning.txType || warning.warnType || warning.type || ''; const content = warning.content || warning.warnContent || ''; const action = warning.action || '制定策略'; // 将完整的 warning 对象数据编码后传递 const warningData = encodeURIComponent(JSON.stringify({ id: warningId, measureData: warning.measureData || '', filePath: warning.filePath || '' })); const row = ` ${serialNumber} ${time} ${type} ${content} ${action} `; tbody.append(row); }); } // 筛选分析预警 let currentWarningFilter = 'all'; function filterWarning(type) { currentWarningFilter = type; // 更新标签状态 $('.warning-tab').removeClass('active'); $(`.warning-tab[data-type="${type}"]`).addClass('active'); // 重新调用接口获取筛选后的数据 getAnalysisWarning(type); } // 更新图表 function updateCharts() { // 这里可以重新加载图表数据 // 实际应该从API获取数据并更新图表 if (riskAnalysisChart) riskAnalysisChart.resize(); if (civilSpecialtyChart) civilSpecialtyChart.resize(); if (electricalSpecialtyChart) electricalSpecialtyChart.resize(); if (expenditureTrendChart) expenditureTrendChart.resize(); if (expenditureProportionChart) expenditureProportionChart.resize(); if (projectRiskChart) projectRiskChart.resize(); } // 制定策略相关变量 let currentWarningId = null; let selectedFile = null; let originalFilePath = null; // 保存原始文件路径 // 打开制定策略弹框 function formulateStrategy(warningDataStr) { try { // 解析传递过来的 warning 数据 const warningData = JSON.parse(decodeURIComponent(warningDataStr)); currentWarningId = warningData.id || ''; selectedFile = null; originalFilePath = warningData.filePath || null; // 回显应对措施 const measureData = warningData.measureData || ''; $('#measureDataInput').val(measureData); // 回显文件路径 $('#fileInput').val(''); if (originalFilePath) { // 从文件路径中提取文件名 const fileName = originalFilePath.split('/').pop() || originalFilePath.split('\\').pop() || '已上传文件'; $('#fileInfo').html('已上传文件: ' + fileName + ' (点击上传文件可替换)'); } else { $('#fileInfo').text(''); } // 显示弹框 $('#strategyModal').addClass('show'); } catch (e) { console.error('解析预警数据失败:', e); // 如果解析失败,使用旧的方式(兼容性处理) currentWarningId = warningDataStr; selectedFile = null; originalFilePath = null; $('#measureDataInput').val(''); $('#fileInput').val(''); $('#fileInfo').text(''); $('#strategyModal').addClass('show'); } } // 关闭制定策略弹框 function closeStrategyModal() { $('#strategyModal').removeClass('show'); currentWarningId = null; selectedFile = null; originalFilePath = null; // 重置表单 $('#measureDataInput').val(''); $('#fileInput').val(''); $('#fileInfo').text(''); } // 初始化文件选择处理(在 layui 初始化后) function initFileUploadHandler() { $('#fileInput').on('change', function (e) { const file = e.target.files[0]; if (file) { // 检查文件大小(20MB = 20 * 1024 * 1024 字节) const maxSize = 20 * 1024 * 1024; if (file.size > maxSize) { if (layer) { layer.msg('文件大小不能超过20MB', { icon: 2 }); } else { alert('文件大小不能超过20MB'); } $(this).val(''); selectedFile = null; $('#fileInfo').text(''); return; } // 检查文件格式 const allowedExtensions = ['.doc', '.docx', '.pdf', '.jpg', '.png']; const fileName = file.name.toLowerCase(); const isValidFormat = allowedExtensions.some(ext => fileName.endsWith(ext)); if (!isValidFormat) { if (layer) { layer.msg('不支持的文件格式,请上传 .doc .docx .pdf .jpg .png 格式的文件', { icon: 2 }); } else { alert('不支持的文件格式,请上传 .doc .docx .pdf .jpg .png 格式的文件'); } $(this).val(''); selectedFile = null; $('#fileInfo').text(''); return; } selectedFile = file; // 如果选择了新文件,清除原始文件路径标记 originalFilePath = null; $('#fileInfo').html('已选择文件: ' + file.name + ' (' + formatFileSize(file.size) + ')'); } }); } // 格式化文件大小 function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } // 提交制定策略 function submitStrategy() { const measureData = $('#measureDataInput').val().trim(); // 验证应对措施必填 if (!measureData) { layer.msg('请输入应对措施', { icon: 2 }); return; } // 构建 FormData const formData = new FormData(); formData.append('measureData', measureData); // 传递预警ID(必传字段) formData.append('id', currentWarningId || ''); // 如果有新选择的文件,添加到 FormData if (selectedFile) { formData.append('file', selectedFile); } else if (originalFilePath) { // 如果没有选择新文件,但有原始文件路径,传递原始文件路径(如果需要保留原文件) // 注意:根据后端接口需求,可能需要传递 filePath 字段 formData.append('filePath', originalFilePath); } // 发送 POST 请求 const url = commonUrl + 'screen/largeScreen/sjNewProManage/uploadFile'; $.ajax({ url: url, type: 'POST', headers: { "authorization": sessionStorage.getItem("zhgd_token"), "decrypt": 'decrypt' }, data: formData, processData: false, contentType: false, beforeSend: function () { layer.load(2); }, success: function (result) { layer.closeAll('loading'); // 由于使用了 ajaxSetup 拦截器,result 已经被 modifyResponseData 处理过了 const response = result; if (response && (response.code === 200 || response.success || response.status === 200)) { layer.msg('提交成功', { icon: 1 }); closeStrategyModal(); // 刷新预警列表 getAnalysisWarning(currentWarningFilter); } else { layer.msg(response.msg || response.message || '提交失败,请重试', { icon: 2 }); } }, error: function (xhr, status, error) { layer.closeAll('loading'); layer.msg('提交失败,请检查网络连接', { icon: 2 }); console.error('提交失败:', error); } }); }