| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- <!doctype html>
- <html lang="zh-CN">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>大屏展示</title>
- <style>
- body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, PingFang SC, Microsoft YaHei, sans-serif; background: #0b1020; color: #e8ecf1; }
- .header { display:flex; justify-content: space-between; align-items:center; padding: 14px 20px; background: #10182b; border-bottom: 1px solid #1e2a44; }
- .title { font-size: 18px; font-weight: 600; }
- .btn { height: 34px; padding: 0 12px; border-radius: 8px; border: 1px solid #2a3b5f; background: #0e1424; color: #e8ecf1; cursor:pointer; }
- .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; padding: 16px; }
- .card { background: #121a2a; border: 1px solid #1e2a44; border-radius: 12px; padding: 16px; }
- .metric { display:flex; gap: 16px; margin-top: 8px; }
- .kv { background: #0e1424; border: 1px solid #2a3b5f; border-radius: 10px; padding: 12px; min-width: 140px; text-align:center; }
- .k { font-size: 12px; color: #9fb0ca; }
- .v { font-size: 22px; font-weight: 700; margin-top: 6px; }
- .error { color: #ff6b6b; font-size: 13px; min-height: 18px; margin-top: 6px; }
- </style>
- <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
- <script>
- let personChart;
- let vehicleChart;
- function ensureToken() {
- const token = localStorage.getItem('access_token');
- if (!token) {
- window.location.href = '/login';
- return null;
- }
- return token;
- }
- async function fetchCount(algorithmId) {
- const token = ensureToken();
- if (!token) return null;
- const resp = await fetch(`/api/v1/CrossCount/getCrossCountByAlgorithmsId/${algorithmId}`, {
- headers: { 'Authorization': `Bearer ${token}` }
- });
- if (!resp.ok) throw new Error(await resp.text());
- return resp.json();
- }
- function initChartsIfNeeded() {
- if (!personChart) {
- const ctx = document.getElementById('person-chart').getContext('2d');
- personChart = new Chart(ctx, {
- type: 'line',
- data: { labels: [], datasets: [
- { label: '进线', data: [], borderColor: '#4cafef', backgroundColor: 'rgba(76,175,239,.2)', tension: .3 },
- { label: '出线', data: [], borderColor: '#ff9f40', backgroundColor: 'rgba(255,159,64,.2)', tension: .3 },
- { label: '在场', data: [], borderColor: '#62d26f', backgroundColor: 'rgba(98,210,111,.2)', tension: .3 }
- ]},
- options: { responsive: true, plugins: { legend: { labels: { color: '#9fb0ca' } } }, scales: { x: { ticks: { color: '#9fb0ca' }, grid: { color: '#1e2a44' } }, y: { ticks: { color: '#9fb0ca' }, grid: { color: '#1e2a44' } } } }
- });
- }
- if (!vehicleChart) {
- const ctx = document.getElementById('vehicle-chart').getContext('2d');
- vehicleChart = new Chart(ctx, {
- type: 'line',
- data: { labels: [], datasets: [
- { label: '进线', data: [], borderColor: '#4cafef', backgroundColor: 'rgba(76,175,239,.2)', tension: .3 },
- { label: '出线', data: [], borderColor: '#ff9f40', backgroundColor: 'rgba(255,159,64,.2)', tension: .3 },
- { label: '在场', data: [], borderColor: '#62d26f', backgroundColor: 'rgba(98,210,111,.2)', tension: .3 }
- ]},
- options: { responsive: true, plugins: { legend: { labels: { color: '#9fb0ca' } } }, scales: { x: { ticks: { color: '#9fb0ca' }, grid: { color: '#1e2a44' } }, y: { ticks: { color: '#9fb0ca' }, grid: { color: '#1e2a44' } } } }
- });
- }
- }
- function pushChartPoint(chart, label, up, down, total) {
- const maxPoints = 60; // 保留最近5分钟的 5s 间隔点
- chart.data.labels.push(label);
- chart.data.datasets[0].data.push(up);
- chart.data.datasets[1].data.push(down);
- chart.data.datasets[2].data.push(total);
- if (chart.data.labels.length > maxPoints) {
- chart.data.labels.shift();
- chart.data.datasets.forEach(d => d.data.shift());
- }
- chart.update('none');
- }
- async function loadData() {
- const err = document.getElementById('error');
- err.textContent = '';
- try {
- initChartsIfNeeded();
- const [person, vehicle] = await Promise.all([
- fetchCount(2),
- fetchCount(3)
- ]);
- renderCard('person', person);
- renderCard('vehicle', vehicle);
- const ts = new Date();
- const label = ts.toLocaleTimeString();
- pushChartPoint(personChart, label, person.crossTotalUpCount ?? 0, person.crossTotalDownCount ?? 0, person.totalCount ?? 0);
- pushChartPoint(vehicleChart, label, vehicle.crossTotalUpCount ?? 0, vehicle.crossTotalDownCount ?? 0, vehicle.totalCount ?? 0);
- } catch (e) {
- err.textContent = e.message || '加载失败';
- }
- }
- function renderCard(prefix, data) {
- document.getElementById(`${prefix}-up`).textContent = data.crossTotalUpCount ?? 0;
- document.getElementById(`${prefix}-down`).textContent = data.crossTotalDownCount ?? 0;
- document.getElementById(`${prefix}-total`).textContent = data.totalCount ?? 0;
- }
- function logout() {
- localStorage.removeItem('access_token');
- window.location.href = '/login';
- }
- document.addEventListener('DOMContentLoaded', loadData);
- // 每60秒自动刷新一次数据
- setInterval(loadData, 60000);
- </script>
- </head>
- <body>
- <div class="header">
- <div class="title">大屏展示</div>
- <div>
- <button class="btn" onclick="loadData()">刷新</button>
- <button class="btn" onclick="logout()">退出</button>
- </div>
- </div>
- <div class="grid">
- <div class="card">
- <div class="title">办公室人员进出入统计</div>
- <div class="metric">
- <div class="kv"><div class="k">进线总数</div><div id="person-up" class="v">0</div></div>
- <div class="kv"><div class="k">出线总数</div><div id="person-down" class="v">0</div></div>
- <div class="kv"><div class="k">当前在场</div><div id="person-total" class="v">0</div></div>
- </div>
- <div style="margin-top: 12px;">
- <canvas id="person-chart" height="120"></canvas>
- </div>
- </div>
- <div class="card">
- <div class="title">园区车辆进出入统计</div>
- <div class="metric">
- <div class="kv"><div class="k">进线总数</div><div id="vehicle-up" class="v">0</div></div>
- <div class="kv"><div class="k">出线总数</div><div id="vehicle-down" class="v">0</div></div>
- <div class="kv"><div class="k">当前在场</div><div id="vehicle-total" class="v">0</div></div>
- </div>
- <div style="margin-top: 12px;">
- <canvas id="vehicle-chart" height="120"></canvas>
- </div>
- </div>
- </div>
- <div id="error" class="error" style="padding: 0 16px;"></div>
- </body>
- </html>
|