开始
This commit is contained in:
Executable
+418
@@ -0,0 +1,418 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}">
|
||||
<div class="dashboard_container">
|
||||
<h1 class="dashboard_title">设备在线率仪表盘</h1>
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<div class="dashboard_section">
|
||||
<h2 class="section_title">设备在线率趋势图</h2>
|
||||
<div class="chart_container">
|
||||
<div class="chart_wrapper">
|
||||
<canvas id="overallChart"></canvas>
|
||||
</div>
|
||||
<div class="chart_wrapper">
|
||||
<canvas id="branchChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart_container">
|
||||
<div class="chart_wrapper">
|
||||
<canvas id="deviceTypeChart"></canvas>
|
||||
</div>
|
||||
<div class="chart_wrapper">
|
||||
<canvas id="townChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 总体设备在线率 -->
|
||||
<div class="dashboard_section">
|
||||
<h2 class="section_title">各类型设备总体在线率</h2>
|
||||
<div class="card_grid">
|
||||
{% for device_type, stats in overall_stats.items() %}
|
||||
<div class="dashboard_card">
|
||||
<div class="card_header">
|
||||
<h3 class="card_title">{{ device_type }}</h3>
|
||||
</div>
|
||||
<div class="card_content">
|
||||
<div class="progress_container">
|
||||
<div class="progress_bar" style="width: {{ stats.online_rate }}%">
|
||||
<span class="progress_text">{{ stats.online_rate }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats_details">
|
||||
<div class="stats_item">
|
||||
<span class="stats_label">设备总数</span>
|
||||
<span class="stats_value">{{ stats.total }}</span>
|
||||
</div>
|
||||
<div class="stats_item">
|
||||
<span class="stats_label">派单数</span>
|
||||
<span class="stats_value">{{ stats.work_orders }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 各支局设备在线率 -->
|
||||
<div class="dashboard_section">
|
||||
<h2 class="section_title">各支局设备在线率</h2>
|
||||
<div class="table_container">
|
||||
<table class="dashboard_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>支局</th>
|
||||
<th>设备类型</th>
|
||||
<th>设备总数</th>
|
||||
<th>派单数</th>
|
||||
<th>在线率</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for branch, types in branch_stats.items() %}
|
||||
{% for device_type, stats in types.items() %}
|
||||
{% if stats.total > 0 %}
|
||||
<tr>
|
||||
{% if loop.first %}
|
||||
<td rowspan="{{ types|length }}" class="branch_cell">{{ branch }}</td>
|
||||
{% endif %}
|
||||
<td>{{ device_type }}</td>
|
||||
<td>{{ stats.total }}</td>
|
||||
<td>{{ stats.work_orders }}</td>
|
||||
<td>{{ stats.online_rate }}%</td>
|
||||
<td>
|
||||
{% if stats.online_rate >= 90 %}
|
||||
<span class="status_badge good">良好</span>
|
||||
{% elif stats.online_rate >= 75 %}
|
||||
<span class="status_badge normal">一般</span>
|
||||
{% else %}
|
||||
<span class="status_badge bad">需关注</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 各乡镇设备在线率 -->
|
||||
<div class="dashboard_section">
|
||||
<h2 class="section_title">各乡镇设备在线率</h2>
|
||||
<div class="accordion_container">
|
||||
{% for town, types in town_stats.items() %}
|
||||
<div class="accordion_item">
|
||||
<div class="accordion_header" onclick="toggleAccordion(this)">
|
||||
<div class="accordion_title">{{ town }}</div>
|
||||
<div class="accordion_summary">
|
||||
<span>总设备数: {{ types['总计'].total }}</span>
|
||||
<span>在线率: {{ types['总计'].online_rate }}%</span>
|
||||
{% if types['总计'].online_rate >= 90 %}
|
||||
<span class="status_badge good">良好</span>
|
||||
{% elif types['总计'].online_rate >= 75 %}
|
||||
<span class="status_badge normal">一般</span>
|
||||
{% else %}
|
||||
<span class="status_badge bad">需关注</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="accordion_icon">▼</div>
|
||||
</div>
|
||||
<div class="accordion_content">
|
||||
<table class="detail_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>设备类型</th>
|
||||
<th>设备总数</th>
|
||||
<th>派单数</th>
|
||||
<th>在线率</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for device_type, stats in types.items() %}
|
||||
{% if device_type != '总计' and stats.total > 0 %}
|
||||
<tr>
|
||||
<td>{{ device_type }}</td>
|
||||
<td>{{ stats.total }}</td>
|
||||
<td>{{ stats.work_orders }}</td>
|
||||
<td>{{ stats.online_rate }}%</td>
|
||||
<td>
|
||||
{% if stats.online_rate >= 90 %}
|
||||
<span class="status_badge good">良好</span>
|
||||
{% elif stats.online_rate >= 75 %}
|
||||
<span class="status_badge normal">一般</span>
|
||||
{% else %}
|
||||
<span class="status_badge bad">需关注</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引入Chart.js库 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<script>
|
||||
function toggleAccordion(element) {
|
||||
const content = element.nextElementSibling;
|
||||
const icon = element.querySelector('.accordion_icon');
|
||||
|
||||
if (content.style.maxHeight) {
|
||||
content.style.maxHeight = null;
|
||||
icon.textContent = '▼';
|
||||
} else {
|
||||
content.style.maxHeight = content.scrollHeight + "px";
|
||||
icon.textContent = '▲';
|
||||
}
|
||||
}
|
||||
|
||||
// 图表数据准备
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 设备类型在线率图表
|
||||
const deviceTypeCtx = document.getElementById('deviceTypeChart').getContext('2d');
|
||||
const deviceTypeLabels = [];
|
||||
const deviceTypeData = [];
|
||||
const deviceTypeColors = [];
|
||||
|
||||
{% for device_type, stats in overall_stats.items() %}
|
||||
deviceTypeLabels.push('{{ device_type }}');
|
||||
deviceTypeData.push({{ stats.online_rate }});
|
||||
|
||||
// 根据在线率设置颜色
|
||||
if ({{ stats.online_rate }} >= 90) {
|
||||
deviceTypeColors.push('rgba(40, 167, 69, 0.7)');
|
||||
} else if ({{ stats.online_rate }} >= 75) {
|
||||
deviceTypeColors.push('rgba(255, 193, 7, 0.7)');
|
||||
} else {
|
||||
deviceTypeColors.push('rgba(220, 53, 69, 0.7)');
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
new Chart(deviceTypeCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: deviceTypeLabels,
|
||||
datasets: [{
|
||||
label: '设备类型在线率(%)',
|
||||
data: deviceTypeData,
|
||||
backgroundColor: deviceTypeColors,
|
||||
borderColor: deviceTypeColors.map(color => color.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '各设备类型在线率对比'
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
title: {
|
||||
display: true,
|
||||
text: '在线率(%)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 支局在线率对比图
|
||||
const branchCtx = document.getElementById('branchChart').getContext('2d');
|
||||
const branchLabels = [];
|
||||
const branchData = [];
|
||||
const branchColors = [];
|
||||
|
||||
{% for branch, types in branch_stats.items() %}
|
||||
branchLabels.push('{{ branch }}');
|
||||
branchData.push({{ types['总计'].online_rate }});
|
||||
|
||||
// 根据在线率设置颜色
|
||||
if ({{ types['总计'].online_rate }} >= 90) {
|
||||
branchColors.push('rgba(40, 167, 69, 0.7)');
|
||||
} else if ({{ types['总计'].online_rate }} >= 75) {
|
||||
branchColors.push('rgba(255, 193, 7, 0.7)');
|
||||
} else {
|
||||
branchColors.push('rgba(220, 53, 69, 0.7)');
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
new Chart(branchCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: branchLabels,
|
||||
datasets: [{
|
||||
label: '支局在线率(%)',
|
||||
data: branchData,
|
||||
backgroundColor: branchColors,
|
||||
borderColor: branchColors.map(color => color.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '各支局在线率对比'
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
title: {
|
||||
display: true,
|
||||
text: '在线率(%)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 乡镇在线率对比图
|
||||
const townCtx = document.getElementById('townChart').getContext('2d');
|
||||
const townLabels = [];
|
||||
const townData = [];
|
||||
const townColors = [];
|
||||
|
||||
{% for town, types in town_stats.items() %}
|
||||
townLabels.push('{{ town }}');
|
||||
townData.push({{ types['总计'].online_rate }});
|
||||
|
||||
// 根据在线率设置颜色
|
||||
if ({{ types['总计'].online_rate }} >= 90) {
|
||||
townColors.push('rgba(40, 167, 69, 0.7)');
|
||||
} else if ({{ types['总计'].online_rate }} >= 75) {
|
||||
townColors.push('rgba(255, 193, 7, 0.7)');
|
||||
} else {
|
||||
townColors.push('rgba(220, 53, 69, 0.7)');
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
new Chart(townCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: townLabels,
|
||||
datasets: [{
|
||||
label: '乡镇在线率(%)',
|
||||
data: townData,
|
||||
backgroundColor: townColors,
|
||||
borderColor: townColors.map(color => color.replace('0.7', '1')),
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '各乡镇在线率对比'
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
title: {
|
||||
display: true,
|
||||
text: '在线率(%)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 总体在线率趋势图(模拟数据)
|
||||
const overallCtx = document.getElementById('overallChart').getContext('2d');
|
||||
|
||||
// 模拟过去7天的数据
|
||||
const dates = [];
|
||||
const today = new Date();
|
||||
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(today.getDate() - i);
|
||||
dates.push(date.getMonth() + 1 + '/' + date.getDate());
|
||||
}
|
||||
|
||||
// 生成模拟数据,假设在线率在当前值附近波动
|
||||
const generateTrendData = (baseValue) => {
|
||||
return Array.from({length: 7}, () => {
|
||||
// 在基准值附近随机波动,但确保不超过100和不低于50
|
||||
const fluctuation = Math.random() * 10 - 5; // -5 到 +5 之间的随机数
|
||||
return Math.min(100, Math.max(50, baseValue + fluctuation));
|
||||
});
|
||||
};
|
||||
|
||||
// 假设当前总体在线率为所有设备类型在线率的平均值
|
||||
let avgOnlineRate = 0;
|
||||
let count = 0;
|
||||
{% for device_type, stats in overall_stats.items() %}
|
||||
avgOnlineRate += {{ stats.online_rate }};
|
||||
count++;
|
||||
{% endfor %}
|
||||
avgOnlineRate = avgOnlineRate / (count || 1);
|
||||
|
||||
new Chart(overallCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: dates,
|
||||
datasets: [{
|
||||
label: '总体在线率趋势(%)',
|
||||
data: generateTrendData(avgOnlineRate),
|
||||
fill: false,
|
||||
borderColor: 'rgb(75, 192, 192)',
|
||||
tension: 0.1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '总体在线率7天趋势'
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false,
|
||||
min: Math.max(0, avgOnlineRate - 20),
|
||||
max: Math.min(100, avgOnlineRate + 20),
|
||||
title: {
|
||||
display: true,
|
||||
text: '在线率(%)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user