feat(frontend): 信号管理页面重构 + 增量刷新 + 删除即时生效
核心变更: - signal-store: 移除 localStorage 持久化, WS 断连清空; update() 新增增量 DOM 刷新 (仅更新变化的单元格); remove() 直接移除 DOM 行 (动画 fade out) - router: 新增 reload() 强制重绘当前页 - ui: renderSignalPage 重写为统一列格式 #|saddr|desc|type|val|[ctrl]|操作; 事件委托替代 inline onclick; 内联输入框替代弹窗设值 - datacenter: 改用后端 dc_data JSON 替代文本解析 (修复 desc 含空格错位); 移除无用的 ctrl 列 - plc_config: 完整重写 — 查看/编辑双模式, 左右分栏布局, 信号面板+逻辑图查看(当前/备份)+编辑器基础选择 - plc_debug: 切换按钮后同步更新 SignalStore (修复 LED 延迟刷新) - param: 内联输入框 + 序号列 修复: - 删除信号后 DOM 行立即消失 (之前需切页才能看到) - WS 推送到达时增量刷新, 不影响正在编辑的输入框 - 批量删除时每项都调用 SignalStore.remove
This commit is contained in:
parent
ccb26007e2
commit
e27b01943c
|
|
@ -208,15 +208,15 @@ a { color: var(--accent); text-decoration: none; }
|
|||
Tables
|
||||
============================================================ */
|
||||
.table-wrap { overflow-x: auto; border-radius: var(--radius-md); border: 1px solid var(--border); }
|
||||
.signal-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.signal-table { width: 100%; border-collapse: collapse; font-size: 13px; table-layout: fixed; }
|
||||
.signal-table th {
|
||||
background: var(--bg-surface); padding: 10px 14px;
|
||||
background: var(--bg-surface); padding: 10px 8px;
|
||||
text-align: left; font-weight: 600; font-size: 11px;
|
||||
text-transform: uppercase; letter-spacing: 0.5px;
|
||||
color: var(--text-secondary); border-bottom: 1px solid var(--border);
|
||||
position: sticky; top: 0; z-index: 1;
|
||||
position: sticky; top: 0; z-index: 1; white-space: nowrap;
|
||||
}
|
||||
.signal-table td { padding: 9px 14px; border-bottom: 1px solid rgba(30,48,64,0.6); }
|
||||
.signal-table td { padding: 9px 8px; border-bottom: 1px solid rgba(30,48,64,0.6); overflow: hidden; text-overflow: ellipsis; }
|
||||
.signal-table tbody tr { transition: background var(--transition); }
|
||||
.signal-table tbody tr:hover { background: var(--bg-hover); }
|
||||
.signal-table .empty-row td { text-align: center; color: var(--text-muted); padding: 40px; }
|
||||
|
|
@ -537,14 +537,48 @@ a { color: var(--accent); text-decoration: none; }
|
|||
#plc-zoom-val { font-family: var(--font-mono); font-weight: 600; color: var(--text-primary); }
|
||||
|
||||
/* ============================================================
|
||||
PLC Config (stub - preserved for backward compat)
|
||||
PLC Config — 信号面板 / 逻辑图查看器 / 编辑器
|
||||
============================================================ */
|
||||
.plc-canvas-wrap {
|
||||
background: var(--bg-card); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md); height: calc(100vh - 200px);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--text-muted); font-size: 14px;
|
||||
.plc-sig-row {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 4px 6px; margin: 2px 0; border-radius: 4px;
|
||||
cursor: pointer; transition: background var(--transition);
|
||||
}
|
||||
.plc-sig-row:hover { background: var(--bg-hover); }
|
||||
.plc-modal-row {
|
||||
padding: 8px 10px; margin: 2px 0; border-radius: 4px;
|
||||
cursor: pointer; transition: background var(--transition);
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.plc-modal-row:hover { background: var(--bg-hover); border-color: var(--border); }
|
||||
.plc-modal-overlay {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.6); display: flex;
|
||||
align-items: center; justify-content: center;
|
||||
z-index: 99990;
|
||||
}
|
||||
.plc-modal-box {
|
||||
background: var(--bg-card); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg); padding: 24px;
|
||||
box-shadow: var(--shadow); color: var(--text-primary);
|
||||
max-height: 80vh; overflow-y: auto;
|
||||
}
|
||||
.plc-modal-box h3 { margin-bottom: 12px; font-size: 16px; font-weight: 600; }
|
||||
.plc-cfg-tabs { display: flex; gap: 0; margin-bottom: 12px; }
|
||||
.plc-cfg-tab {
|
||||
padding: 8px 16px; background: var(--bg-surface);
|
||||
border: 1px solid var(--border); border-right: none;
|
||||
cursor: pointer; font-size: 13px; color: var(--text-secondary);
|
||||
transition: all var(--transition);
|
||||
}
|
||||
.plc-cfg-tab:first-child { border-radius: var(--radius-md) 0 0 var(--radius-md); }
|
||||
.plc-cfg-tab:last-child { border-radius: 0 var(--radius-md) var(--radius-md) 0; border-right: 1px solid var(--border); }
|
||||
.plc-cfg-tab.active { background: var(--accent); color: var(--bg-deep); border-color: var(--accent); font-weight: 600; }
|
||||
.plc-sig-search { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
|
||||
.plc-sig-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
.plc-sig-table th { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); color: var(--text-secondary); font-weight: 600; }
|
||||
.plc-sig-table td { padding: 4px 8px; border-bottom: 1px solid rgba(255,255,255,0.04); }
|
||||
.plc-sig-table tr:hover td { background: var(--bg-hover); }
|
||||
|
||||
/* ============================================================
|
||||
Login Page
|
||||
|
|
|
|||
|
|
@ -1,58 +1,83 @@
|
|||
/**
|
||||
* @file param.js
|
||||
* @brief 定值管理页面 — 多定值区展开
|
||||
* @brief 定值管理页面 — 多定值区展开 + 内联输入框
|
||||
*/
|
||||
|
||||
|
||||
Router.register('#param', function(c) {
|
||||
var signals = SignalStore.get('param');
|
||||
var rows = '';
|
||||
|
||||
if (signals.length === 0)
|
||||
{
|
||||
rows = '<tr class="empty-row"><td colspan="5">' + I18n.t('signal.no_data') + '</td></tr>';
|
||||
rows = '<tr class="empty-row"><td colspan="6">' + I18n.t('signal.no_data') + '</td></tr>';
|
||||
}
|
||||
else
|
||||
{
|
||||
signals.forEach(function(s) {
|
||||
signals.forEach(function(s, idx) {
|
||||
var zones = s.setting_zone_list || [];
|
||||
var zoneHtml = '';
|
||||
zones.forEach(function(z) {
|
||||
zoneHtml += '<div style="display:flex;gap:8px;align-items:center;padding:2px 0;">'
|
||||
+ '<span style="color:var(--text-muted);font-family:var(--font-mono);width:40px;">#' + esc(z.id) + '</span>'
|
||||
+ '<span style="font-family:var(--font-mono);color:var(--accent);">' + esc(z.val) + '</span>'
|
||||
+ '<button class="btn btn-sm" onclick="ParamActions.set(\'' + esc(s.saddr) + '\',\'' + esc(z.id) + '\')">' + I18n.t('signal.set') + '</button>'
|
||||
zoneHtml += '<div style="display:flex;gap:6px;align-items:center;padding:2px 0;">'
|
||||
+ '<span style="color:var(--text-muted);font-family:var(--font-mono);width:28px;font-size:11px">#' + esc(z.id) + '</span>'
|
||||
+ '<span class="param-zone-val" data-zone="' + esc(z.id) + '" style="font-family:var(--font-mono);color:var(--accent);font-size:12px">' + esc(z.val) + '</span>'
|
||||
+ '<input class="param-zone-input" data-action="param-set" data-saddr="' + esc(s.saddr) + '" data-zone="' + esc(z.id) + '" placeholder="new" style="width:50px;padding:1px 4px;font-size:11px;border:1px solid var(--border);border-radius:3px;background:var(--bg-input);color:var(--text-primary);font-family:var(--font-mono)">'
|
||||
+ '<button class="btn btn-sm" data-action="param-set-go" data-saddr="' + esc(s.saddr) + '" data-zone="' + esc(z.id) + '" style="padding:1px 5px;font-size:11px">' + I18n.t('signal.set') + '</button>'
|
||||
+ '</div>';
|
||||
});
|
||||
rows += '<tr>'
|
||||
rows += '<tr data-saddr="' + esc(s.saddr) + '">'
|
||||
+ '<td class="cell-mono" style="color:var(--text-muted)">' + (idx + 1) + '</td>'
|
||||
+ '<td class="cell-mono">' + esc(s.saddr) + '</td>'
|
||||
+ '<td class="cell-desc">' + esc(s.desc || '') + '</td>'
|
||||
+ '<td class="cell-mono">' + esc(s.type || '?') + '</td>'
|
||||
+ '<td>' + (zoneHtml || '\u2014') + '</td>'
|
||||
+ '<td><button class="btn btn-danger btn-sm" onclick="SignalActions.del(\'param\',\'' + esc(s.saddr) + '\')">' + Icon.get('trash') + '</button></td>'
|
||||
+ '<td><button class="btn btn-danger btn-sm" data-action="del" data-saddr="' + esc(s.saddr) + '">' + Icon.get('trash') + '</button></td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
}
|
||||
|
||||
c.innerHTML = '<div class="card">'
|
||||
+ '<div class="card-header"><span>PARAM \u2014 ' + signals.length + ' signals</span>'
|
||||
+ '<div class="card-header"><span>PARAM — ' + signals.length + ' signals</span>'
|
||||
+ '<button class="btn btn-primary btn-sm" onclick="SignalActions.add(\'param\')">' + Icon.get('plus') + ' ' + I18n.t('signal.add') + '</button>'
|
||||
+ '</div>'
|
||||
+ '<div class="table-wrap"><table class="signal-table">'
|
||||
+ '<thead><tr><th>' + I18n.t('signal.saddr') + '</th><th>' + I18n.t('signal.desc') + '</th><th>' + I18n.t('signal.type') + '</th><th>' + I18n.t('signal.zone') + '</th><th>' + I18n.t('signal.action') + '</th></tr></thead>'
|
||||
+ '<tbody>' + rows + '</tbody>'
|
||||
+ '<thead><tr><th style="width:36px">#</th><th>' + I18n.t('signal.saddr') + '</th><th>' + I18n.t('signal.desc') + '</th><th>' + I18n.t('signal.type') + '</th><th>' + I18n.t('signal.zone') + '</th><th>' + I18n.t('signal.action') + '</th></tr></thead>'
|
||||
+ '<tbody data-type="param">' + rows + '</tbody>'
|
||||
+ '</table></div></div>';
|
||||
});
|
||||
|
||||
var ParamActions = {
|
||||
set: function(saddr, zoneId) {
|
||||
showModal(I18n.t('signal.set_title') + ' \u2014 ' + esc(saddr) + ' ' + I18n.t('signal.zone') + ' ' + zoneId,
|
||||
'<div class="form-group"><label class="form-label">' + I18n.t('signal.new_val') + '</label><input class="form-input" id="mf-val" placeholder="Value"></div>',
|
||||
function(ov) {
|
||||
var val = getModalVal(ov, '#mf-val');
|
||||
WsClient.send({ saddr: saddr, signal_type: 'param', curd: 'set', signal_data: val, setting_zone: zoneId });
|
||||
toast(I18n.t('toast.set_sent') + ': ' + saddr + '#' + zoneId + ' = ' + val, 'success');
|
||||
ov.remove();
|
||||
}, I18n.t('signal.set'));
|
||||
/* Param event delegation */
|
||||
document.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('[data-action="param-set-go"]');
|
||||
if (!btn) return;
|
||||
var saddr = btn.getAttribute('data-saddr');
|
||||
var zone = btn.getAttribute('data-zone') || '0';
|
||||
var row = btn.closest('tr');
|
||||
var inp = row ? row.querySelector('[data-action="param-set"][data-zone="' + zone + '"]') : null;
|
||||
var val = inp ? inp.value.trim() : '';
|
||||
if (!val) { toast(I18n.t('toast.val_required'), 'error'); return; }
|
||||
WsClient.send({ saddr: saddr, signal_type: 'param', curd: 'set', signal_data: val, setting_zone: zone });
|
||||
/* 立即更新本地 SignalStore + DOM */
|
||||
var arr2 = SignalStore.get('param');
|
||||
for (var k2 = 0; k2 < arr2.length; k2++) {
|
||||
if (arr2[k2].saddr === saddr && arr2[k2].setting_zone_list) {
|
||||
for (var z = 0; z < arr2[k2].setting_zone_list.length; z++) {
|
||||
if (arr2[k2].setting_zone_list[z].id === zone) { arr2[k2].setting_zone_list[z].val = val; break; }
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
var zc = row.querySelector('.param-zone-val[data-zone="' + zone + '"]');
|
||||
if (zc) { zc.textContent = val; zc.style.transition = 'color 0.3s'; zc.style.color = '#2ed573'; setTimeout(function(){zc.style.color='';}, 800); }
|
||||
inp.value = '';
|
||||
});
|
||||
/* Enter key on param inline input */
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter' && e.target.classList.contains('param-zone-input')) {
|
||||
e.preventDefault();
|
||||
var inp = e.target;
|
||||
var row = inp.closest('tr');
|
||||
var zone = inp.getAttribute('data-zone');
|
||||
var btn = row ? row.querySelector('[data-action="param-set-go"][data-zone="' + zone + '"]') : null;
|
||||
if (btn) btn.click();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -192,7 +192,13 @@ var PlcDebugPage = (function() {
|
|||
if (!b.saddr) { showPicker(function(sa, de) { b.saddr = sa; b.desc = de; var f = findSig(sa); if (f) b.val = f.val; renderAll(); }); return; }
|
||||
var nv = parseFloat(b.val) > 0 ? 0 : 1;
|
||||
WsClient.send({ curd: 'set', signal_type: 'out', saddr: b.saddr, signal_data: String(nv), setting_zone: '0' });
|
||||
b.val = nv; renderAll();
|
||||
b.val = nv;
|
||||
/* 同步写入 SignalStore,确保 refreshData() 不覆盖新值 */
|
||||
var outs = SignalStore.get('out');
|
||||
for (var i = 0; i < outs.length; i++) {
|
||||
if (outs[i].saddr === b.saddr) { outs[i].val = String(nv); break; }
|
||||
}
|
||||
renderAll();
|
||||
},
|
||||
_toggleLed: function(i, v) { ledChecked[i] = v; updateDelBtn(); },
|
||||
_toggleBtnChk: function(j, v) { btnChecked[j] = v; updateDelBtn(); },
|
||||
|
|
@ -210,6 +216,7 @@ var PlcDebugPage = (function() {
|
|||
sigChecked = {}; renderAll();
|
||||
},
|
||||
_refresh: function() { refreshData(); incrRefresh(); },
|
||||
incrRefresh: function() { refreshData(); incrRefresh(); },
|
||||
_delChecked: delChecked,
|
||||
};
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -104,41 +104,60 @@ function th(s) {
|
|||
/**
|
||||
* @brief 渲染信号管理页面通用模板
|
||||
*/
|
||||
function renderSignalPage(type, headers, extraCols, actionsForRow) {
|
||||
function renderSignalPage(type, showCheckbox, extraHeader, extraCell) {
|
||||
var signals = SignalStore.get(type);
|
||||
var rows = '';
|
||||
var colCount = (showCheckbox ? 1 : 0) + 5 + (extraHeader ? 1 : 0) + 1;
|
||||
|
||||
if (signals.length === 0)
|
||||
{
|
||||
rows = '<tr class="empty-row"><td colspan="' + headers.length + '">' + I18n.t('signal.no_data') + '</td></tr>';
|
||||
rows = '<tr class="empty-row"><td colspan="' + colCount + '">' + I18n.t('signal.no_data') + '</td></tr>';
|
||||
}
|
||||
else
|
||||
{
|
||||
signals.forEach(function(s) {
|
||||
var xtra = extraCols ? extraCols(s) : '';
|
||||
var acts = actionsForRow ? actionsForRow(s) : '';
|
||||
rows += '<tr>'
|
||||
signals.forEach(function(s, idx) {
|
||||
var xtra = extraCell ? extraCell(s) : '';
|
||||
var acts = (type === 'out' || type === 'yk' || type === 'ao')
|
||||
? '<input class="sig-set-input" data-action="set-inline" data-saddr="' + esc(s.saddr) + '" placeholder="value" style="width:55px;padding:2px 4px;font-size:12px;border:1px solid var(--border);border-radius:3px;background:var(--bg-input);color:var(--text-primary);font-family:var(--font-mono)">'
|
||||
+ '<button class="btn btn-sm" data-action="set-go" data-saddr="' + esc(s.saddr) + '" style="padding:2px 6px;font-size:12px">' + I18n.t('signal.set') + '</button>'
|
||||
+ '<button class="btn btn-danger btn-sm" data-action="del" data-saddr="' + esc(s.saddr) + '">' + Icon.get('trash') + '</button>'
|
||||
: '<button class="btn btn-danger btn-sm" data-action="del" data-saddr="' + esc(s.saddr) + '">' + Icon.get('trash') + '</button>';
|
||||
rows += '<tr data-saddr="' + esc(s.saddr) + '">'
|
||||
+ (showCheckbox ? '<td><input type="checkbox"></td>' : '')
|
||||
+ '<td class="cell-mono" style="color:var(--text-muted)">' + (idx + 1) + '</td>'
|
||||
+ '<td class="cell-mono">' + esc(s.saddr) + '</td>'
|
||||
+ '<td class="cell-desc">' + esc(s.desc || '') + '</td>'
|
||||
+ xtra
|
||||
+ '<td class="cell-mono">' + esc(s.type || '?') + '</td>'
|
||||
+ '<td class="cell-val">' + fmtVal(s.val) + '</td>'
|
||||
+ '<td>' + acts + '</td>'
|
||||
+ xtra
|
||||
+ '<td class="cell-acts" data-saddr="' + esc(s.saddr) + '">' + acts + '</td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
}
|
||||
|
||||
var headerHtml = '';
|
||||
if (showCheckbox) headerHtml += '<th style="width:36px"><input type="checkbox" id="cb-all-' + type + '" onclick="var v=this.checked;document.querySelectorAll(\'#tbody-' + type + ' input[type=checkbox]\').forEach(function(c){c.checked=v;})"></th>';
|
||||
headerHtml += '<th style="width:36px">#</th>';
|
||||
headerHtml += '<th>' + I18n.t('signal.saddr') + '</th>';
|
||||
headerHtml += '<th>' + I18n.t('signal.desc') + '</th>';
|
||||
headerHtml += '<th>' + I18n.t('signal.type') + '</th>';
|
||||
headerHtml += '<th>' + I18n.t('signal.val') + '</th>';
|
||||
if (extraHeader) headerHtml += '<th>' + extraHeader + '</th>';
|
||||
headerHtml += '<th>' + I18n.t('signal.action') + '</th>';
|
||||
|
||||
return '<div class="card">'
|
||||
+ '<div class="card-header"><span>' + type.toUpperCase() + ' \u2014 ' + signals.length + ' signals</span>'
|
||||
+ '<div class="card-header"><span>' + type.toUpperCase() + ' — ' + signals.length + ' signals</span>'
|
||||
+ '<div class="btn-group">'
|
||||
+ '<button class="btn btn-primary btn-sm" onclick="SignalActions.add(\'' + type + '\')">' + Icon.get('plus') + ' ' + I18n.t('signal.add') + '</button>'
|
||||
+ '<button class="btn btn-danger btn-sm" onclick="SignalActions.batchDel(\'' + type + '\')">' + Icon.get('trash') + ' ' + I18n.t('signal.delete_sel') + '</button>'
|
||||
+ '</div></div>'
|
||||
+ '<div class="table-wrap"><table class="signal-table">'
|
||||
+ '<thead><tr>' + headers.map(function(h) { return th(h); }).join('') + '<th>' + I18n.t('signal.val') + '</th><th>' + I18n.t('signal.action') + '</th></tr></thead>'
|
||||
+ '<tbody id="tbody-' + type + '">' + rows + '</tbody>'
|
||||
+ '<thead><tr>' + headerHtml + '</tr></thead>'
|
||||
+ '<tbody id="tbody-' + type + '" data-type="' + type + '">' + rows + '</tbody>'
|
||||
+ '</table></div></div>';
|
||||
}
|
||||
|
||||
|
||||
/* ==================================================================
|
||||
* 信号 CRUD 操作
|
||||
* ================================================================== */
|
||||
|
|
@ -147,33 +166,21 @@ var SignalActions = {
|
|||
var body = '';
|
||||
body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.saddr') + '</label><input class="form-input" id="mf-saddr" placeholder="e.g. plc.st.out.0"></div>';
|
||||
body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.desc') + '</label><input class="form-input" id="mf-desc" placeholder="Signal description"></div>';
|
||||
|
||||
if (type === 'in')
|
||||
{
|
||||
body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.link') + '</label><input class="form-input" id="mf-link" placeholder="e.g. self_ptl.st.0"></div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.type') + '</label>'
|
||||
+ '<select class="form-select" id="mf-dtype">'
|
||||
+ '<option value="U8">U8</option><option value="U16">U16</option>'
|
||||
+ '<option value="U32">U32</option><option value="F32">F32</option>'
|
||||
+ '<option value="STR">STR</option></select></div>';
|
||||
}
|
||||
|
||||
if (type === 'yk' || type === 'ao' || type === 'param')
|
||||
{
|
||||
body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.type') + '</label>'
|
||||
+ '<select class="form-select" id="mf-dtype">'
|
||||
+ '<option value="U8">U8</option><option value="U16">U16</option>'
|
||||
+ '<option value="U32">U32</option><option value="F32">F32</option>'
|
||||
+ '<option value="STR">STR</option></select></div>';
|
||||
if (type === 'yk' || type === 'ao' || type === 'param') {
|
||||
body += '<div class="form-group"><label class="form-label">' + I18n.t('signal.ctrl') + '</label>'
|
||||
+ '<select class="form-select" id="mf-ctrl">'
|
||||
+ '<option value="0">' + I18n.t('signal.ctrl_direct') + '</option>'
|
||||
+ '<option value="1">' + I18n.t('signal.ctrl_sbo') + '</option></select></div>';
|
||||
}
|
||||
|
||||
showModal(I18n.t('signal.add_title') + ' (' + type.toUpperCase() + ')', body, function(ov) {
|
||||
var saddr = getModalVal(ov, '#mf-saddr');
|
||||
if (!saddr) { toast(I18n.t('toast.saddr_required'), 'error'); return; }
|
||||
var msg = { saddr: saddr, signal_type: type, curd: 'add', signal_data: '', setting_zone: '0' };
|
||||
if (type === 'in') { msg.link_saddr = getModalVal(ov, '#mf-link'); }
|
||||
WsClient.send(msg);
|
||||
toast(I18n.t('toast.add_sent') + ': ' + saddr, 'success');
|
||||
ov.remove();
|
||||
|
|
@ -186,7 +193,6 @@ var SignalActions = {
|
|||
function(ov) {
|
||||
WsClient.send({ saddr: saddr, signal_type: type, curd: 'del', setting_zone: '0' });
|
||||
SignalStore.remove(type, saddr);
|
||||
Router.dispatch(Router.currentHash);
|
||||
toast(I18n.t('toast.del_sent') + ': ' + saddr, 'info');
|
||||
ov.remove();
|
||||
}, Icon.get('trash') + ' ' + I18n.t('signal.delete'));
|
||||
|
|
@ -199,8 +205,12 @@ var SignalActions = {
|
|||
'<p>' + I18n.t('signal.confirm_batch_msg') + ' ' + checked.length + ' ' + I18n.t('signal.confirm_batch_del') + '?</p>',
|
||||
function(ov) {
|
||||
checked.forEach(function(cb) {
|
||||
var saddr = cb.closest('tr').dataset.saddr;
|
||||
WsClient.send({ saddr: saddr, signal_type: type, curd: 'del', setting_zone: '0' });
|
||||
var tr = cb.closest('tr');
|
||||
var saddr = tr ? tr.getAttribute('data-saddr') : '';
|
||||
if (saddr) {
|
||||
WsClient.send({ saddr: saddr, signal_type: type, curd: 'del', setting_zone: '0' });
|
||||
SignalStore.remove(type, saddr);
|
||||
}
|
||||
});
|
||||
toast(checked.length + ' ' + I18n.t('toast.batch_del_sent'), 'info');
|
||||
ov.remove();
|
||||
|
|
@ -218,3 +228,45 @@ var SignalActions = {
|
|||
}, I18n.t('signal.set'));
|
||||
}
|
||||
};
|
||||
|
||||
/* ---- Event delegation: data-action buttons ---- */
|
||||
document.addEventListener('click', function(e) {
|
||||
var btn = e.target.closest('[data-action]');
|
||||
if (!btn) return;
|
||||
var action = btn.getAttribute('data-action');
|
||||
var saddr = btn.getAttribute('data-saddr');
|
||||
var type = btn.getAttribute('data-page-type');
|
||||
if (!type) {
|
||||
var tbody = e.target.closest('tbody[data-type]');
|
||||
if (tbody) type = tbody.getAttribute('data-type');
|
||||
}
|
||||
if (!type) return;
|
||||
if (action === 'set-go') {
|
||||
e.preventDefault();
|
||||
var row = btn.closest('tr');
|
||||
var inp = row ? row.querySelector('.sig-set-input') : null;
|
||||
var val = inp ? inp.value.trim() : '';
|
||||
if (!val) { toast(I18n.t('toast.val_required'), 'error'); return; }
|
||||
WsClient.send({ saddr: saddr, signal_type: type, curd: 'set', signal_data: val, setting_zone: '0' });
|
||||
/* 立即更新本地 SignalStore 和 DOM,推送到达前先显示 */
|
||||
var arr = SignalStore.get(type);
|
||||
for (var k = 0; k < arr.length; k++) { if (arr[k].saddr === saddr) { arr[k].val = val; break; } }
|
||||
/* 直接更新当前行的值单元格 */
|
||||
var vc = row.querySelector('.cell-val');
|
||||
if (vc) { vc.textContent = val; vc.classList.add('changed'); setTimeout(function(){vc.classList.remove('changed');},600); }
|
||||
inp.value = '';
|
||||
} else if (action === 'del') {
|
||||
e.preventDefault();
|
||||
SignalActions.del(type, saddr);
|
||||
}
|
||||
});
|
||||
/* Enter key on inline input triggers set */
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter' && e.target.classList.contains('sig-set-input')) {
|
||||
e.preventDefault();
|
||||
var inp = e.target;
|
||||
var row = inp.closest('tr');
|
||||
var btn = row ? row.querySelector('[data-action="set-go"]') : null;
|
||||
if (btn) btn.click();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ var WsClient = {
|
|||
var self = WsClient;
|
||||
self._setStatus('disconnected');
|
||||
self.ws = null;
|
||||
/* 清空纯内存信号缓存 (后端会话已销毁, 前端不应保留旧数据) */
|
||||
if (typeof SignalStore !== 'undefined' && SignalStore.clear) {
|
||||
SignalStore.clear();
|
||||
}
|
||||
self._schedule();
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue