feat: 自动认领开关 + 全站按设备名称显示
一、指纹匹配自动认领(可选,默认关)
- 发现设置新增「指纹匹配自动认领」勾选(app_meta: discovery_auto_claim,默认 0)
- 打开后:扫描发现某设备指纹与池中已有记录一致(同一台换了 IP)→ 自动迁移记录到新地址
并同步分组/任务引用,零点击;关闭时维持"识别自动 + 人工点一次确认"
- 默认关的原因:认领会改写分组/任务引用(数据结构变动),交人工确认更稳妥
- 扫描结果与状态行会显示本轮自动认领了几台
二、设备名称在界面上呈现(凡选择/展示设备处都显示名称)
- 新增前端 helper `devText(name, serial)`(base.js):有名称→「名称 · serial」
- 监控页设备表:名称加粗为主、地址作副行(未命名显示橙色提醒);任务概况的覆盖设备
chip 也优先显示名称(tooltip 保留完整地址)
- AI 控制台:目标设备下拉、实时画面设备下拉、目标/运行中提示都带名称(serial→name 映射)
- 任务编辑器「指定设备」下拉、分组编辑的设备勾选列表:带名称
- 后端 `/api/devices` 新增 `items`([{serial,name,model}],`devices` 保持兼容);
`/api/agent/devices` 增加 `name` 字段
- MCP `de_list_devices` 返回 `name`,并在工具说明与 Agent 系统提示里要求"汇报用名称、
调工具用 serial"
文档:API.md(items/name/auto_claim + §6.2.1 名称呈现表)、MCP.md(工具返回)
自测(全通过):自动认领端到端(开开关→扫描→自动迁址 + 名称保留 + 分组/任务引用同步 +
待连接池清理 + 开关默认关且可持久化);名称显示浏览器验证(监控页/覆盖设备 chip/AI 目标与
观看下拉/任务编辑器/分组弹窗/发现设置开关);设备指纹与人工认领回归
This commit is contained in:
+19
-6
@@ -16,6 +16,12 @@ function _friendlyAgentError(msg){
|
||||
|
||||
// ================== 初始化 ==================
|
||||
let _agentDefaultSerial = '';
|
||||
// 目标设备显示文案:名称优先(设备身份),没有名称退地址
|
||||
let _agentTargets={}; // serial -> name
|
||||
function _agentTargetText(serial){
|
||||
const nm=_agentTargets[serial];
|
||||
return nm ? (nm + ' · ' + serial) : String(serial||'');
|
||||
}
|
||||
function initAgentChat(){
|
||||
if(_agentCfgLoaded)return;
|
||||
_agentCfgLoaded = true;
|
||||
@@ -81,9 +87,11 @@ function loadAgentTargetDevices(){
|
||||
if(!sel)return;
|
||||
const cur = sel.value;
|
||||
const devs = (r.devices||[]).filter(x=>x.online);
|
||||
_agentTargets = {};
|
||||
devs.forEach(d=>{ if(d.name) _agentTargets[d.serial]=d.name; });
|
||||
sel.innerHTML = '<option value="">请选择设备(AI 只操作选定设备)…</option>'
|
||||
+ devs.map(d=>{
|
||||
const label = d.serial + (d.model ? ' · ' + d.model : '')
|
||||
const label = devText(d.name, d.serial) + (d.model ? ' · ' + d.model : '')
|
||||
+ (d.busy ? ' ⛔ 任务中:' + d.task_job : '');
|
||||
return '<option value="'+esc(d.serial)+'"' + (d.busy ? ' disabled' : '')
|
||||
+ '>' + esc(label) + '</option>';
|
||||
@@ -93,7 +101,7 @@ function loadAgentTargetDevices(){
|
||||
else if(_agentDefaultSerial && [...sel.options].some(o=>o.value===_agentDefaultSerial))
|
||||
sel.value = _agentDefaultSerial;
|
||||
const hint = document.getElementById('agent-target-hint');
|
||||
if(hint && sel.value) hint.textContent = '将操作:' + sel.value;
|
||||
if(hint && sel.value) hint.textContent = '将操作:' + _agentTargetText(sel.value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,10 +122,11 @@ function pollRunState(){
|
||||
if(hint){
|
||||
hint.textContent = running
|
||||
? ('⏳ 运行中' + (r.started ? ' ' + r.started + ' 起' : '')
|
||||
+ (r.serial ? ' · ' + r.serial : '')
|
||||
+ (r.serial ? ' · ' + _agentTargetText(r.serial) : '')
|
||||
+ ':' + (r.prompt||'').slice(0,70))
|
||||
: (document.getElementById('agent-target-select').value
|
||||
? '将操作:' + document.getElementById('agent-target-select').value : '');
|
||||
? '将操作:' + _agentTargetText(document.getElementById('agent-target-select').value)
|
||||
: '');
|
||||
}
|
||||
document.getElementById('btn-agent-send').disabled = running;
|
||||
// 运行中同步 token(本窗口没订阅事件流时也能看到进度)
|
||||
@@ -608,9 +617,13 @@ function loadLiveDevices(){
|
||||
const sel = document.getElementById('agent-live-select');
|
||||
if(!sel)return;
|
||||
const cur = sel.value;
|
||||
const devs = (r.devices||[]).filter(x=>x.indexOf(':')>=0);
|
||||
// items 带设备名称(设备身份);老接口只有 serial 列表时兜底
|
||||
const items = (r.items && r.items.length ? r.items
|
||||
: (r.devices||[]).map(x=>({serial:x, name:''})))
|
||||
.filter(x=>String(x.serial||'').indexOf(':')>=0);
|
||||
sel.innerHTML = '<option value="">选择设备观看…</option>'
|
||||
+ devs.map(s=>'<option value="'+esc(s)+'"'+(s===cur?' selected':'')+'>'+esc(s)+'</option>').join('');
|
||||
+ items.map(x=>'<option value="'+esc(x.serial)+'"'+(x.serial===cur?' selected':'')+'>'
|
||||
+esc(devText(x.name, x.serial))+'</option>').join('');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user