mesEnergyScreen.html 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>能耗监控大屏</title>
  7. <style>
  8. * { box-sizing: border-box; }
  9. body {
  10. margin: 0;
  11. width: 100vw;
  12. height: 100vh;
  13. overflow: hidden;
  14. color: #e9f7ff;
  15. font-family: "Microsoft YaHei", Arial, sans-serif;
  16. background: #07121f;
  17. }
  18. .screen {
  19. width: 100vw;
  20. height: 100vh;
  21. padding: 18px 22px;
  22. display: grid;
  23. grid-template-rows: 74px 118px 310px 1fr;
  24. gap: 14px;
  25. background: linear-gradient(135deg, #07121f 0%, #102234 48%, #081522 100%);
  26. }
  27. .header {
  28. display: flex;
  29. align-items: center;
  30. justify-content: space-between;
  31. border-bottom: 1px solid rgba(108, 214, 255, .28);
  32. }
  33. .title { font-size: 30px; font-weight: 700; letter-spacing: 0; }
  34. .time { text-align: right; color: #9fd8ff; font-size: 16px; line-height: 26px; }
  35. .summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
  36. .metric, .panel, .device-card {
  37. border: 1px solid rgba(95, 201, 255, .28);
  38. background: rgba(7, 25, 41, .76);
  39. box-shadow: inset 0 0 22px rgba(49, 163, 220, .08);
  40. border-radius: 6px;
  41. }
  42. .metric { padding: 18px 20px; display: flex; flex-direction: column; justify-content: center; }
  43. .metric-label { color: #9fb6c7; font-size: 15px; }
  44. .metric-value { margin-top: 8px; font-size: 34px; font-weight: 700; color: #44e0c2; }
  45. .metric-unit { margin-left: 6px; font-size: 16px; color: #a8cfe7; }
  46. .main { min-height: 0; }
  47. .device-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; min-height: 0; }
  48. .device-card { padding: 16px; min-height: 0; }
  49. .device-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
  50. .device-name { font-size: 20px; font-weight: 700; }
  51. .badge { padding: 4px 10px; border-radius: 4px; font-size: 13px; background: rgba(68, 224, 194, .18); color: #44e0c2; }
  52. .badge.offline { background: rgba(255, 103, 103, .14); color: #ff8686; }
  53. .device-sn { margin-left: 10px; color: #8fb3ca; font-size: 13px; font-weight: 400; }
  54. .kv-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  55. .kv { padding: 10px; border-radius: 4px; background: rgba(255,255,255,.04); }
  56. .kv span { display: block; color: #92a9bb; font-size: 13px; margin-bottom: 5px; }
  57. .kv strong {
  58. display: block;
  59. width: 100%;
  60. font-size: 22px;
  61. color: #ffffff;
  62. line-height: 1.25;
  63. white-space: normal;
  64. overflow-wrap: anywhere;
  65. }
  66. .kv strong.small { font-size: 17px; }
  67. .chart-wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; min-height: 0; }
  68. .panel { min-height: 0; padding: 14px; display: flex; flex-direction: column; }
  69. .panel-title { height: 30px; line-height: 30px; font-size: 18px; font-weight: 700; color: #79d9ff; }
  70. .chart { flex: 1; min-height: 0; }
  71. </style>
  72. </head>
  73. <body>
  74. <div class="screen">
  75. <div class="header">
  76. <div class="title">能源消耗监控大屏</div>
  77. <div class="time"><div id="dateText">--</div><div id="timeText">--</div></div>
  78. </div>
  79. <div class="summary">
  80. <div class="metric"><div class="metric-label" id="meter1PowerLabel">电表1 当前功率</div><div class="metric-value"><span id="meter1Power">0</span><span class="metric-unit">kW</span></div></div>
  81. <div class="metric"><div class="metric-label" id="meter1EnergyLabel">电表1 今日用电</div><div class="metric-value"><span id="meter1Energy">0</span><span class="metric-unit">kWh</span></div></div>
  82. <div class="metric"><div class="metric-label" id="meter2PowerLabel">电表2 当前功率</div><div class="metric-value"><span id="meter2Power">0</span><span class="metric-unit">kW</span></div></div>
  83. <div class="metric"><div class="metric-label" id="meter2EnergyLabel">电表2 今日用电</div><div class="metric-value"><span id="meter2Energy">0</span><span class="metric-unit">kWh</span></div></div>
  84. </div>
  85. <div class="main">
  86. <div class="device-list" id="deviceList"></div>
  87. </div>
  88. <div class="chart-wrap">
  89. <div class="panel"><div class="panel-title">今日小时能耗趋势</div><div id="trendChart" class="chart"></div></div>
  90. <div class="panel"><div class="panel-title">实时功率对比</div><div id="powerChart" class="chart"></div></div>
  91. </div>
  92. </div>
  93. <script src="${ctxStatic}/jquery-1.11.3.min.js"></script>
  94. <script src="${ctxStatic}/echarts/4.2/echarts.min.js"></script>
  95. <script>
  96. var trendChart = echarts.init(document.getElementById('trendChart'));
  97. var powerChart = echarts.init(document.getElementById('powerChart'));
  98. function fmt(value) {
  99. var num = Number(value || 0);
  100. return Number.isFinite(num) ? num.toFixed(2) : '0.00';
  101. }
  102. function deviceTitle(device, index) {
  103. return device.deviceName || device.deviceSn || ('电表' + (index + 1));
  104. }
  105. function formatTime(value) {
  106. if (!value) {
  107. return '-';
  108. }
  109. if (typeof value === 'string') {
  110. return value.replace('T', ' ').replace(/\.\d+.*$/, '');
  111. }
  112. return value;
  113. }
  114. function updateClock() {
  115. var now = new Date();
  116. $('#dateText').text(now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0'));
  117. $('#timeText').text(String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0') + ':' + String(now.getSeconds()).padStart(2, '0'));
  118. }
  119. function renderDevices(devices) {
  120. var html = '';
  121. devices.forEach(function (d, index) {
  122. var title = deviceTitle(d, index);
  123. html += '<div class="device-card">' +
  124. '<div class="device-head"><div class="device-name">' + title + '<span class="device-sn">' + (d.deviceSn || '-') + '</span></div>' +
  125. '<div class="badge ' + (d.online ? '' : 'offline') + '">' + (d.online ? '在线' : '离线') + '</div></div>' +
  126. '<div class="kv-grid">' +
  127. '<div class="kv"><span>当前功率</span><strong>' + fmt(d.power) + ' kW</strong></div>' +
  128. '<div class="kv"><span>今日用电</span><strong>' + fmt(d.todayEnergy) + ' kWh</strong></div>' +
  129. '<div class="kv"><span>A/B/C电压</span><strong class="small">' + fmt(d.ua) + ' / ' + fmt(d.ub) + ' / ' + fmt(d.uc) + ' V</strong></div>' +
  130. '<div class="kv"><span>A/B/C电流</span><strong class="small">' + fmt(d.ia) + ' / ' + fmt(d.ib) + ' / ' + fmt(d.ic) + ' A</strong></div>' +
  131. '<div class="kv"><span>上报时间</span><strong class="small">' + formatTime(d.reportTimeText || d.reportTime) + '</strong></div>' +
  132. '</div></div>';
  133. });
  134. $('#deviceList').html(html || '<div class="device-card"><div class="device-name">暂无电表数据</div></div>');
  135. }
  136. function renderTopMeters(devices) {
  137. for (var i = 0; i < 2; i++) {
  138. var device = devices[i] || {};
  139. var title = deviceTitle(device, i);
  140. $('#meter' + (i + 1) + 'PowerLabel').text(title + ' 当前功率');
  141. $('#meter' + (i + 1) + 'EnergyLabel').text(title + ' 今日用电');
  142. $('#meter' + (i + 1) + 'Power').text(fmt(device.power));
  143. $('#meter' + (i + 1) + 'Energy').text(fmt(device.todayEnergy));
  144. }
  145. }
  146. function renderCharts(data) {
  147. var devices = data.devices || [];
  148. trendChart.setOption({
  149. color: ['#44e0c2', '#79d9ff', '#ffcc66'],
  150. tooltip: { trigger: 'axis' },
  151. legend: { top: 0, right: 10, textStyle: { color: '#b8d9ee' } },
  152. grid: { left: 45, right: 25, bottom: 34, top: 42 },
  153. xAxis: { type: 'category', data: data.hours || [], axisLabel: { color: '#9fb6c7' }, axisLine: { lineStyle: { color: '#31536a' } } },
  154. yAxis: { type: 'value', name: 'kWh', nameTextStyle: { color: '#9fb6c7' }, axisLabel: { color: '#9fb6c7' }, splitLine: { lineStyle: { color: 'rgba(100,160,190,.16)' } } },
  155. series: (data.hourlySeries || []).map(function (item) {
  156. return { name: item.name, type: 'line', smooth: true, symbol: 'circle', symbolSize: 5, data: item.data || [] };
  157. })
  158. });
  159. powerChart.setOption({
  160. color: ['#44e0c2'],
  161. tooltip: {},
  162. grid: { left: 45, right: 25, bottom: 34, top: 20 },
  163. xAxis: { type: 'category', data: devices.map(function (d, index) { return deviceTitle(d, index); }), axisLabel: { color: '#9fb6c7' }, axisLine: { lineStyle: { color: '#31536a' } } },
  164. yAxis: { type: 'value', name: 'kW', nameTextStyle: { color: '#9fb6c7' }, axisLabel: { color: '#9fb6c7' }, splitLine: { lineStyle: { color: 'rgba(100,160,190,.16)' } } },
  165. series: [{ type: 'bar', barWidth: 36, data: devices.map(function (d) { return Number(d.power || 0); }) }]
  166. });
  167. }
  168. function loadData() {
  169. $.get('${ctx}/mes/mesProduct/energyScreenData', function (resp) {
  170. if (!resp || resp.result !== 'true') { return; }
  171. var data = resp.data || {};
  172. var devices = data.devices || [];
  173. renderTopMeters(devices);
  174. renderDevices(devices);
  175. renderCharts(data);
  176. });
  177. }
  178. updateClock();
  179. loadData();
  180. setInterval(updateClock, 1000);
  181. setInterval(loadData, 10000);
  182. window.addEventListener('resize', function () {
  183. trendChart.resize();
  184. powerChart.resize();
  185. });
  186. </script>
  187. </body>
  188. </html>