/** * @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 '
' + '
' + Icon.get(t.icon === 'out' ? 'signal' : t.icon === 'in' ? 'link' : t.icon === 'yk' ? 'zap' : t.icon === 'ao' ? 'sliders' : 'settings') + '
' + '
' + count + '
' + '
' + I18n.t(t.key) + '
' + '
' + I18n.t(t.descKey) + '
' + '
'; }).join(''); c.innerHTML = '
' + cards + '
' + '
' + '
' + I18n.t('dash.quick_actions') + '
' + '
' + '' + '' + '' + '' + '
' + '
' + '
' + I18n.t('dash.connection') + '
' + '
' + I18n.t('dash.url') + ': ws://' + location.hostname + ':8080/ws
' + I18n.t('dash.status') + ': ' + I18n.t('status.disconnected') + '
' + '
'; 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 = '\n\n'; var hasData = false; types.forEach(function(st) { var data = SignalStore.get(st); xml += ' \n'; if (data.length > 0) { hasData = true; data.forEach(function(s, i) { xml += ' \n'; }); } xml += ' \n'; }); xml += ''; 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(); } };