feat: 新增柱状图,显示每小时平均用量
This commit is contained in:
@@ -9,21 +9,21 @@
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
<!-- ECharts -->
|
||||
<script src="static/js/echarts.min.js"></script>
|
||||
|
||||
<script src="/static/js/echarts.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-100 text-gray-800">
|
||||
|
||||
<div class="max-w-5xl mx-auto p-6 space-y-8">
|
||||
|
||||
<h2 class="text-2xl font-bold text-center">
|
||||
房间 {{ room_id }} 最近用电情况
|
||||
</h2>
|
||||
|
||||
<div id="eleChart" class="w-full h-80 bg-white rounded-xl shadow"></div>
|
||||
<div id="hourlyBarChart" class="w-full h-80 bg-white rounded-xl shadow"></div>
|
||||
|
||||
<!-- 表格 -->
|
||||
|
||||
<!-- 表格容器 -->
|
||||
<div class="bg-white rounded-xl shadow p-6 overflow-x-auto">
|
||||
<table class="min-w-full text-sm text-left">
|
||||
<thead class="bg-gray-50">
|
||||
@@ -33,19 +33,20 @@
|
||||
<th class="px-4 py-3">剩余金额 (元)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for i in data[::-1] %}
|
||||
<tr class="border-t hover:bg-gray-50 transition">
|
||||
<td class="px-4 py-3">{{ i.created_at | datetime }}</td>
|
||||
<td class="px-4 py-3">{{ i.left_ele }}</td>
|
||||
<td class="px-4 py-3">{{ i.left_money }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody id="table-body"></tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分页控制 -->
|
||||
<div class="flex justify-between items-center mt-4 text-sm">
|
||||
<button id="prevBtn" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">上一页</button>
|
||||
<span id="pageInfo"></span>
|
||||
<button id="nextBtn" class="px-3 py-1 bg-gray-200 rounded hover:bg-gray-300">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ===== 图表数据 =====
|
||||
const raw = {{ data | tojson }};
|
||||
const labels = raw.map(i => new Date(i.created_at).toLocaleString());
|
||||
const values = raw.map(i => i.left_ele);
|
||||
@@ -53,38 +54,108 @@
|
||||
const chartDom = document.getElementById('eleChart');
|
||||
const myChart = echarts.init(chartDom);
|
||||
|
||||
const option = {
|
||||
myChart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
|
||||
// 🔥支持区域缩放:鼠标拖动 & 下方滑块
|
||||
dataZoom: [
|
||||
{ type: 'inside' }, // 鼠标滚轮缩放 + 拖动
|
||||
{
|
||||
type: 'slider', // 底部滑块
|
||||
height: 20,
|
||||
bottom: 0,
|
||||
}
|
||||
],
|
||||
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: labels,
|
||||
boundaryGap: false
|
||||
},
|
||||
dataZoom: [{ type: 'inside' }, { type: 'slider', height: 20, bottom: 0 }],
|
||||
xAxis: { type: 'category', data: labels, boundaryGap: false },
|
||||
yAxis: { type: 'value' },
|
||||
|
||||
series: [{
|
||||
data: values,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
opacity: 0.3
|
||||
},
|
||||
areaStyle: { opacity: 0.3 },
|
||||
lineStyle: { width: 2 }
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
// raw 已经是后端传来的数据,保持正序
|
||||
const dailyData = raw.slice();
|
||||
|
||||
const hourSum = Array(24).fill(0);
|
||||
const hourCount = Array(24).fill(0);
|
||||
|
||||
for (let i = 1; i < dailyData.length; i++) {
|
||||
const prev = dailyData[i - 1];
|
||||
const curr = dailyData[i];
|
||||
|
||||
const diff = prev.left_ele - curr.left_ele; // 前一条减后一条
|
||||
if (diff <= 0) continue;
|
||||
|
||||
const h = new Date(curr.created_at).getHours();
|
||||
hourSum[h] += diff;
|
||||
hourCount[h] += 1;
|
||||
}
|
||||
myChart.setOption(option);
|
||||
|
||||
const avgUsage = hourSum.map((sum, h) => hourCount[h] > 0 ? (sum / hourCount[h]).toFixed(2) : 0);
|
||||
|
||||
|
||||
// X 轴显示 0:00~23:00
|
||||
const hours = Array.from({ length: 24 }, (_, i) => i + ":00");
|
||||
|
||||
// 渲染柱状图
|
||||
const hourlyDom = document.getElementById("hourlyBarChart");
|
||||
const hourlyChart = echarts.init(hourlyDom);
|
||||
|
||||
hourlyChart.setOption({
|
||||
title: { text: "一天24小时平均用电量", left: "center" },
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: { type: 'category', data: hours, boundaryGap: true },
|
||||
yAxis: { type: 'value', name: "度" },
|
||||
series: [{
|
||||
type: 'bar',
|
||||
data: avgUsage,
|
||||
barWidth: '50%',
|
||||
itemStyle: { opacity: 0.9 }
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
|
||||
// ===== 分页(前端渲染)=====
|
||||
const pageSize = 10; // 每页10条
|
||||
let currentPage = 1;
|
||||
const reversed = raw.slice().reverse(); // 倒序
|
||||
|
||||
function renderTable() {
|
||||
const tbody = document.getElementById("table-body");
|
||||
tbody.innerHTML = "";
|
||||
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const pageData = reversed.slice(start, end);
|
||||
|
||||
for (const row of pageData) {
|
||||
tbody.innerHTML += `
|
||||
<tr class="border-t hover:bg-gray-50 transition">
|
||||
<td class="px-4 py-3">${new Date(row.created_at).toLocaleString()}</td>
|
||||
<td class="px-4 py-3">${row.left_ele}</td>
|
||||
<td class="px-4 py-3">${row.left_money}</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
document.getElementById("pageInfo").textContent =
|
||||
`第 ${currentPage} / ${Math.ceil(reversed.length / pageSize)} 页`;
|
||||
}
|
||||
|
||||
document.getElementById("prevBtn").onclick = () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
renderTable();
|
||||
}
|
||||
};
|
||||
document.getElementById("nextBtn").onclick = () => {
|
||||
const total = Math.ceil(reversed.length / pageSize);
|
||||
if (currentPage < total) {
|
||||
currentPage++;
|
||||
renderTable();
|
||||
}
|
||||
};
|
||||
|
||||
renderTable();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user