/** * 资源利用率分析页面 - API版本 * 使用真实的后端API接口获取数据 */ let table, layer, form; let utilizationChart = null, trendsChart = null; let isAnimating = false; let lastUpdateTime = ''; // 页面初始化 layui.use(['layer', 'table', 'form'], function () { layer = layui.layer; table = layui.table; form = layui.form; // 初始化粒子背景 initParticles(); // 加载数据 loadPageData(); }); /** * 加载页面数据 */ async function loadPageData() { try { // 显示加载动画 showLoading(); // 并行加载所有必要数据 const [overviewData, detailsData, trendsData, distributionData, suggestionsData] = await Promise.all([ resourceApiService.getResourceOverview(), resourceApiService.getResourceDetails(), resourceApiService.getResourceTrends(), resourceApiService.getResourceDistribution(), resourceApiService.getOptimizationSuggestions() ]); // 初始化页面数据 initPageData(overviewData, detailsData, suggestionsData); // 初始化图表 initCharts(distributionData, trendsData); // 隐藏加载动画 hideLoading(); // 启动数据更新定时器 startDataRefresh(); console.log('页面数据加载完成'); } catch (error) { console.error('页面数据加载失败:', error); hideLoading(); // 显示错误提示 layer.msg('数据加载失败,请刷新页面重试', {icon: 2}); } } /** * 显示加载动画 */ function showLoading() { $('#loadingOverlay').show(); } /** * 隐藏加载动画 */ function hideLoading() { $('#loadingOverlay').fadeOut(500); } /** * 初始化页面数据 */ function initPageData(overviewData, detailsData, suggestionsData) { // 设置头部统计数据 if (overviewData && overviewData.overallStats) { const stats = overviewData.overallStats; $('#totalRate').text(stats.totalRate.toFixed(1) + '%'); $('#personnelRate').text(stats.personnelRate.toFixed(1) + '%'); $('#equipmentRate').text(stats.equipmentRate.toFixed(1) + '%'); $('#materialRate').text(stats.materialRate.toFixed(1) + '%'); } // 设置资源概览数据 if (overviewData && overviewData.resourceOverview) { const overview = overviewData.resourceOverview; $('#personnelCount').text(overview.personnel.count); $('#equipmentCount').text(overview.equipment.count); $('#materialCount').text(overview.material.count); $('#energyCount').text(overview.energy.count + '%'); } // 更新资源详细表格 if (detailsData && detailsData.list) { updateResourceTable(detailsData.list); } // 更新优化建议 if (suggestionsData) { updateOptimizationSuggestions(suggestionsData); } } /** * 更新资源详细表格 */ function updateResourceTable(resourceList) { const tableBody = $('#resourceTableBody'); tableBody.empty(); resourceList.forEach((item, index) => { const assessmentClass = getAssessmentClass(item.assessment); const statusClass = getStatusClass(item.status); const row = ` ${item.type} ${item.name} ${translateStatus(item.status)} ${item.rate}% ${item.assessment} `; tableBody.append(row); }); } /** * 更新优化建议 */ function updateOptimizationSuggestions(suggestions) { const container = $('#optimizationList'); container.empty(); suggestions.forEach((suggestion, index) => { const priorityClass = getPriorityClass(suggestion.priority); const iconClass = getPriorityIcon(suggestion.priority); const item = `
${suggestion.title} ${suggestion.priority}
${suggestion.description}
`; container.append(item); }); } /** * 初始化图表 */ function initCharts(distributionData, trendsData) { initUtilizationChart(distributionData); initTrendsChart(trendsData); } /** * 初始化利用率分布图表 */ function initUtilizationChart(distributionData) { const chartDom = document.getElementById('utilizationChart'); utilizationChart = echarts.init(chartDom); const option = { backgroundColor: 'transparent', tooltip: { trigger: 'item', backgroundColor: 'rgba(22, 186, 170, 0.9)', borderColor: '#16baaa', textStyle: { color: '#fff' } }, legend: { orient: 'vertical', left: 'left', top: 'center', textStyle: { color: '#16baaa', fontSize: 12 } }, series: [ { name: '资源利用率', type: 'pie', radius: ['40%', '70%'], center: ['65%', '40%'], avoidLabelOverlap: false, itemStyle: { borderRadius: 8, borderColor: '#0a1e2b', borderWidth: 2 }, label: { show: true, position: 'outside', color: '#8cc8c1', fontSize: 11, formatter: '{b}: {c}%' }, emphasis: { label: { show: true, fontSize: 12, fontWeight: 'bold', color: '#16baaa' }, itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(22, 186, 170, 0.5)' } }, data: distributionData || [] } ] }; utilizationChart.setOption(option); } /** * 初始化趋势图表 */ function initTrendsChart(trendsData) { const chartDom = document.getElementById('trendsChart'); trendsChart = echarts.init(chartDom); if (!trendsData || !trendsData.dates) { console.warn('趋势数据为空'); return; } console.log('趋势数据:', trendsData); // 调试日志 // 处理后端返回的数据格式 let personnelData = []; let equipmentData = []; let materialData = []; let energyData = []; if (trendsData.trends) { // 如果数据格式是 { dates: [], trends: { personnel: [], equipment: [], material: [], energy: [] } } personnelData = trendsData.trends.personnel || []; equipmentData = trendsData.trends.equipment || []; materialData = trendsData.trends.material || []; energyData = trendsData.trends.energy || []; } else { // 如果数据格式是 { dates: [], personnel: [], equipment: [], material: [], energy: [] } personnelData = trendsData.personnel || []; equipmentData = trendsData.equipment || []; materialData = trendsData.material || []; energyData = trendsData.energy || []; } // 检查数据是否有效 const hasValidData = personnelData.some(val => val > 0) || equipmentData.some(val => val > 0) || materialData.some(val => val > 0) || energyData.some(val => val > 0); if (!hasValidData) { console.warn('所有趋势数据都为0或无效'); // 显示无数据提示 showNoDataMessage(); return; } // 动态调整Y轴范围 const allData = [...personnelData, ...equipmentData, ...materialData, ...energyData]; const validData = allData.filter(val => val > 0); const minValue = validData.length > 0 ? Math.max(0, Math.min(...validData) - 10) : 0; const maxValue = validData.length > 0 ? Math.min(100, Math.max(...validData) + 10) : 100; const option = { backgroundColor: 'transparent', tooltip: { trigger: 'axis', backgroundColor: 'rgba(22, 186, 170, 0.9)', borderColor: '#16baaa', textStyle: { color: '#fff' }, axisPointer: { type: 'cross', lineStyle: { color: '#16baaa' } }, formatter: function(params) { let tooltip = `${params[0].axisValueLabel}
`; params.forEach(param => { if (param.value > 0) { // 只显示大于0的数据 tooltip += `${param.marker}${param.seriesName}: ${param.value}%
`; } }); return tooltip; } }, legend: { data: ['人员', '设备', '材料', '能源'], textStyle: { color: '#16baaa' }, // top: 10 }, /* grid: { left: '3%', right: '4%', bottom: '0%', top: '5%', containLabel: true }, */ xAxis: { type: 'category', boundaryGap: false, data: trendsData.dates.map(date => { // 处理日期格式,支持不同的日期格式 if (date.length > 10) { return date.substring(5, 10); // YYYY-MM-DD 格式 } else { return date.substring(5); // MM-DD 格式 } }), axisLine: { lineStyle: { color: '#16baaa' } }, axisTick: { lineStyle: { color: '#16baaa' } }, axisLabel: { color: '#8cc8c1' } }, yAxis: { type: 'value', min: minValue, max: maxValue, axisLine: { lineStyle: { color: '#16baaa' } }, axisTick: { lineStyle: { color: '#16baaa' } }, axisLabel: { color: '#8cc8c1', formatter: '{value}%' }, splitLine: { lineStyle: { color: 'rgba(22, 186, 170, 0.2)' } } }, series: [ { name: '人员', type: 'line', smooth: true, data: personnelData, lineStyle: { color: '#16baaa', width: 2 }, itemStyle: { color: '#16baaa' }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: 'rgba(22, 186, 170, 0.3)' }, { offset: 1, color: 'rgba(22, 186, 170, 0.1)' } ]) }, connectNulls: false // 不连接空值 }, { name: '设备', type: 'line', smooth: true, data: equipmentData, lineStyle: { color: '#20d3c2', width: 2 }, itemStyle: { color: '#20d3c2' }, connectNulls: false }, { name: '材料', type: 'line', smooth: true, data: materialData, lineStyle: { color: '#25e5d0', width: 2 }, itemStyle: { color: '#25e5d0' }, connectNulls: false }, { name: '能源', type: 'line', smooth: true, data: energyData, lineStyle: { color: '#2af0dd', width: 2 }, itemStyle: { color: '#2af0dd' }, connectNulls: false } ] }; trendsChart.setOption(option); } /** * 显示无数据提示 */ function showNoDataMessage() { const chartDom = document.getElementById('trendsChart'); if (trendsChart) { trendsChart.dispose(); } trendsChart = echarts.init(chartDom); const option = { backgroundColor: 'transparent', title: { text: '暂无趋势数据', left: 'center', top: 'middle', textStyle: { color: '#8cc8c1', fontSize: 16 } }, graphic: { type: 'text', left: 'center', top: '60%', style: { text: '请检查数据源或联系管理员', textAlign: 'center', fill: '#666', fontSize: 12 } } }; trendsChart.setOption(option); window.addEventListener("resize", function () { trendsChart.resize(); }); } /** * 启动数据刷新定时器 */ function startDataRefresh() { setInterval(async () => { try { // 获取实时状态更新 const realtimeData = await resourceApiService.getRealtimeStatus(lastUpdateTime); if (realtimeData && realtimeData.hasUpdate) { // 更新最后更新时间 lastUpdateTime = realtimeData.updateTime; // 更新页面数据 if (realtimeData.updates && realtimeData.updates.overallStats) { updateHeaderStats(realtimeData.updates.overallStats); } // 更新图表 updateCharts(); console.log('数据已更新:', new Date().toLocaleTimeString()); } } catch (error) { console.error('定时更新失败:', error); } }, 60000); // 每60秒更新一次 } /** * 更新头部统计数据 */ function updateHeaderStats(stats) { if (isAnimating) return; isAnimating = true; const currentTotal = parseFloat($('#totalRate').text()) || 0; const currentPersonnel = parseFloat($('#personnelRate').text()) || 0; const currentEquipment = parseFloat($('#equipmentRate').text()) || 0; const currentMaterial = parseFloat($('#materialRate').text()) || 0; animateValue('totalRate', currentTotal, stats.totalRate, '%'); animateValue('personnelRate', currentPersonnel, stats.personnelRate, '%'); animateValue('equipmentRate', currentEquipment, stats.equipmentRate, '%'); animateValue('materialRate', currentMaterial, stats.materialRate, '%'); setTimeout(() => { isAnimating = false; }, 1500); } /** * 更新图表数据 */ async function updateCharts() { try { // 获取最新的分布数据 const distributionData = await resourceApiService.getResourceDistribution(); if (utilizationChart && distributionData) { utilizationChart.setOption({ series: [{ data: distributionData }] }); } } catch (error) { console.error('图表更新失败:', error); } } // 数值动画函数 function animateValue(elementId, start, end, suffix = '') { const element = document.getElementById(elementId); if (!element) return; if (isNaN(start) || start < 0) start = 0; if (isNaN(end)) return; if (Math.abs(end - start) < 0.1) { element.textContent = end.toFixed(1) + suffix; return; } const range = end - start; const duration = 1000; const startTime = Date.now(); const animate = () => { const now = Date.now(); const elapsed = now - startTime; const progress = Math.min(elapsed / duration, 1); const easeOutQuart = 1 - Math.pow(1 - progress, 4); const current = start + (range * easeOutQuart); element.textContent = current.toFixed(1) + suffix; if (progress < 1) { requestAnimationFrame(animate); } else { element.textContent = end.toFixed(1) + suffix; } }; animate(); } // 工具函数 function getAssessmentClass(assessment) { switch(assessment) { case '优秀': return 'assessment-excellent'; case '良好': return 'assessment-good'; case '一般': return 'assessment-normal'; default: return ''; } } function getStatusClass(status) { // 处理英文状态和中英文对照状态 const lowerStatus = status.toLowerCase(); // 正常/良好状态 if (lowerStatus.includes('active') || lowerStatus.includes('normal') || lowerStatus.includes('sufficient') || lowerStatus.includes('running') || lowerStatus.includes('working') || lowerStatus.includes('available') || lowerStatus.includes('stable') || lowerStatus.includes('good') || status.includes('工作中') || status.includes('运行中') || status.includes('充足') || status.includes('正常') || status.includes('可用') || status.includes('稳定')) { return 'status-active'; } // 警告/异常状态 else if (lowerStatus.includes('rest') || lowerStatus.includes('maintenance') || lowerStatus.includes('low') || lowerStatus.includes('high_consumption') || lowerStatus.includes('low_stock') || lowerStatus.includes('warning') || lowerStatus.includes('critical') || lowerStatus.includes('offline') || lowerStatus.includes('error') || lowerStatus.includes('unavailable') || lowerStatus.includes('unstable') || lowerStatus.includes('poor') || status.includes('休息中') || status.includes('维护中') || status.includes('库存低') || status.includes('偏高') || status.includes('警告') || status.includes('紧急') || status.includes('离线') || status.includes('故障') || status.includes('不可用') || status.includes('不稳定') || status.includes('较差')) { return 'status-warning'; } // 默认状态 return 'status-normal'; } // 状态中英文对照 function translateStatus(status) { const statusMap = { 'active': '运行中', 'normal': '正常', 'sufficient': '充足', 'rest': '休息中', 'maintenance': '维护中', 'low_stock': '库存不足', 'high_consumption': '消耗偏高', 'offline': '离线', 'error': '故障', 'warning': '警告', 'good': '良好', 'poor': '较差', 'working': '工作中', 'running': '运行中', 'idle': '空闲', 'standby': '待机', 'busy': '繁忙', 'available': '可用', 'unavailable': '不可用', 'critical': '紧急', 'low': '偏低', 'high': '偏高', 'stable': '稳定', 'unstable': '不稳定' }; // 检查是否为纯英文状态(不包含中文字符) const isEnglishOnly = /^[a-zA-Z_\-\s]+$/.test(status); if (isEnglishOnly) { const lowerStatus = status.toLowerCase().trim(); // 如果是英文状态,返回中文翻译+英文原文 if (statusMap[lowerStatus]) { return `${statusMap[lowerStatus]}`; } else { // 如果没有对应翻译,但是是英文,则添加提示 return `${status} (英文状态)`; } } // 如果已经是中文或混合文本,直接返回 return status; } function getPriorityClass(priority) { switch(priority) { case '高优先级': return 'priority-high'; case '中优先级': return 'priority-medium'; case '低优先级': return 'priority-low'; default: return ''; } } function getPriorityIcon(priority) { switch(priority) { case '高优先级': return 'fas fa-exclamation-triangle priority-icon-high'; case '中优先级': return 'fas fa-info-circle priority-icon-medium'; case '低优先级': return 'fas fa-check-circle priority-icon-low'; default: return 'fas fa-circle'; } } // 初始化粒子背景 function initParticles() { if (typeof particlesJS !== 'undefined') { particlesJS('particles-js', { "particles": { "number": { "value": 80, "density": { "enable": true, "value_area": 800 } }, "color": { "value": "#16baaa" }, "shape": { "type": "circle" }, "opacity": { "value": 0.5, "random": false }, "size": { "value": 3, "random": true }, "line_linked": { "enable": true, "distance": 150, "color": "#16baaa", "opacity": 0.4, "width": 1 }, "move": { "enable": true, "speed": 2, "direction": "none", "random": false, "straight": false, "out_mode": "out", "bounce": false } }, "interactivity": { "detect_on": "canvas", "events": { "onhover": { "enable": true, "mode": "repulse" }, "onclick": { "enable": true, "mode": "push" }, "resize": true }, "modes": { "repulse": { "distance": 200, "duration": 0.4 }, "push": { "particles_nb": 4 } } }, "retina_detect": true }); } } // 窗口大小改变时重新调整图表 window.addEventListener('resize', function() { if (utilizationChart) { utilizationChart.resize(); } if (trendsChart) { trendsChart.resize(); } }); // 添加状态和评估样式 $(document).ready(function() { $('