fix(robot): scope devices to selected agent

This commit is contained in:
2026-08-16 15:42:05 +08:00
parent 3b4669722e
commit 87a95b4cec
3 changed files with 132 additions and 23 deletions

View File

@@ -326,7 +326,10 @@ export function AiHardware() {
}, [configReloadKey, selectedAgentId]);
const selectedAgent = overview?.agents.find((item) => item.id === selectedAgentId) ?? null;
const devices = useMemo(() => overview?.devices ?? [], [overview]);
const devices = useMemo(
() => overview?.devices.filter((device) => device.agent_id === selectedAgentId) ?? [],
[overview, selectedAgentId],
);
const resetDialog = () => {
setDialogError(null); setBusy(false);
clearCreateOperation(); clearBindOperation(); clearConfigOperation(); clearAssignmentOperation();
@@ -625,7 +628,10 @@ export function AiHardware() {
<p className="text-sm text-muted-foreground"> 6 </p>
) : null}
<div><Label htmlFor="hardware-activation-code">6 </Label><Input ref={activationCodeInputRef} id="hardware-activation-code" autoFocus type="text" inputMode="numeric" autoComplete="off" maxLength={6} onInput={(e) => { e.currentTarget.value = e.currentTarget.value.replace(/\D/g, '').slice(0, 6); }} /></div>
<AgentSelect agents={overview.agents} value={dialogAgentId} onChange={(value) => { setDialogAgentId(value); clearBindOperation(); }} />
<div className="rounded-xl bg-surface-subtle px-3 py-2">
<p className="text-xs text-muted-foreground"></p>
<p className="mt-1 text-sm font-semibold">{overview.agents.find((agent) => agent.id === dialogAgentId)?.name ?? '未选择'}</p>
</div>
<p className="text-xs text-muted-foreground"></p>
</div>
);
@@ -665,10 +671,13 @@ export function AiHardware() {
<Card className="mx-auto max-w-md text-center"><CardHeader><CardTitle></CardTitle><CardDescription>{overview.status === 'unprovisioned' ? '创建智能体将同时开通你的机器人工作台。' : '智能体创建后,才能把机器人设备绑定给它。'}</CardDescription></CardHeader><CardContent><Button onClick={() => { resetDialog(); setCreateOpen(true); }}><Plus className="mr-2 h-4 w-4" /></Button></CardContent></Card>
) : (
<div className="grid gap-5 lg:grid-cols-[minmax(240px,0.7fr)_minmax(0,1.3fr)]">
<Card><CardHeader className="flex-row items-center justify-between"><div><CardTitle></CardTitle><CardDescription>{overview.agents.length} </CardDescription></div><Button size="icon" aria-label="创建智能体" onClick={() => { resetDialog(); setCreateOpen(true); }}><Plus className="h-4 w-4" /></Button></CardHeader><CardContent className="space-y-2">{overview.agents.map((agent) => <button key={agent.id} type="button" aria-pressed={agent.id === selectedAgentId} onClick={() => setSelectedAgentId(agent.id)} className={`motion-press flex min-h-12 w-full items-center gap-3 rounded-xl px-3 py-2 text-left ${agent.id === selectedAgentId ? 'bg-brand-soft' : 'bg-surface-subtle hover:bg-surface-tertiary'}`}><Bot className="h-4 w-4 shrink-0" /><span className="min-w-0 flex-1"><span className="block truncate text-sm font-semibold">{agent.name}</span><span title={agent.id} className="block text-xs tabular-nums text-muted-foreground">{shortId(agent.id)} · r{agent.config_revision}</span></span></button>)}</CardContent></Card>
<Card><CardHeader className="flex-row items-center justify-between"><div><CardTitle></CardTitle><CardDescription>{overview.agents.length} </CardDescription></div><Button size="icon" aria-label="创建智能体" onClick={() => { resetDialog(); setCreateOpen(true); }}><Plus className="h-4 w-4" /></Button></CardHeader><CardContent className="space-y-2">{overview.agents.map((agent) => {
const deviceCount = overview.devices.filter((device) => device.agent_id === agent.id).length;
return <button key={agent.id} type="button" aria-pressed={agent.id === selectedAgentId} onClick={() => setSelectedAgentId(agent.id)} className={`motion-press flex min-h-12 w-full items-center gap-3 rounded-xl px-3 py-2 text-left ${agent.id === selectedAgentId ? 'bg-brand-soft' : 'bg-surface-subtle hover:bg-surface-tertiary'}`}><Bot className="h-4 w-4 shrink-0" /><span className="min-w-0 flex-1"><span className="block truncate text-sm font-semibold">{agent.name}</span><span title={agent.id} className="block text-xs tabular-nums text-muted-foreground">{shortId(agent.id)} · r{agent.config_revision} · {deviceCount} </span></span></button>;
})}</CardContent></Card>
<div className="space-y-5">
<Card><CardHeader className="flex-row items-center justify-between"><div><CardTitle>{selectedAgent?.name ?? '智能体配置'}</CardTitle><CardDescription></CardDescription></div><Button variant="outline" disabled={configLoading || !config} onClick={openConfigurationEditor}>{configLoading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Settings2 className="mr-2 h-4 w-4" />}</Button></CardHeader><CardContent>{config ? <dl className="grid gap-3 text-sm sm:grid-cols-2"><div><dt className="text-muted-foreground"></dt><dd>{config.language || config.lang_code || '未设置'}</dd></div><div><dt className="text-muted-foreground"></dt><dd>{config.tts_voice_id || '未设置'}</dd></div><div className="sm:col-span-2"><dt className="text-muted-foreground"></dt><dd className="mt-1 whitespace-pre-wrap">{config.system_prompt || '未设置'}</dd></div></dl> : configLoading ? <FeedbackState state="loading" title="正在读取配置" /> : configLoadFailed ? <FeedbackState state="error" title="无法读取智能体配置" description="请检查服务连接后重试。" action={<Button variant="outline" onClick={() => setConfigReloadKey((value) => value + 1)}></Button>} /> : null}</CardContent></Card>
<Card><CardHeader className="flex-row items-center justify-between"><div><CardTitle></CardTitle><CardDescription>{devices.length} </CardDescription></div><Button onClick={openBindingDialog}><Link2 className="mr-2 h-4 w-4" /></Button></CardHeader><CardContent>{devices.length ? <div className="space-y-2">{devices.map((device) => <div key={device.id} className="flex min-h-12 items-center justify-between gap-3 rounded-xl bg-surface-subtle px-3 py-2"><span className="min-w-0"><span className="block text-sm font-semibold"> {shortId(device.id)}</span><span title={device.id} className="text-xs tabular-nums text-muted-foreground"> r{device.assignment_revision}</span></span><Button variant="outline" size="sm" onClick={() => void openAssignment(device)}></Button></div>)}</div> : <FeedbackState state="empty" title="还没有绑定设备" description="使用设备上的 6 位激活码完成绑定。" />}</CardContent></Card>
<Card><CardHeader className="flex-row items-center justify-between"><div><CardTitle></CardTitle><CardDescription>{devices.length} </CardDescription></div><Button onClick={openBindingDialog}><Link2 className="mr-2 h-4 w-4" /></Button></CardHeader><CardContent>{devices.length ? <div className="space-y-2">{devices.map((device) => <div key={device.id} className="flex min-h-12 items-center justify-between gap-3 rounded-xl bg-surface-subtle px-3 py-2"><span className="min-w-0"><span className="block text-sm font-semibold"> {shortId(device.id)}</span><span title={device.id} className="text-xs tabular-nums text-muted-foreground"> r{device.assignment_revision}</span></span><Button variant="outline" size="sm" onClick={() => void openAssignment(device)}></Button></div>)}</div> : <FeedbackState state="empty" title="当前智能体还没有绑定设备" description="使用设备上的 6 位激活码绑定到当前智能体。" />}</CardContent></Card>
</div>
</div>
)}