NAC_Blockchain/nac-quantum-browser/view/index/index.html

219 lines
9.7 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{include file="index/layout_header" title="首页"}
<!-- 网络统计卡片 -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-3">
<div class="card bg-black border-primary h-100">
<div class="card-body text-center py-3">
<div class="text-muted small mb-1">当前区块高度</div>
<div class="fs-3 fw-bold text-primary" id="stat-block">{$stats.currentBlock|default=0}</div>
<div class="text-muted" style="font-size:11px">CBPP 共识</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card bg-black border-success h-100">
<div class="card-body text-center py-3">
<div class="text-muted small mb-1">CBPP 共识状态</div>
<div class="fs-6 fw-bold">
<span class="badge {$stats.cbppBadge|default='bg-secondary'}">{$stats.cbppConsensus|default='unknown'}</span>
</div>
<div class="text-muted" style="font-size:11px">宪政区块生产协议</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card bg-black border-info h-100">
<div class="card-body text-center py-3">
<div class="text-muted small mb-1">CSNP 网络</div>
<div class="fs-6 fw-bold">
<span class="badge {$stats.csnpBadge|default='bg-secondary'}">{$stats.csnpNetwork|default='unknown'}</span>
</div>
<div class="text-muted" style="font-size:11px">宪政安全网络协议</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card bg-black border-warning h-100">
<div class="card-body text-center py-3">
<div class="text-muted small mb-1">宪法层</div>
<div class="fs-6 fw-bold">
<span class="badge {$stats.constitutionBadge|default='bg-secondary'}">{$stats.constitutionText|default='未知'}</span>
</div>
<div class="text-muted" style="font-size:11px">Charter 智能合约</div>
</div>
</div>
</div>
</div>
<!-- 第二行统计 -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-2">
<div class="card bg-dark border-secondary h-100">
<div class="card-body text-center py-2">
<div class="text-secondary small">Chain ID</div>
<div class="fw-bold text-info">{$stats.chainId|default=20260131}</div>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card bg-dark border-secondary h-100">
<div class="card-body text-center py-2">
<div class="text-secondary small">网络</div>
<div class="fw-bold text-light">{$stats.network|default='mainnet'}</div>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card bg-dark border-secondary h-100">
<div class="card-body text-center py-2">
<div class="text-secondary small">节点数</div>
<div class="fw-bold text-light">{$stats.nodeCount|default=0}</div>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card bg-dark border-secondary h-100">
<div class="card-body text-center py-2">
<div class="text-secondary small">总交易数</div>
<div class="fw-bold text-light">{$stats.totalTransactions|default=0}</div>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card bg-dark border-secondary h-100">
<div class="card-body text-center py-2">
<div class="text-secondary small">TPS</div>
<div class="fw-bold text-light">{$stats.tps|default=0}</div>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card bg-dark border-secondary h-100">
<div class="card-body text-center py-2">
<div class="text-secondary small">平均出块</div>
<div class="fw-bold text-light">{$stats.avgBlockTime|default='3.0'}s</div>
</div>
</div>
</div>
</div>
<!-- 最新区块列表 -->
<div class="card bg-dark border-secondary">
<div class="card-header d-flex justify-content-between align-items-center border-secondary">
<span class="fw-bold">
<span class="text-success"></span> 最新区块
</span>
<a href="/blocks" class="btn btn-sm btn-outline-secondary">查看全部</a>
</div>
<div class="table-responsive">
<table class="table table-dark table-hover mb-0" id="block-table">
<thead>
<tr class="text-secondary small">
<th class="ps-3">区块号</th>
<th>区块哈希</th>
<th>出块时间</th>
<th>交易数</th>
<th>出块节点</th>
<th>大小</th>
</tr>
</thead>
<tbody id="block-tbody">
{volist name="blocks" id="block"}
<tr style="{if condition='$block.isHeartbeat'}background:rgba(255,193,7,0.05);{/if}">
<td class="ps-3">
<a href="/block?n={$block.number}" class="{if condition='$block.isHeartbeat'}text-warning{else}text-primary{/if} fw-bold">#{$block.number}</a>
{if condition='$block.isHeartbeat'}<span class="badge bg-warning text-dark ms-1" style="font-size:0.65em" title="{$block.blockTypeNote}">心跳</span>{/if}
</td>
<td>
<a href="/block?n={$block.number}" class="text-info font-monospace small">{$block.shortHash}</a>
</td>
<td class="text-secondary small">{$block.timeAgo}</td>
<td>
{if condition='$block.isHeartbeat'}<span class="text-warning small">心跳块</span>{else}{$block.txCount|default=0}{/if}
</td>
<td class="font-monospace small text-secondary">{$block.shortValidator}</td>
<td class="text-secondary small">{$block.size|default=0} B</td>
</tr>
{/volist}
{empty name="blocks"}
<tr>
<td colspan="6" class="text-center text-secondary py-4">
暂无区块数据,等待链上数据同步...
</td>
</tr>
{/empty}
</tbody>
</table>
</div>
</div>
<!-- WebSocket 实时推送脚本 -->
<script>
(function() {
var wsUrl = 'wss://' + location.host + '/ws';
var ws, reconnectTimer;
var tbodyEl = document.getElementById('block-tbody');
var statBlock = document.getElementById('stat-block');
function shortHash(h, f, b) {
f = f || 10; b = b || 8;
if (!h || h.length <= f + b + 3) return h || 'N/A';
return h.substring(0, f) + '...' + h.substring(h.length - b);
}
function timeAgo(ts) {
var diff = Math.floor(Date.now()/1000) - ts;
if (diff < 60) return diff + ' 秒前';
if (diff < 3600) return Math.floor(diff/60) + ' 分钟前';
return Math.floor(diff/3600) + ' 小时前';
}
function connect() {
try {
ws = new WebSocket(wsUrl);
ws.onopen = function() { clearTimeout(reconnectTimer); };
ws.onmessage = function(e) {
try {
var data = JSON.parse(e.data);
if (data.type === 'new_block' && data.block) {
prependBlock(data.block);
if (statBlock) statBlock.textContent = data.block.number;
}
} catch(err) {}
};
ws.onclose = function() { reconnectTimer = setTimeout(connect, 5000); };
ws.onerror = function() { ws.close(); };
} catch(e) {}
}
function prependBlock(block) {
if (!tbodyEl) return;
// 移除"暂无数据"行
tbodyEl.querySelectorAll('td[colspan]').forEach(function(td) { td.parentNode.remove(); });
// 限制最多20行
var rows = tbodyEl.querySelectorAll('tr');
if (rows.length >= 20) rows[rows.length - 1].remove();
var isHb = block.isHeartbeat || block.blockType === 'heartbeat';
var tr = document.createElement('tr');
tr.className = 'table-active';
tr.style.background = isHb ? 'rgba(255,193,7,0.05)' : '';
var numColor = isHb ? 'text-warning' : 'text-primary';
var hbBadge = isHb ? '<span class="badge bg-warning text-dark ms-1" style="font-size:0.65em" title="CBPP 宪法原则四无交易时每60秒产生心跳块">心跳</span>' : '';
var txCell = isHb ? '<span class="text-warning small">心跳块</span>' : (block.txCount || 0);
tr.innerHTML = '<td class="ps-3"><a href="/block?n=' + block.number + '" class="' + numColor + ' fw-bold">#' + block.number + '</a>' + hbBadge + '</td>'
+ '<td><a href="/block?n=' + block.number + '" class="text-info font-monospace small">' + shortHash(block.hash) + '</a></td>'
+ '<td class="text-secondary small">' + timeAgo(block.timestamp) + '</td>'
+ '<td>' + txCell + '</td>'
+ '<td class="font-monospace small text-secondary">' + shortHash(block.validator, 8, 6) + '</td>'
+ '<td class="text-secondary small">' + (block.size || 0) + ' B</td>';
tbodyEl.insertBefore(tr, tbodyEl.firstChild);
setTimeout(function() { tr.className = ''; }, 2000);
}
connect();
})();
</script>
{include file="index/layout_footer"}