156 lines
4.2 KiB
JavaScript
156 lines
4.2 KiB
JavaScript
/**
|
|
* @file terminal.js
|
|
* @brief 命令终端页面 — 按钮面板 + 输入框
|
|
* @details 连接后自动获取命令列表,按模块分组生成按钮。
|
|
* 点击按钮将命令名填充到输入框,用户可继续输入子命令参数。
|
|
* 回车执行,输出显示在下方窗口。
|
|
*/
|
|
|
|
var TermCommands = [];
|
|
|
|
Router.register('#terminal', function(c) {
|
|
c.innerHTML =
|
|
'<div class="card">' +
|
|
' <div class="card-header"><span>' + I18n.t('term.title') + '</span></div>' +
|
|
' <div class="terminal-wrap">' +
|
|
' <div class="cmd-panels" id="cmd-panels">' +
|
|
' <div style="padding:16px;color:#888;">' + (I18n.t('term.loading') || '正在加载命令列表...') + '</div>' +
|
|
' </div>' +
|
|
' <div class="terminal-output" id="term-out">' + I18n.t('term.welcome') + '</div>' +
|
|
' <div class="terminal-input-row">' +
|
|
' <span class="terminal-prompt">RTU></span>' +
|
|
' <input class="terminal-input" id="term-input" placeholder="点击上方按钮填充命令,或直接输入..." ' +
|
|
' autocomplete="off" onkeydown="handleTermKey(event)">' +
|
|
' </div>' +
|
|
' </div>' +
|
|
'</div>';
|
|
|
|
if (TermCommands.length > 0)
|
|
{
|
|
renderCmdPanels(TermCommands);
|
|
}
|
|
else
|
|
{
|
|
WsClient.send({ type: 'get_cmds' });
|
|
}
|
|
});
|
|
|
|
/* ---- 命令分组 ---- */
|
|
function groupCommands(cmds)
|
|
{
|
|
var groups = {};
|
|
|
|
for (var i = 0; i < cmds.length; i++)
|
|
{
|
|
var mod = 'other';
|
|
var name = cmds[i].name;
|
|
|
|
if (name.indexOf('datacenter') === 0) mod = 'datacenter';
|
|
else if (name.indexOf('iec') === 0) mod = 'iec';
|
|
else if (name.indexOf('plc') === 0) mod = 'plc';
|
|
else if (name.indexOf('self_ptl') === 0) mod = 'self_ptl';
|
|
else if (name.indexOf('modbus_m') === 0) mod = 'modbus_m';
|
|
else if (name === 'help' || name === 'version') mod = 'system';
|
|
|
|
if (!groups[mod]) groups[mod] = [];
|
|
groups[mod].push(cmds[i]);
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
var groupMeta = {
|
|
'datacenter': { icon: '📊', label: '数据中心' },
|
|
'iec': { icon: '⚡', label: 'IEC-104' },
|
|
'plc': { icon: '🎛', label: 'PLC 逻辑' },
|
|
'self_ptl': { icon: '🔗', label: '自协议' },
|
|
'modbus_m': { icon: '📡', label: 'Modbus 主站' },
|
|
'system': { icon: '⚙', label: '系统' },
|
|
'other': { icon: '📦', label: '其他' },
|
|
};
|
|
|
|
/* ---- 渲染命令面板 ---- */
|
|
function renderCmdPanels(cmds)
|
|
{
|
|
TermCommands = cmds;
|
|
|
|
var panels = document.getElementById('cmd-panels');
|
|
if (!panels) return;
|
|
|
|
var groups = groupCommands(cmds);
|
|
var html = '';
|
|
|
|
for (var mod in groups)
|
|
{
|
|
var meta = groupMeta[mod] || { icon: '', label: mod };
|
|
|
|
html += '<div class="cmd-group">';
|
|
html += '<div class="cmd-group-title">' + meta.icon + ' ' + meta.label + '</div>';
|
|
html += '<div class="cmd-group-btns">';
|
|
|
|
for (var j = 0; j < groups[mod].length; j++)
|
|
{
|
|
var cmd = groups[mod][j];
|
|
html += '<button class="cmd-btn" title="' + esc(cmd.desc || '') + '" ' +
|
|
'onclick="fillTermCmd(\'' + esc(cmd.name) + '\')">' +
|
|
'<span class="cmd-btn-name">' + esc(cmd.name) + '</span>' +
|
|
'<span class="cmd-btn-desc">' + esc(cmd.desc || '') + '</span>' +
|
|
'</button>';
|
|
}
|
|
|
|
html += '</div></div>';
|
|
}
|
|
|
|
panels.innerHTML = html;
|
|
}
|
|
|
|
/* ---- 填充命令到输入框 ---- */
|
|
function fillTermCmd(cmd)
|
|
{
|
|
var input = document.getElementById('term-input');
|
|
if (!input) return;
|
|
|
|
input.value = cmd + ' ';
|
|
input.focus();
|
|
}
|
|
|
|
/* ---- 回车执行 ---- */
|
|
function handleTermKey(e)
|
|
{
|
|
if (e.key !== 'Enter') return;
|
|
|
|
var input = document.getElementById('term-input');
|
|
var out = document.getElementById('term-out');
|
|
if (!input || !out) return;
|
|
|
|
var cmd = input.value.trim();
|
|
if (!cmd) return;
|
|
|
|
/* 本地清屏 */
|
|
if (cmd === 'clear')
|
|
{
|
|
out.textContent = '';
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
/* 显示已执行命令 */
|
|
out.textContent += '\n> ' + cmd + '\n';
|
|
out.scrollTop = out.scrollHeight;
|
|
|
|
/* 发送到后端 */
|
|
WsClient.send({ type: 'cmd', data: cmd });
|
|
|
|
input.value = '';
|
|
}
|
|
|
|
/* ---- WebSocket 二进制消息追加到输出窗口 ---- */
|
|
function termOnBinary(text)
|
|
{
|
|
var out = document.getElementById('term-out');
|
|
if (!out) return;
|
|
|
|
out.textContent += text;
|
|
out.scrollTop = out.scrollHeight;
|
|
}
|