mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-30 04:40:18 +08:00
feat: 飞书官方插件迁移 + 配对审批 + Gateway防卡死 + 微信升级修复 + 更新检测修复
- 飞书渠道从 @openclaw/feishu 迁移到 @larksuite/openclaw-lark 官方插件 - 保存飞书配置时自动禁用旧 feishu 插件,防止新旧插件冲突 - 所有主要渠道(飞书/Telegram/Discord/Slack)启用配对审批UI - gateway_command 增加20s超时,超时后force-kill+fresh start - 全平台启动前端口占用检查,防止Guardian无限拉起 - Linux gateway_command 补齐 Duration 导入和 cleanup_zombie 实现 - Guardian自动守护在Tauri桌面端也启用,轮询间隔30s→15s - 微信渠道:升级操作不再弹出扫码二维码,按钮文案区分安装/升级 - 版本更新检测:CI不再将minAppVersion写死为当前版本 - 部署脚本增强OpenClaw检测,支持已安装的官方版 - 日间/夜间模式圆形扩散切换动画(View Transitions API) - API错误信息完整展示(429限流等),URL自动转可点击链接 - 第三方API接入引导优化:移除内置密钥,引导式流程 - 修复全平台 Clippy 警告(strip_prefix/dead_code/unnecessary_unwrap等) - Rust代码格式化修复(cargo fmt) - toast组件支持HTML内容渲染 - Rust后端test_model返回详细错误信息
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import { api, invalidate } from '../lib/tauri-api.js'
|
||||
import { toast } from '../components/toast.js'
|
||||
import { showModal, showConfirm } from '../components/modal.js'
|
||||
import { CHANNEL_LABELS } from '../lib/channel-labels.js'
|
||||
|
||||
export async function render() {
|
||||
const page = document.createElement('div')
|
||||
@@ -25,7 +26,7 @@ export async function render() {
|
||||
</div>
|
||||
`
|
||||
|
||||
const state = { agents: [] }
|
||||
const state = { agents: [], bindings: [] }
|
||||
// 非阻塞:先返回 DOM,后台加载数据
|
||||
loadAgents(page, state)
|
||||
|
||||
@@ -52,7 +53,12 @@ async function loadAgents(page, state) {
|
||||
const container = page.querySelector('#agents-list')
|
||||
renderSkeleton(container)
|
||||
try {
|
||||
state.agents = await api.listAgents()
|
||||
const [agents, config] = await Promise.all([
|
||||
api.listAgents(),
|
||||
api.readOpenclawConfig().catch(() => null),
|
||||
])
|
||||
state.agents = agents
|
||||
state.bindings = Array.isArray(config?.bindings) ? config.bindings : []
|
||||
renderAgents(page, state)
|
||||
|
||||
// 只在第一次加载时绑定事件(避免重复绑定)
|
||||
@@ -61,11 +67,27 @@ async function loadAgents(page, state) {
|
||||
state.eventsAttached = true
|
||||
}
|
||||
} catch (e) {
|
||||
container.innerHTML = '<div style="color:var(--error);padding:20px">加载失败: ' + e + '</div>'
|
||||
container.innerHTML = '<div style="color:var(--error);padding:20px">加载失败: ' + String(e).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') + '</div>'
|
||||
toast('加载 Agent 列表失败: ' + e, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
/** 为指定 agent 生成绑定渠道的 badge HTML */
|
||||
function renderBindingBadges(agentId, bindings) {
|
||||
const matched = (bindings || []).filter(b => (b.agentId || 'main') === agentId)
|
||||
if (!matched.length) {
|
||||
return '<span style="color:var(--text-tertiary)">未绑定渠道</span>'
|
||||
}
|
||||
return matched.map(b => {
|
||||
const channel = b.match?.channel || ''
|
||||
const label = CHANNEL_LABELS[channel] || channel
|
||||
const accountId = b.match?.accountId
|
||||
const text = accountId ? `${label} · ${accountId}` : label
|
||||
const escaped = text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
||||
return `<span style="font-size:var(--font-size-xs);color:var(--accent);background:var(--accent-muted);padding:1px 6px;border-radius:10px;white-space:nowrap">${escaped}</span>`
|
||||
}).join(' ')
|
||||
}
|
||||
|
||||
function renderAgents(page, state) {
|
||||
const container = page.querySelector('#agents-list')
|
||||
if (!state.agents.length) {
|
||||
@@ -102,6 +124,10 @@ function renderAgents(page, state) {
|
||||
<span class="agent-info-label">工作区:</span>
|
||||
<span class="agent-info-value" style="font-family:var(--font-mono);font-size:var(--font-size-xs)">${a.workspace || '未设置'}</span>
|
||||
</div>
|
||||
<div class="agent-info-row">
|
||||
<span class="agent-info-label">绑定渠道:</span>
|
||||
<span class="agent-info-value">${renderBindingBadges(a.id, state.bindings)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user