feat: 查看钱包优化,地址增加链上查询
This commit is contained in:
@@ -22,7 +22,25 @@
|
||||
.addr-chain-unknown, .addr-chain-unkno { background: #909399; }
|
||||
.addr-monitor-on { color: #16b777; font-weight: 600; }
|
||||
.addr-monitor-off { color: #ff5722; font-weight: 600; }
|
||||
.addr-cell { word-break: break-all; font-family: Menlo, Monaco, Consolas, monospace; font-size: 12px; }
|
||||
.addr-cell { font-family: Menlo, Monaco, Consolas, monospace; font-size: 12px; }
|
||||
.addr-cell-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.addr-cell-row .addr-text {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.addr-cell-row .addr-explorer {
|
||||
flex: 0 0 auto;
|
||||
font-family: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
<div class="layui-card">
|
||||
<form class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-addr-search">
|
||||
@@ -145,6 +163,39 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
return '<span class="' + cls + '">' + raw + '</span>';
|
||||
}
|
||||
|
||||
function explorerUrl(chain, address) {
|
||||
var addr = String(address || '').trim();
|
||||
if (!addr) return '';
|
||||
var c = String(chain || '').trim().toUpperCase();
|
||||
if (c === 'TRON' || c === 'TRX') return 'https://tronscan.org/#/address/' + encodeURIComponent(addr);
|
||||
if (c === 'ETH' || c === 'ETHEREUM' || c === 'EVM') return 'https://etherscan.io/address/' + encodeURIComponent(addr);
|
||||
if (c === 'BSC' || c === 'BNB' || c === 'BINANCE') return 'https://bscscan.com/address/' + encodeURIComponent(addr);
|
||||
if (c === 'BTC' || c === 'BITCOIN') return 'https://mempool.space/address/' + encodeURIComponent(addr);
|
||||
if (c === 'SOL' || c === 'SOLANA') return 'https://solscan.io/account/' + encodeURIComponent(addr);
|
||||
return '';
|
||||
}
|
||||
|
||||
function escHtml(s) {
|
||||
return String(s == null ? '' : s)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function addressCellHtml(d) {
|
||||
var addr = d.address || '';
|
||||
var url = explorerUrl(d.chain_type, addr);
|
||||
var html = '<div class="addr-cell addr-cell-row">';
|
||||
html += '<span class="addr-text" title="' + escHtml(addr) + '">' + escHtml(addr) + '</span>';
|
||||
if (url) {
|
||||
html += '<a class="layui-btn layui-btn-primary layui-btn-xs addr-explorer" href="' + escHtml(url) +
|
||||
'" target="_blank" rel="noopener noreferrer">链上查询</a>';
|
||||
}
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function assetsForRow(d) {
|
||||
var type = String(d.chain_type || '').toUpperCase();
|
||||
var addr = String(d.address || '');
|
||||
@@ -243,9 +294,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
{ field: 'id', title: 'ID', width: 70, sort: true },
|
||||
{ field: 'source', title: '来源', width: 120 },
|
||||
{ field: 'device_key', title: '设备 ID', width: 150, sort: true },
|
||||
{ field: 'address', title: '地址', minWidth: 360, templet: function (d) {
|
||||
return '<span class="addr-cell">' + (d.address || '') + '</span>';
|
||||
}},
|
||||
{ field: 'address', title: '地址', minWidth: 420, templet: addressCellHtml },
|
||||
{ field: 'chain_type', title: 'Chain', width: 110, templet: function (d) {
|
||||
return chainTag(d.chain_type);
|
||||
}},
|
||||
|
||||
@@ -28,9 +28,27 @@
|
||||
}
|
||||
.mn-wallet-table th { background: #fafafa; font-weight: 600; white-space: nowrap; }
|
||||
.mn-wallet-table .addr-cell {
|
||||
word-break: break-all;
|
||||
font-family: Menlo, Monaco, Consolas, monospace;
|
||||
max-width: 280px;
|
||||
max-width: 360px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.mn-wallet-table .addr-cell .addr-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.mn-wallet-table .addr-cell .addr-text {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mn-wallet-table .addr-cell .mn-explorer {
|
||||
flex: 0 0 auto;
|
||||
font-family: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mn-wallet-table .coin-cell { white-space: nowrap; font-variant-numeric: tabular-nums; }
|
||||
.mn-inactive {
|
||||
@@ -45,6 +63,7 @@
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.mn-updating { color: #1e9fff; }
|
||||
</style>
|
||||
<div class="layui-card">
|
||||
<form class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="LAY-mn-search">
|
||||
@@ -114,13 +133,40 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
return (v === null || v === undefined || v === '') ? '—' : esc(v);
|
||||
}
|
||||
|
||||
function explorerUrl(chain, address) {
|
||||
var addr = String(address || '').trim();
|
||||
if (!addr) return '';
|
||||
var c = String(chain || '').trim().toUpperCase();
|
||||
if (c === 'TRON' || c === 'TRX') return 'https://tronscan.org/#/address/' + encodeURIComponent(addr);
|
||||
if (c === 'ETH' || c === 'ETHEREUM' || c === 'EVM') return 'https://etherscan.io/address/' + encodeURIComponent(addr);
|
||||
if (c === 'BSC' || c === 'BNB' || c === 'BINANCE') return 'https://bscscan.com/address/' + encodeURIComponent(addr);
|
||||
if (c === 'BTC' || c === 'BITCOIN') return 'https://mempool.space/address/' + encodeURIComponent(addr);
|
||||
if (c === 'SOL' || c === 'SOLANA') return 'https://solscan.io/account/' + encodeURIComponent(addr);
|
||||
return '';
|
||||
}
|
||||
|
||||
function addrCellHtml(r) {
|
||||
var addr = r.address || '';
|
||||
var url = explorerUrl(r.chain_type, addr);
|
||||
var html = '<td class="addr-cell"><div class="addr-row">';
|
||||
html += '<span class="addr-text" title="' + esc(addr) + '">' + esc(addr) + '</span>';
|
||||
if (url) {
|
||||
html += '<a class="layui-btn layui-btn-primary layui-btn-xs mn-explorer" href="' + esc(url) +
|
||||
'" target="_blank" rel="noopener noreferrer">链上查询</a>';
|
||||
}
|
||||
html += '</div></td>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderWalletsHtml(payload) {
|
||||
var rows = (payload && payload.addresses) || [];
|
||||
var html = '<div class="mn-wallet-wrap">';
|
||||
html += '<div class="mn-wallet-hd">';
|
||||
html += '<div class="mn-wallet-meta">助记词 #' + esc(payload.mnemonic_id) +
|
||||
(payload.source ? ' · ' + esc(payload.source) : '') +
|
||||
' · 关联地址 ' + rows.length + ' 个</div>';
|
||||
' · 关联地址 ' + rows.length + ' 个' +
|
||||
(payload.updating ? ' · <span class="mn-updating">正在更新</span>' : '') +
|
||||
'</div>';
|
||||
if (rows.length && payload.refresh_url) {
|
||||
html += '<button type="button" class="layui-btn layui-btn-sm" id="LAY-mn-refresh-bal">刷新余额</button>';
|
||||
}
|
||||
@@ -139,7 +185,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
'<td>' + (r.id == null || r.id === '' ? '—' : esc(r.id)) + '</td>' +
|
||||
'<td>' + esc(r.chain_type || '—') +
|
||||
(inactive ? '<span class="mn-inactive">未激活</span>' : '') + '</td>' +
|
||||
'<td class="addr-cell">' + esc(r.address) + '</td>' +
|
||||
addrCellHtml(r) +
|
||||
'<td>' + (r.derive_index == null || r.derive_index === '' ? '—' : esc(r.derive_index)) + '</td>' +
|
||||
'<td class="coin-cell">' + coinVal(r.usdt) + '</td>' +
|
||||
'<td class="coin-cell">' + coinVal(r.trx) + '</td>' +
|
||||
@@ -166,25 +212,36 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
bindRefresh(layero, payload);
|
||||
}
|
||||
|
||||
function bindRefresh(layero, payload) {
|
||||
function bindRefresh(layero, payload, auto) {
|
||||
layero.find('#LAY-mn-refresh-bal').off('click').on('click', function () {
|
||||
var btn = $(this);
|
||||
if (btn.prop('disabled')) return;
|
||||
if (!payload.refresh_url) return;
|
||||
btn.prop('disabled', true).addClass('layui-btn-disabled');
|
||||
var loadIdx = layer.load(1, { shade: 0.1 });
|
||||
$.ajax({
|
||||
url: payload.refresh_url,
|
||||
method: 'POST',
|
||||
data: { _token: token },
|
||||
success: function (res) {
|
||||
layer.close(loadIdx);
|
||||
if (!res || res.code !== 0) {
|
||||
btn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
return layer.msg((res && res.msg) || '刷新失败');
|
||||
}
|
||||
var next = res.data || {};
|
||||
applyWalletsHtml(layero, next);
|
||||
runWalletRefresh(layero, payload, false);
|
||||
});
|
||||
if (auto && payload.updating && payload.refresh_url) {
|
||||
runWalletRefresh(layero, payload, true);
|
||||
}
|
||||
}
|
||||
|
||||
function runWalletRefresh(layero, payload, auto) {
|
||||
var btn = layero.find('#LAY-mn-refresh-bal');
|
||||
if (!payload.refresh_url) return;
|
||||
if (btn.prop('disabled') && !auto) return;
|
||||
btn.prop('disabled', true).addClass('layui-btn-disabled');
|
||||
var loadIdx = auto ? null : layer.load(1, { shade: 0.1 });
|
||||
$.ajax({
|
||||
url: payload.refresh_url,
|
||||
method: 'POST',
|
||||
data: { _token: token },
|
||||
success: function (res) {
|
||||
if (loadIdx != null) layer.close(loadIdx);
|
||||
if (!res || res.code !== 0) {
|
||||
btn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
if (!auto) layer.msg((res && res.msg) || '刷新失败');
|
||||
return;
|
||||
}
|
||||
var next = res.data || {};
|
||||
next.updating = false;
|
||||
applyWalletsHtml(layero, next);
|
||||
if (!auto) {
|
||||
var ok = Number(next.refreshed || 0);
|
||||
var fail = Number(next.failed || 0);
|
||||
layer.msg(fail ? ('已写入地址表 ' + ok + ' 个,失败 ' + fail + ' 个') : ('已写入地址表 ' + ok + ' 个'));
|
||||
@@ -194,22 +251,30 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
setTimeout(function () {
|
||||
nextBtn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
}, cooldown * 1000);
|
||||
},
|
||||
error: function (xhr) {
|
||||
layer.close(loadIdx);
|
||||
var body = xhr.responseJSON || {};
|
||||
var retry = body.data && body.data.retry_after ? Number(body.data.retry_after) : 0;
|
||||
if (xhr.status === 429 && retry > 0) {
|
||||
layer.msg(body.msg || ('请 ' + retry + ' 秒后再试'));
|
||||
setTimeout(function () {
|
||||
btn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
}, retry * 1000);
|
||||
return;
|
||||
}
|
||||
btn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
layer.msg(body.msg || body.message || '刷新失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (xhr) {
|
||||
if (loadIdx != null) layer.close(loadIdx);
|
||||
var body = xhr.responseJSON || {};
|
||||
var retry = body.data && body.data.retry_after ? Number(body.data.retry_after) : 0;
|
||||
if (xhr.status === 429) {
|
||||
if (!auto) layer.msg(body.msg || ('请 ' + retry + ' 秒后再试'));
|
||||
payload.updating = false;
|
||||
applyWalletsHtml(layero, payload);
|
||||
if (!auto && retry > 0) {
|
||||
var nextBtn = layero.find('#LAY-mn-refresh-bal');
|
||||
nextBtn.prop('disabled', true).addClass('layui-btn-disabled');
|
||||
setTimeout(function () {
|
||||
nextBtn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
}, retry * 1000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
btn.prop('disabled', false).removeClass('layui-btn-disabled');
|
||||
payload.updating = false;
|
||||
applyWalletsHtml(layero, payload);
|
||||
if (!auto) layer.msg(body.msg || body.message || '刷新失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -234,7 +299,7 @@ layui.use(['table', 'form', 'layer'], function () {
|
||||
shadeClose: true,
|
||||
content: renderWalletsHtml(payload),
|
||||
success: function (layero) {
|
||||
bindRefresh(layero, payload);
|
||||
bindRefresh(layero, payload, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user