Major changes: - Shift from "OpenFang desktop client" to "independent AI Agent desktop app" - Add decision principle: "Is this useful for ZCLAW? Does it affect ZCLAW?" - Simplify project structure and tech stack sections - Replace OpenClaw vs OpenFang comparison with unified backend approach - Consolidate troubleshooting from scattered sections into organized FAQ - Update Hands system documentation with 8 capabilities and status - Stream
3.9 KiB
3.9 KiB
团队功能开发笔记
完成日期: 2026-03-19 任务: 修复团队功能页面空白问题
一、问题描述
点击"团队"导航后,页面显示空白,控制台报错 teams.map is not a function。
二、根因分析
2.1 数据格式冲突
Zustand 的 persist 中间件存储格式为:
{
"state": { "teams": [...], "activeTeam": ... },
"version": 0
}
但 loadTeams 函数期望的是直接的数组格式 Team[]。
2.2 类型安全问题
TeamList 组件中的 availableAgents 变量使用了条件表达式,返回类型不一致:
clones是Clone[]类型agents.map(...)返回的是{ id, name, role }[]类型
TypeScript 无法推断统一类型,运行时可能导致错误。
三、解决方案
3.1 修复 loadTeams 函数
loadTeams: async () => {
set({ isLoading: true, error: null });
try {
const stored = localStorage.getItem('zclaw-teams');
let teams: Team[] = [];
if (stored) {
const parsed = JSON.parse(stored);
// 处理 persist 中间件格式
if (parsed?.state?.teams && Array.isArray(parsed.state.teams)) {
teams = parsed.state.teams;
} else if (Array.isArray(parsed)) {
teams = parsed;
}
}
set({ teams, isLoading: false });
} catch (error) {
set({ teams: [], isLoading: false });
}
},
3.2 修复 availableAgents 类型
const availableAgents: Array<{ id: string; name: string; role?: string }> =
(clones && clones.length > 0)
? clones.map(c => ({ id: c.id, name: c.name, role: c.role }))
: (agents && agents.length > 0)
? agents.map(a => ({ id: a.id, name: a.name, role: '默认助手' }))
: [];
3.3 添加防御性检查
// TeamList.tsx
{!Array.isArray(teams) || teams.length === 0 ? (
<EmptyState ... />
) : (
teams.map(...)
)}
四、相关文件
| 文件 | 修改内容 |
|---|---|
store/teamStore.ts |
loadTeams 函数处理 persist 格式 |
components/TeamList.tsx |
类型修复、防御性检查、中文化 |
components/ui/EmptyState.tsx |
CSS 修复 (flex-1 → h-full) |
App.tsx |
motion.main 添加 flex flex-col |
五、经验教训
- persist 中间件存储格式: Zustand persist 存储的是
{ state, version }结构,不是直接的状态值 - 条件表达式类型一致性: 三元表达式的两个分支必须返回相同类型
- 防御性编程: 对从 store 获取的数据进行 Array.isArray 检查
文档创建: 2026-03-19
六、协作功能修复 (2026-03-19)
6.1 问题描述
- UI 颜色不一致: SwarmDashboard 使用蓝色(blue-500)作为主色调,与系统的橙色/灰色风格不匹配
- 内容重复渲染: 左侧边栏和主内容区同时渲染 SwarmDashboard,导致内容重复
6.2 解决方案
问题 1: 内容重复
- 从
Sidebar.tsx移除{activeTab === 'swarm' && <SwarmDashboard />}渲染 - 只保留
App.tsx中的主内容区渲染 - 移除未使用的
import { SwarmDashboard }语句
问题 2: 颜色一致性
修改 SwarmDashboard.tsx 中的配色:
- 主色调:
blue-500→orange-500 - 按钮背景:
bg-blue-500→bg-orange-500 - Filter tabs:
bg-blue-100→bg-orange-100 - 选中边框:
border-blue-500→border-orange-500 - Focus ring:
ring-blue-500→ring-orange-500 - 保留执行状态(
executing/running)的蓝色作为状态指示色
6.3 相关文件
| 文件 | 修改内容 |
|---|---|
components/Sidebar.tsx |
移除 SwarmDashboard 渲染和 import |
components/SwarmDashboard.tsx |
配色从蓝色改为橙色 |
6.4 设计原则
- 单一渲染原则: 每个视图组件只在唯一位置渲染,避免多处同时显示
- 颜色一致性: 交互元素使用系统主色调(橙色),状态指示可保留语义色(蓝色=执行中,绿色=完成,红色=失败)