RTU_ALL_AI/config/web/js/pages/dashboard.js

106 lines
4.7 KiB
JavaScript

/**
* @file dashboard.js
* @brief Dashboard 仪表盘页面 — 信号卡片 + 快捷操作 + 连接状态
*/
Router.register('#dashboard', function(c) {
var types = [
{ t: 'out', key: 'nav.out', descKey: 'dash.out.desc', color: 'var(--accent)' },
{ t: 'in', key: 'nav.in', descKey: 'dash.in.desc', color: 'var(--accent2)' },
{ t: 'yk', key: 'nav.yk', descKey: 'dash.yk.desc', color: 'var(--warning)' },
{ t: 'ao', key: 'nav.ao', descKey: 'dash.ao.desc', color: 'var(--success)' },
{ t: 'param', key: 'nav.param', descKey: 'dash.param.desc', color: '#a78bfa' },
];
var cards = types.map(function(t) {
var count = SignalStore.get(t.t).length;
return '<div class="dash-card" onclick="window.location.hash=\'#' + t.t + '\'">'
+ '<div class="dash-card-icon">' + Icon.get(t.icon === 'out' ? 'signal' : t.icon === 'in' ? 'link' : t.icon === 'yk' ? 'zap' : t.icon === 'ao' ? 'sliders' : 'settings') + '</div>'
+ '<div class="dash-card-count">' + count + '</div>'
+ '<div class="dash-card-label">' + I18n.t(t.key) + '</div>'
+ '<div style="font-size:11px;color:var(--text-muted);margin-top:4px;">' + I18n.t(t.descKey) + '</div>'
+ '</div>';
}).join('');
c.innerHTML = '<div class="dash-grid">' + cards + '</div>'
+ '<div class="card" style="margin-top:16px;">'
+ '<div class="card-header"><span>' + I18n.t('dash.quick_actions') + '</span></div>'
+ '<div class="btn-group">'
+ '<button class="btn" onclick="DashActions.exportAll()">' + I18n.t('dash.export_xml') + '</button>'
+ '<button class="btn" onclick="DashActions.importAll()">' + I18n.t('dash.import_xml') + '</button>'
+ '<button class="btn btn-sm" onclick="window.location.hash=\'#terminal\'">' + I18n.t('dash.open_terminal') + '</button>'
+ '<button class="btn btn-sm" onclick="window.location.hash=\'#monitor\'">' + I18n.t('dash.open_monitor') + '</button>'
+ '</div></div>'
+ '<div class="card">'
+ '<div class="card-header"><span>' + I18n.t('dash.connection') + '</span></div>'
+ '<div style="font-family:var(--font-mono);font-size:12px;color:var(--text-secondary);">'
+ I18n.t('dash.url') + ': <span style="color:var(--accent);">ws://' + location.hostname + ':8080/ws</span><br>'
+ I18n.t('dash.status') + ': <span id="dash-status" style="color:var(--offline);">' + I18n.t('status.disconnected') + '</span><br>'
+ '<span style="color:var(--text-muted);" id="dash-time"></span></div></div>';
WsClient.onStatus(function(s) {
var el = document.getElementById('dash-status');
if (el)
{
el.textContent = I18n.t('status.' + s) || s;
el.style.color = s === 'connected' ? 'var(--online)' : s === 'connecting' ? 'var(--warning)' : 'var(--offline)';
}
});
});
var DashActions = {
exportAll: function() {
var types = ['out', 'in', 'yk', 'ao', 'param'];
var xml = '<?xml version="1.0" encoding="UTF-8"?>\n<root>\n';
var hasData = false;
types.forEach(function(st) {
var data = SignalStore.get(st);
xml += ' <signals type="' + st + '">\n';
if (data.length > 0) { hasData = true; data.forEach(function(s, i) { xml += ' <signal no="' + i + '" saddr="' + esc(s.saddr) + '"/>\n'; }); }
xml += ' </signals>\n';
});
xml += '</root>';
if (!hasData) { toast(I18n.t('toast.no_export'), 'error'); return; }
var blob = new Blob([xml], { type: 'application/xml' });
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'rtu_signals_' + Date.now() + '.xml';
a.click();
toast(I18n.t('toast.xml_exported'), 'success');
},
importAll: function() {
var input = document.createElement('input');
input.type = 'file';
input.accept = '.xml';
input.onchange = function(e) {
var file = e.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function(ev) {
var parser = new DOMParser();
var doc = parser.parseFromString(ev.target.result, 'text/xml');
var err = doc.querySelector('parsererror');
if (err) { toast(I18n.t('toast.xml_parse_error') + ': ' + err.textContent, 'error'); return; }
var nodes = doc.querySelectorAll('signals');
if (nodes.length === 0) { toast(I18n.t('toast.xml_no_signals'), 'error'); return; }
var count = 0;
nodes.forEach(function(el) {
var type = el.getAttribute('type');
if (['out', 'in', 'yk', 'ao', 'param'].indexOf(type) === -1) return;
el.querySelectorAll('signal').forEach(function(sig) {
var saddr = sig.getAttribute('saddr');
if (!saddr || !saddr.trim()) return;
WsClient.send({ saddr: saddr.trim(), signal_type: type, curd: 'add', setting_zone: '0', signal_data: '' });
count++;
});
});
toast(I18n.t('toast.xml_importing') + ' (' + count + ')', 'success');
};
reader.readAsText(file);
};
input.click();
}
};