mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-06-02 14:20:10 +08:00
feat(ux): 小白 UX 全面改造 - 错误友好度 + 致命操作强确认 + 空状态 + 新手引导 + 术语表
面向小白用户的产品定位重塑,从七大 UX 痛点逐一改造:
## U1 错误友好度(59 处改造)
- 新工具 src/lib/humanize-error.js:自动把后端原始错误(fetch failed、ENETUNREACH、ENOENT 等)
映射成「主行 + hint 行动建议 + 折叠技术详情」三段式结构化对象
- toast 组件升级支持 { message, hint, raw } 结构化入参,向后完全兼容
- 14 个 page 文件中所有 toast(t('xxx.failed') + ': ' + e, 'error') 替换为 toast(humanizeError(e, t(...)), 'error')
- common.js 加 error.* / errorHint.* 共 13 个新 i18n 键(11 语言):
网络/Gateway 未启动/命令缺失/权限/超时/限流/未找到/鉴权/服务繁忙/通用
## U2 致命操作强确认(14 处改造)
- showConfirm 升级支持结构化对象 { message, impact[], title, confirmText, cancelText, variant }
- 加 .modal-impact-list 红边样式(让小白看清楚删了会丢什么)
- 14 处致命操作改造,每处显示影响列表 + 红色「删除/移除/重置」按钮 + 灰色「保留」取消:
· agents.js 删除 Agent(动态显示 N 个绑定影响)
· channels.js 移除平台(动态算 N 个 binding)+ 移除 Agent binding
· memory.js 删除记忆文件
· services.js 卸载 Gateway(3 段影响)+ 删除备份
· models.js 批量删模型
· chat.js 删除会话 + 重置会话
· dreaming.js 重置梦境日记 + 清空 grounded 短期记忆
· agent-detail.js 解除渠道绑定
· cron.js 删除任务(OpenClaw + Hermes 两端)
- skills.js 原生 confirm() 改 showConfirm
- hermes-cron.js 原生 confirm() 改 showConfirm,顺手修末尾多余 `}` 的 syntax 残留
## U3-C 空状态 emoji+CTA(5 页面)
- 通用 .empty-state 组件(大 emoji + 标题 + 副本 + CTA 按钮 + 紧凑变体)
- agents.js: 🤖 + 「+ 新建 Agent」CTA
- memory.js: 🧠 + 「+ 新建记忆文件」CTA(紧凑版)
- cron.js: ⏰ + 「+ 新建任务」CTA
- skills.js: 🛠️ + 「技能商店」CTA(点击切 Tab)
- channels.js: 💬 + 紧凑提示
- CTA 巧妙复用页面顶部已有按钮的 click,零重复逻辑
## U3-B Dashboard 新手任务卡片
- 蓝紫渐变卡片,4 步任务自动检测:启动 Gateway / 添加模型 / 创建 Agent / 第一次聊天
- 已完成:✓ 徽章 + 删除线 + 60% 透明
- 未完成:编号徽章 + 蓝色 CTA 按钮跳对应页面
- 全部完成 → 庆祝条「🎉 全部搞定!」+ 关闭按钮
- localStorage 标记,用户主动关闭后永久隐藏
- 14 个新 i18n 键,文案小白化(Gateway 是「发动机」/ Agent 是「分身」/ 模型给 AI 装「大脑」)
## U3-A 术语表页(/glossary)
- 25 个核心术语 × 4 大分类(核心 8 / 模型 6 / 接入 5 / 进阶 6)
- 搜索框实时过滤 + Tab 切换分类 + 卡片网格布局
- 每条术语:「比喻 + 一句话」描述(避免循环引用)+ 「打开页面 →」CTA 直达配置
- 3 语言(zh-CN / en / zh-TW)完整翻译,其他 8 语言 fallback
- 双引擎(OpenClaw + Hermes)共用路由
- dashboard quick-actions 加「📖 面板术语」入口
## U3-D 术语 ⓘ tooltip
- 通用 src/lib/term-tooltip.js helper:termHelpHtml(id) + attachTermTooltips(root)
- 8 个高频术语精简表(OAuth / Webhook / Bot Token / API Key / Token / Context Window / Binding / Scope)
- channels.js 字段 label 智能匹配关键词自动追加 ⓘ(覆盖 8 个渠道全部敏感字段)
- models.js 添加/编辑 provider 的 API Key label 也加 ⓘ
- 点 ⓘ → 弹小型 modal 含解释 + 「打开术语表 →」CTA
- attachTermTooltips 内部去重,可安全多次调用
## 累计交付
- 4 个新文件(humanize-error.js / term-tooltip.js / glossary.js page / glossary.js i18n)
- 6 个升级文件(toast / modal / components.css / dashboard / channels / models)
- 14 个 page 错误 toast 友好化(59 处)
- 14 处致命操作强确认
- 5 处空状态升级 + Dashboard 新手卡片 + 术语表 + ⓘ tooltip
- 109 个新 i18n 键(11 语言)
- Build 全程通过
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
import { api, invalidate } from '../lib/tauri-api.js'
|
||||
import { toast } from '../components/toast.js'
|
||||
import { humanizeError } from '../lib/humanize-error.js'
|
||||
import { showModal, showConfirm } from '../components/modal.js'
|
||||
import { CHANNEL_LABELS } from '../lib/channel-labels.js'
|
||||
import { t } from '../lib/i18n.js'
|
||||
@@ -72,7 +73,7 @@ async function loadAgents(page, state) {
|
||||
}
|
||||
} catch (e) {
|
||||
container.innerHTML = '<div style="color:var(--error);padding:20px">' + t('agents.loadFailed') + ': ' + String(e).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') + '</div>'
|
||||
toast(t('agents.loadListFailed') + ': ' + e, 'error')
|
||||
toast(humanizeError(e, t('agents.loadListFailed')), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +116,17 @@ function _renderRuntimeBadge(runtime) {
|
||||
function renderAgents(page, state) {
|
||||
const container = page.querySelector('#agents-list')
|
||||
if (!state.agents.length) {
|
||||
container.innerHTML = `<div style="color:var(--text-tertiary);padding:20px;text-align:center">${t('agents.noAgents')}</div>`
|
||||
container.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">🤖</div>
|
||||
<div class="empty-title">${t('agents.noAgents')}</div>
|
||||
<div class="empty-desc">${t('common.emptyGetStartedHint')}</div>
|
||||
<div class="empty-cta"><button class="btn btn-primary" data-empty-cta="add-agent">${t('agents.addAgent')}</button></div>
|
||||
</div>
|
||||
`
|
||||
container.querySelector('[data-empty-cta="add-agent"]')?.addEventListener('click', () => {
|
||||
page.querySelector('#btn-add-agent')?.click()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -241,7 +252,7 @@ async function showAddAgentDialog(page, state) {
|
||||
invalidate('list_agents')
|
||||
await loadAgents(page, state)
|
||||
} catch (e) {
|
||||
toast(t('agents.createFailed') + ': ' + e, 'error')
|
||||
toast(humanizeError(e, t('agents.createFailed')), 'error')
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -322,14 +333,26 @@ async function showEditAgentDialog(page, state, id) {
|
||||
toast(t('agents.updated'), 'success')
|
||||
} catch (e) {
|
||||
console.error('[Agent编辑] 保存失败:', e)
|
||||
toast(t('agents.updateFailed') + ': ' + e, 'error')
|
||||
toast(humanizeError(e, t('agents.updateFailed')), 'error')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function deleteAgent(page, state, id) {
|
||||
const yes = await showConfirm(t('agents.confirmDelete', { id }))
|
||||
// 计算关联渠道绑定数(小白看清楚删了会丢什么)
|
||||
const linkedBindings = (state.bindings || []).filter(b => (b.agentId || 'main') === id).length
|
||||
const impact = [t('agents.deleteImpactConfig')]
|
||||
if (linkedBindings > 0) {
|
||||
impact.unshift(t('agents.deleteImpactBindings', { n: linkedBindings }))
|
||||
}
|
||||
const yes = await showConfirm({
|
||||
title: t('agents.deleteConfirmTitle'),
|
||||
message: t('agents.confirmDelete', { id }),
|
||||
impact,
|
||||
confirmText: t('agents.deleteConfirmBtn'),
|
||||
cancelText: t('agents.deleteCancelBtn'),
|
||||
})
|
||||
if (!yes) return
|
||||
|
||||
try {
|
||||
@@ -337,7 +360,7 @@ async function deleteAgent(page, state, id) {
|
||||
toast(t('agents.deleted'), 'success')
|
||||
await loadAgents(page, state)
|
||||
} catch (e) {
|
||||
toast(t('agents.deleteFailed') + ': ' + e, 'error')
|
||||
toast(humanizeError(e, t('agents.deleteFailed')), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,6 +375,6 @@ async function backupAgent(id) {
|
||||
} catch { /* fallback */ }
|
||||
toast(t('agents.backupDone', { file: zipPath.split('/').pop() }), 'success')
|
||||
} catch (e) {
|
||||
toast(t('agents.backupFailed') + ': ' + e, 'error')
|
||||
toast(humanizeError(e, t('agents.backupFailed')), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user