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

83 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 技术实现方案:09-Tab 设置统一管理
> 迭代编号:09 依据:PLAN-010、R-011、产品约束-009、UI约束-003、技术约束-014
## 1. 数据模型(src/settings.js
### 1.1 tabs 有序数组(新 schema
```js
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)
```js
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 一致);
- **名称 join**tabs 存 refId,展示时 join strategies;策略改名后 tabs 行 name 自动跟随(Q4);
- **注册**:refresh = 刷新页面重载插件(无事件机制,沿用);
- **RPC**strategies/move 废弃需同步移除客户端调用(settings UI 不再有箭头);
- **回归**:技术约束-011 —— 写操作测试用独立数据目录(ODL_TEST_DATA_DIR / dataDir 参数)。