810 lines
25 KiB
JavaScript
810 lines
25 KiB
JavaScript
/**
|
|
* 资源利用率分析页面 - 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 = `
|
|
<tr class="table-row-animate" style="animation-delay: ${index * 0.1}s;">
|
|
<td>${item.type}</td>
|
|
<td>${item.name}</td>
|
|
<td><span class="${statusClass}">${translateStatus(item.status)}</span></td>
|
|
<td><span class="rate-value">${item.rate}%</span></td>
|
|
<td><span class="${assessmentClass}">${item.assessment}</span></td>
|
|
</tr>
|
|
`;
|
|
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 = `
|
|
<div class="optimization-item animate__animated animate__fadeInUp" style="animation-delay: ${index * 0.1}s;">
|
|
<div class="optimization-title">
|
|
<i class="${iconClass}"></i>
|
|
${suggestion.title}
|
|
<span class="optimization-priority ${priorityClass}">${suggestion.priority}</span>
|
|
</div>
|
|
<div class="optimization-desc">${suggestion.description}</div>
|
|
</div>
|
|
`;
|
|
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}<br/>`;
|
|
params.forEach(param => {
|
|
if (param.value > 0) { // 只显示大于0的数据
|
|
tooltip += `${param.marker}${param.seriesName}: ${param.value}%<br/>`;
|
|
}
|
|
});
|
|
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() {
|
|
$('<style>').prop('type', 'text/css').html(`
|
|
.status-active { color: #16baaa; font-weight: bold; }
|
|
.status-warning { color: #ffa726; font-weight: bold; }
|
|
.status-normal { color: #8cc8c1; }
|
|
.assessment-excellent { color: #16baaa; font-weight: bold; }
|
|
.assessment-good { color: #20d3c2; font-weight: bold; }
|
|
.assessment-normal { color: #ffa726; font-weight: bold; }
|
|
.priority-high { color: #ff5722; }
|
|
.priority-medium { color: #ffa726; }
|
|
.priority-low { color: #4caf50; }
|
|
.priority-icon-high { color: #ff5722; margin-right: 5px; animation: blink 1s infinite; }
|
|
.priority-icon-medium { color: #ffa726; margin-right: 5px; }
|
|
.priority-icon-low { color: #4caf50; margin-right: 5px; }
|
|
.rate-value {
|
|
color: #20d3c2;
|
|
font-weight: bold;
|
|
text-shadow: 0 0 5px rgba(32, 211, 194, 0.5);
|
|
}
|
|
.table-row-animate {
|
|
animation: slideInFromLeft 0.5s ease forwards;
|
|
opacity: 0;
|
|
transform: translateX(-20px);
|
|
}
|
|
@keyframes slideInFromLeft {
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
@keyframes blink {
|
|
0%, 50% { opacity: 1; }
|
|
51%, 100% { opacity: 0.3; }
|
|
}
|
|
`).appendTo('head');
|
|
});
|