// 检查 ECharts 是否加载完成
function checkEchartsLoaded(callback, maxAttempts = 10) {
let attempts = 0;
const checkInterval = setInterval(() => {
attempts++;
if (window.echarts) {
clearInterval(checkInterval);
callback();
} else if (attempts >= maxAttempts) {
clearInterval(checkInterval);
console.error('ECharts 加载失败');
alert('图表组件加载失败,请刷新页面重试');
}
}, 500);
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 等待 ECharts 加载完成后初始化图表
checkEchartsLoaded(() => {
// 初始化所有图表
initCharts();
// 启动数据更新循环
setInterval(updateData, 30000);
});
});
// 初始化所有图表
function initCharts() {
initEquipmentTopology();
initGanttChart();
initFaultTrend();
initEfficiencyChart();
initQualityIssues();
updateHeaderData();
updateOrderList();
updateFaultList();
}
// 更新头部数据
function updateHeaderData() {
document.getElementById('oee').innerText = MockData.random(80, 95) + '%';
document.getElementById('production').innerText = MockData.random(2000, 3000).toLocaleString();
document.getElementById('quality').innerText = (MockData.random(975, 995) / 10) + '%';
document.getElementById('downtime').innerText = (MockData.random(15, 35) / 10) + '%';
}
// 初始化设备拓扑图
function initEquipmentTopology() {
const dom = document.getElementById('equipment-topology');
if (!dom) {
console.error('找不到设备拓扑图容器');
return;
}
try {
const chart = echarts.init(dom);
const categories = ['运行', '停机', '故障', '维护'];
const option = {
tooltip: {},
series: [{
type: 'graph',
layout: 'circular',
symbolSize: 60,
roam: true,
focusNodeAdjacency: true,
categories: categories.map(name => ({
name: name
})),
label: {
show: true,
color: '#fff'
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [4, 10],
data: [
{name: '成型机A', value: 'running', category: '运行', symbolSize: 70},
{name: '成型机B', value: 'running', category: '运行', symbolSize: 70},
{name: '硫化机A', value: 'warning', category: '故障', symbolSize: 70},
{name: '硫化机B', value: 'running', category: '运行', symbolSize: 70},
{name: '检测台A', value: 'maintenance', category: '维护', symbolSize: 60},
{name: '检测台B', value: 'running', category: '运行', symbolSize: 60},
{name: '包装机', value: 'running', category: '运行', symbolSize: 65}
],
links: [
{source: '成型机A', target: '硫化机A'},
{source: '成型机B', target: '硫化机B'},
{source: '硫化机A', target: '检测台A'},
{source: '硫化机B', target: '检测台B'},
{source: '检测台A', target: '包装机'},
{source: '检测台B', target: '包装机'}
],
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
shadowBlur: 10,
shadowColor: 'rgba(30, 84, 231, 0.5)'
},
lineStyle: {
color: '#1890ff',
width: 2
}
}]
};
chart.setOption(option);
} catch (error) {
console.error('初始化设备拓扑图失败:', error);
}
}
// 初始化甘特图
function initGanttChart() {
const dom = document.getElementById('gantt-chart');
if (!dom) {
console.error('找不到甘特图容器');
return;
}
try {
const chart = echarts.init(dom);
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function(params) {
const data = params[0];
return `工单进度:${data.value}%
预计完成时间:${new Date().toLocaleDateString()}`;
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
axisLabel: {
color: '#fff'
}
},
yAxis: {
type: 'category',
data: ['工单1', '工单2', '工单3', '工单4'],
axisLabel: {
color: '#fff'
}
},
series: [{
name: '完成',
type: 'bar',
stack: 'total',
itemStyle: {
color: '#52c41a'
},
data: [80, 60, 40, 20]
}, {
name: '剩余',
type: 'bar',
stack: 'total',
itemStyle: {
color: '#1890ff'
},
data: [20, 40, 60, 80]
}]
};
chart.setOption(option);
} catch (error) {
console.error('初始化甘特图失败:', error);
}
}
// 初始化故障趋势图
function initFaultTrend() {
const dom = document.getElementById('fault-trend');
if (!dom) {
console.error('找不到故障趋势图容器');
return;
}
try {
const chart = echarts.init(dom);
const option = {
tooltip: {
trigger: 'axis',
formatter: function(params) {
return `${params[0].name}
故障次数:${params[0].value}次
同比上周:${params[0].value > 4 ? '↑' : '↓'}${Math.abs(params[0].value - 4)}次`;
}
},
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLabel: {
color: '#fff'
}
},
yAxis: {
type: 'value',
axisLabel: {
color: '#fff'
}
},
series: [{
data: [5, 3, 4, 2, 6, 4, 3],
type: 'line',
smooth: true,
itemStyle: {
color: '#ff4d4f'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(255, 77, 79, 0.3)'
}, {
offset: 1,
color: 'rgba(255, 77, 79, 0)'
}])
}
}]
};
chart.setOption(option);
} catch (error) {
console.error('初始化故障趋势图失败:', error);
}
}
// 初始化效率图表
function initEfficiencyChart() {
const dom = document.getElementById('efficiency-chart');
if (!dom) {
console.error('找不到效率图表容器');
return;
}
try {
const chart = echarts.init(dom);
const {dates, values} = MockData.getEfficiencyData();
const option = {
title: {
text: '生产效率趋势',
textStyle: {
color: '#fff',
fontSize: 14
},
subtextStyle: {
color: '#66a3ff'
}
},
grid: {
top: 50,
right: 30,
bottom: 30,
left: 50
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: dates,
axisLabel: {
color: '#fff'
}
},
yAxis: {
type: 'value',
min: 70,
axisLabel: {
color: '#fff'
}
},
series: [{
data: values,
type: 'line',
smooth: true,
itemStyle: {
color: '#1890ff'
}
}]
};
chart.setOption(option);
} catch (error) {
console.error('初始化效率图表失败:', error);
}
}
// 初始化质量问题图表
function initQualityIssues() {
const dom = document.getElementById('quality-issues');
if (!dom) {
console.error('找不到质量问题图表容器');
return;
}
try {
const chart = echarts.init(dom);
const issues = MockData.getQualityIssues();
const option = {
title: {
text: '质量问题分布',
textStyle: {
color: '#fff',
fontSize: 14
}
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'item'
},
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
roseType: 'radius',
data: issues.map(issue => ({
name: issue.name,
value: issue.value
})),
itemStyle: {
borderColor: '#0a1128',
borderWidth: 2
},
label: {
color: '#fff'
}
}]
};
chart.setOption(option);
} catch (error) {
console.error('初始化质量问题图表失败:', error);
}
}
// 更新工单列表
function updateOrderList() {
const tbody = document.getElementById('order-list-body');
const orders = MockData.getOrders();
tbody.innerHTML = orders.map(order => `