Files
one_divine_lot/docs/04-迭代记录/09-Tab设置统一管理/技术实现方案.md
T
kyugao f1e7e785a1 docs(迭代09/10): Tab 设置统一管理 + UI 主题适配 全量文档
迭代09 (R-011 Tab 设置统一管理):
- 需求 R-011 (定稿 Q1-Q5) + 需求池索引
- 计划 PLAN-010 + 迭代09 四件套 (目标/技术方案/验收/复盘)
- 约束: 产品约束-009 / UI约束-003 / 技术约束-014
- UI约束-002 修订: 通用设置→Tab 设置

迭代10 (R-012 UI 适配 DSH 主题):
- 需求 R-012 (定稿: 暂定跟随系统) + 需求池索引
- 计划 PLAN-011 + 迭代10 四件套
- 约束: UI约束-004 (主题适配约定)
2026-09-02 14:01:08 +08:00

4.1 KiB
Raw Blame History

技术实现方案:09-Tab 设置统一管理

迭代编号:09 依据:PLAN-010、R-011、产品约束-009、UI约束-003、技术约束-014

1. 数据模型(src/settings.js

1.1 tabs 有序数组(新 schema

tabs: z.array(z.object({
  id: z.string().required(),        // 唯一 id'tab-all-positions' / 'tab-strategy-<id>'
  kind: z.enum(['builtin', 'strategy']).required(),
  refKey: z.string().optional(),    // builtin 专用:'allPositions' | 'tradeRecords' | 'watchlist'
  refId: z.string().optional(),     // strategy 专用:策略 id
  name: z.string().required(),      // 展示名(策略行 join 时刷新)
  visible: z.boolean().default(true),
  order: z.number().default(0),
})).default(DEFAULT_TABS)

1.2 归一化迁移(读取时)

getTabs(scope) 逻辑:

  • 读取 settings.tabs:若为数组 → 直接返回(按 order 排序);
  • 若为旧布尔对象(或缺失)→ 生成默认内置三条(全部持仓/交易记录/关注列表,保留原显隐值),策略按 strategies 旧 order 接续追加(旧 hidden → visible=true);
  • 首次写入时落库归一化后的数组(幂等,不重复迁移)。

1.3 strategies 收窄

strategies schema 去掉 visible/order(仅 {id, name});getStrategies 不再排序/过滤 visible;策略展示名统一由 getTabs join strategies 得出(Q4 自动跟随改名)。

2. API 层(src/api/strategies.js

端点 变更
tabs 返回合并后的完整 tabs 数组(含策略行 name join
tabs/update 整表更新 { tabs }(顺序 + 显隐);策略行 name 由服务端 join 刷新,客户端可只传 id 顺序
strategies/add 联动 append tab 条目(末尾,order = max+1
strategies/remove 联动删除对应 tab 条目(refId === 策略 id
strategies/move 废弃(从 METHODS 移除)
strategies/update 仅剩重命名(联动刷新 tabs 中策略行 name)

3. 客户端注册(src/client/index.js

合并 registerGeneralTabs + registerStrategyTabs → registerAllFromTabs(tabs)

const sorted = tabs.slice().sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
for (const t of sorted) {
  if (t.visible === false) continue;
  const render = t.kind === 'builtin' ? GENERAL_RENDER[t.refKey] : (props) => createElement(StrategyTab, { ...props, strategyId: t.refId, strategyName: t.name });
  // slots.register conversation.view, order: t.order
}
  • GENERAL_RENDER = { allPositions, tradeRecords, watchlist } 映射常量;
  • 移除硬编码 order 10/11/12 与 13+ 间隔(直接用 t.order);
  • fetchTabConfig → fetchTabs 返回数组。

4. 设置页 UIsrc/client/views/SettingsSection.jsx

4.1 「Tab 设置」子 tab

  • 子 tab 导航:general → tabs(更名),标签「Tab 设置」;
  • 表格列:拖动手柄(≡)| 名称(内置行带「内置」徽标、策略行带「策略」徽标)| 显示(Switch);
  • 无重命名/删除按钮(任何行)
  • 拖动(原生 DnD):
    • tr draggableonDragStart 记 idonDragOver 阻止默认 + 计算插入位;onDrop 重排数组;
    • 落点 → 立即调 tabs/update(整表提交)→ 提示「已保存,刷新页面后生效」;
  • 显隐开关:切换 → 立即调 tabs/update → 提示。

4.2 策略分组瘦身

  • 移除排序箭头列(↑↓)与显示列(Switch);
  • 保留:新增输入框 + 新增按钮 / 重命名 / 删除(确认弹窗 + 份额回未分配沿用);
  • 顶部加提示:顺序与显示请在「Tab 设置」中调整。

5. 兼容与风险

  • 迁移:读取归一化幂等;旧策略 visible=false 迁移后 visible=trueQ3,产品约束-009 一致);
  • 名称 jointabs 存 refId,展示时 join strategies;策略改名后 tabs 行 name 自动跟随(Q4);
  • 注册:refresh = 刷新页面重载插件(无事件机制,沿用);
  • RPCstrategies/move 废弃需同步移除客户端调用(settings UI 不再有箭头);
  • 回归:技术约束-011 —— 写操作测试用独立数据目录(ODL_TEST_DATA_DIR / dataDir 参数)。