迭代22+23: 策略tab静态化 + 计算字段(数字/判定型)+ 网格超市信号字段

- R-027 静态化:策略恒为内置两项(不可增删改名,保留显隐/排序);设置页移除「策略分组」;
  字段配置入口迁到策略 tab 内「列设置」旁(FieldConfigDialog + schema-update 单策略写入)
- R-026 计算字段:自写公式引擎(中文变量、四则/括号/round·abs·min·max、缺值短路→—)+
  变量目录(行情/合约/持仓/自定义字段)+ strategy-positions 逐行现算(只读内存缓存,不落库、列只读)
- R-028 判定型:比较运算 + and/or + inferResultKind + 结果类型一致性校验 + 判定列 ✓/— 渲染;
  网格超市落地 可下空单=涨停价>基准值+网格大小、可下多单=跌停价<基准值-网格大小
- 变量选择改标签平铺(老师反馈);公式手册 docs/99-其他材料/计算字段公式说明.md
- 约束同步:产品约束-002/003/004/009/013/014、技术约束-014/015/022/023/024、UI约束-002/003/005/008
- 回归:新增 test-r026/test-r027,更新 r011/r013/r017,17 个脚本全绿;typecheck/build 通过
This commit is contained in:
2026-09-10 14:05:06 +08:00
parent 3dfa39b8bf
commit ec02a791ee
40 changed files with 3369 additions and 659 deletions
@@ -0,0 +1,164 @@
# 迭代 22 UI 交互设计:字段配置入口迁移 + 策略计算字段
> 依据:**R-027(重构·第一阶段,已定稿)** + **R-026(计算字段·第二阶段,已定稿)** + PLAN-019 日期:2026-09-10
> 状态:**已定稿**(D-1D-7 老师逐项拍板;2026-09-10 因入口迁移(R-027)同步修订;D-8~D-9 为 AI 默认项,老师可否决)
> 视觉基线:UI约束-004--dsw-* token)、UI约束-006ExpandChevron)、UI约束-007(间隙高亮线);UI约束-005 待随本次修订
> 复用:ColumnSettingsPopover(列设置)、StrategyFieldsEditor(改造为弹层内容)、ToastFieldCellEditor 不参与(formula 不可编辑)
## 1. 交互总览(一句话)
策略 tab 顶部**「字段配置」+「列设置」**两个按钮:字段配置管**字段定义**(含计算字段的公式),列设置管**列的显隐与顺序**;
字段定义保存后表格列自动跟随;计算字段列**只读**展示服务端实时算出的值(缺数据 `—`)。
> 本次修订要点(R-027):字段配置入口从「设置页 → 策略分组 → 策略行展开」**迁到策略 tab 内**,与列设置并列;设置页不再有「策略分组」子 tab。
## 2. 控件布局(示意)
### 2.1 策略 tab 顶栏
```
┌─ 策略 tab:网格超市 ─────────────────────────────────────────────┐
│ 持仓 13 只 · 今日盈亏 … [历史持仓 ▾] [字段配置] [列设置] │
│ ┌ 持仓表 ─────────────────────────────────────────────────────┐ │
│ │ | | 代码 | 名称 | 现价 | ƒ 盈亏比例 | … | 操作 | │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
- 两按钮同层级、同视觉族(现有「列设置」按钮样式),**字段配置**在左、**列设置**在右(先定义字段、再排列,顺序符合操作动线)。
### 2.2 字段配置弹层(列表 + 表单)
```
┌─ 字段配置:网格超市 ─────────────────────────────────────────┐
│ 自定义字段(5) [+ 添加字段] │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 展示名 key 类型 公式/默认 单位 操作 │ │
│ │ 网格上边界 grid_ceiling 数字 默认 11 元 编辑 删除│
│ │ 基准值 current_base 数字 默认 10 元 编辑 删除│
│ │ ƒ 盈亏比例 pctProfit 计算 (现价 ÷ 成本价 - 1)×100 % 编辑 删除│
│ └──────────────────────────────────────────────────────────┘ │
│ 字段增删后,持仓表列自动跟随(新增列默认追加末尾、删除列自动移除)│
│ [关闭] [保存字段] │
└────────────────────────────────────────────────────────────────┘
```
- 弹层形态(D-9):锚定弹层(与列设置同源视觉),宽度约 520px、最大高度约 70vh、内容超出内部滚动;
- **保存字段**按钮在弹层底部(右对齐,主色实心);**关闭**在左(次按钮);
- 有未保存改动时点「关闭」/ 外点/Esc → 二次确认「有未保存的字段改动,确定放弃?」(D-8)。
### 2.3 字段表单 · 类型 = 计算(第二阶段)
```
┌─ 添加字段 / 编辑字段 ────────────────────────────────────────────┐
│ 展示名 [盈亏比例 ] 标识 [pctProfit ] 类型 [计算 ▾] │
│ 单位 [% ] 小数位 [2] │
│ ┌ 公式 ───────────────────────────────────────────────────────┐ │
│ │ (现价 ÷ 成本价 - 1) × 100 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ [试算] 试算:平安银行 000001.SZ → 6.67 % │
│ 行情 [现价] [昨收] [今开] [最高] [最低] [成交量] [成交额] │
│ 合约 [涨停价] [跌停价] 持仓 [份额] [成本价] [最后成交价] │
│ 自定义 [网格大小] [基准值] [网格交易量] ← 点 tag 插入公式 │
│ 可用变量 行情:现价 昨收 今开 最高 最低 成交量 成交额 │
│ 合约:涨停价 跌停价 │
│ 持仓:份额 成本价 最后成交价 │
│ 自定义:网格大小 基准值 网格交易量 ← 本策略手填字段 │
│ [取消] [确定] │
└─────────────────────────────────────────────────────────────────┘
```
> 其余四类(文本/数字/布尔/枚举)表单形态不变,仅「类型」下拉多一项「计算」;选「计算」时隐藏默认值、显示公式区与小数位。
### 2.4 策略持仓表(只读计算列)
```
| | 代码 | 名称 | 现价 | 成本价 | 份额 | ƒ 盈亏比例 | ƒ 浮动盈亏 | 操作 |
|▸| 000001.SZ | 平安银行 | 11.20 | 10.50 | 1000 | 6.67 % | 700.00 元 | 全部移入/移出 |
```
- 表头 `ƒ`tertiary 色,`title="计算字段(只读)"`)+ 列名;悬停列头或单元格 title 显示公式原文(`= (现价 ÷ 成本价 - 1) × 100`);
- 单元格点击**不进入内联编辑**;手填字段列的行为不变(点击即编辑)。
### 2.5 列设置弹层(ColumnSettingsPopover
- 计算字段与手填字段同列混排(名称显示 `ƒ 盈亏比例`);显隐勾选 / 拖拽排序 / 间隙高亮线 / 立即持久化**零改动**。
## 3. 字段配置弹层状态机
| 状态 | 外观 | 交互 |
|---|---|---|
| 初始 | 列表(含公式原文列)+ 底部「保存字段」(无改动时禁用/次色) | 可添加/编辑/删除字段 |
| 有未保存改动 | 「保存字段」主色可点;关闭类动作触发放弃确认 | 保存 → 调 `strategies/schema-update` |
| 保存中 | 「保存字段」disabled(文案「保存中…」) | 防重入 |
| 保存成功 | Toast「字段已保存」+ 弹层关闭(或保持打开并刷新)+ 表格列同步跟随 | — |
| 保存失败 | Toast 错误 + 弹层不关闭 + 改动保留 | 可重试 |
| 公式校验失败(计算字段) | 表单内公式框**描红** + 错误行(`语法错误:第 8 字符处缺少运算符` / `未知变量「网格距」` | 表单不关闭,光标回公式框 |
| 试算中 | 「试算」disabled(「试算中…」) | 防重入 |
| 试算成功/失败 | 行内 `试算:平安银行 000001.SZ → 6.67 %` / error 色原因(`该策略当前无持仓` | 不阻塞保存 |
## 4. 交互细节
1. **打开入口**:点策略 tab 顶栏「字段配置」→ 弹层打开并显示**本策略**字段定义(各自 tab 管自己,Q4);数据来源沿用 `strategies` 端点(取当前策略 configSchema)。
2. **字段列表**:列 = 展示名 / key / 类型 / 公式或默认值 / 单位 / 操作(编辑、删除);计算字段无默认值,公式列等宽、单行省略、悬停全文。
3. **添加/编辑字段**:沿用现有表单(展示名 → 自动生成 key、类型下拉、枚举选项、默认值、单位)**新增**「计算」分支(见 2.3);`类型` 下拉:文本 / 数字 / 布尔 / 枚举 / **计算**
4. **删除字段**:列表内即时移除(弹层内草稿),**保存**后生效;保存后该列自动消失(归一化跟随),行内残留旧值按 R-013 既有防御隐藏。
5. **保存**:调新增单策略端点 `strategies/schema-update { strategyId, configSchema }`;成功后 Toast + 刷新当前策略数据(列与字段同步);**不写 values 列**、不动份额账本。
6. **变量插入**(计算字段,2026-09-10 老师反馈改为标签平铺):公式框下方按分组(行情 / 合约 / 持仓 / 自定义字段)**平铺变量 tag**(胶囊形、悬停显示释义),点选即插入到公式框光标处(无光标信息则追加末尾并补空格);不自弹下拉菜单。
7. **变量命名规则**(D-1 已拍板):内置变量固定中文名(现价/昨收/今开/最高/最低/成交量/成交额/涨停价/跌停价/份额/成本价/最后成交价);自定义字段以其**展示名**作变量名。
8. **保存校验(服务端)**:① 公式非空且语法合法;② 变量全部存在;③ 同策略字段展示名唯一、且不与内置变量名冲突;④ 仅引用**非公式字段**(目录层排除)。失败 → Toast + 公式框描红 + 定位文案。
9. **试算**:服务端取该策略一行真实持仓(优先份额 > 0 首行)→ 变量目录取数 → 求值 → 返回 {`code`,`name`,`value`};前端按单位/小数位格式化显示。
10. **持仓表只读渲染**:按小数位 + 单位格式化为 `6.67 %` / `700.00 元`;悬停显示公式原文。
11. **刷新语义**(已知取舍):计算列随 `strategy-positions` 轮询刷新(行情服务端 5s 更新,列值刷新粒度 = 接口轮询周期);不与 `PriceCell` 实时高亮联动。
12. **历史持仓行**:计算列显示 `—`(清仓行无「现价」语义,不做历史时点计算)。
13. **旧公式引用已删变量**D-4 已拍板):字段列表给 `⚠` 标记 + 悬停原因;运行期该列显示 `—`
14. **设置页**(第一阶段):不再有「策略分组」子 tab;Tab 设置里策略行与内置行同权(显隐 + 拖拽排序 + 间隙高亮线),**不提供**新增/重命名/删除任何入口。
15. **键盘/可及**:Esc / 外点关闭弹层(有未保存改动则确认);`ƒ``⚠` 标记均带 title 文本(不单靠颜色表意)。
## 5. 空态与边界
| 场景 | UI |
|---|---|
| 本策略尚无字段 | 弹层列表空态:「尚未配置字段 —— 点击「+ 添加字段」为本策略添加(该策略下每个持仓按此定义填写值)」 |
| 该策略无手填字段(配计算字段时) | 变量菜单「自定义」分组空态:「本策略暂无手填字段」 |
| 该策略当前无持仓 | 试算行内:「暂无可试算的持仓数据(保存不受影响)」 |
| 行情缺失 | 计算列单元格 `—`tertiary 色,不报错) |
| 计算失败(除零/非有限) | 单元格 `—` |
| 公式为空 / 未知变量 / 变量名冲突 | 拒绝保存 + 公式框描红 + 具体文案(见 §4.8) |
| 弹层有未保存改动时关闭 | 二次确认「有未保存的字段改动,确定放弃?」(D-8,AI 默认) |
| 未分配持仓 / 无 holding 锚点行 | 计算列照常显示(公式只用行情 + 持仓账本,不依赖 holding 锚点) |
| 设置页 | 仅「Tab 设置 / QMT 连接配置」两个子 tab |
## 6. 组件落点(技术方案阶段细化)
- `src/client/views/StrategyTab.jsx`:顶栏新增「字段配置」按钮(列设置旁)+ 弹层开关;计算列只读渲染(`ƒ` 表头 + title + 格式化 + `—`);
- `src/client/views/FieldConfigDialog.jsx`(**新增**):字段配置弹层容器(宽度/滚动/底部按钮/放弃确认),内容复用改造后的 StrategyFieldsEditor
- `src/client/views/StrategyFieldsEditor.jsx`(**改造**):作为弹层内容;保存回调改单策略端点;类型下拉加「计算」+ 公式分支(公式框 / 变量选择器 / 试算 / 小数位);
- `src/client/views/SettingsSection.jsx`(**改造**):移除「策略分组」子 tab(含 CRUD 与确认弹窗);
- `src/client/views/ColumnSettingsPopover.jsx`:列名 `ƒ` 前缀(仅文案);
- 服务端(技术方案阶段):`strategies` 常量 + tabs 策略条目固定 + 存储归一化;`strategies/schema-update` 单策略字段端点;变量目录 + 表达式引擎 + `strategy-positions` 现算 + 校验/试算端点;
- 零改动面:`FieldCellEditor`(formula 不进入)、列配置持久化与归一化、历史持仓路径、份额账本与交易归属。
## 7. 已拍板决策(2026-09-10 老师逐项确认)
| # | 问题 | 结论 |
|---|---|---|
| D-1 | 变量命名细则 | 内置固定中文名;自定义字段用**展示名**(新增唯一性 + 不与内置名冲突校验) |
| D-2 | 计算列视觉区分 | 表头 `ƒ` 前缀 + 悬停看公式;不加背景色 |
| D-3 | 试算预览 | 本期做 |
| D-4 | 已删变量的提示 | 字段列表 `⚠` 标记 + 悬停原因;运行期 `—` |
| D-5 | 小数位 | 数字输入 0-4,默认 2 |
| D-6 | 公式框能力 | 等宽字体 + 选择器插入;不做实时高亮/自动补全 |
| D-7 | 约束文档更正 | 一并更正 UI约束-005 + 新增计算字段约束条目 |
| D-8 | 弹层未保存改动的关闭行为 | **AI 默认**:二次确认放弃;老师可否决(备选:直接丢弃 / 禁止关闭) |
| D-9 | 弹层形态 | **AI 默认**:锚定弹层(与列设置同源视觉),~520px 宽、70vh 高、内部滚动;老师可否决(备选:右侧抽屉 / 居中模态) |
## 8. 约束与历史需求联动(实施时执行)
- **约束修订**R-027):产品约束-002/003/004(标签体系 / 标签自定义 + 显示开关场景收敛为「固定两策略 + 字段自定义」)、产品约束-009(Tab 统一管理中「策略条目动态化」条款)、UI约束-002(设置页子 tab 构成:移除策略分组)、UI约束-003(Tab 设置:策略行不再随 CRUD 增删)、**UI约束-005**(字段配置 UI 入口迁移 + 更正为「表格列化 + 单元格内联编辑」现状);
- **约束新增**R-026):拟 UI约束-008(计算字段表单 + 只读计算列)、产品功能约束(计算字段语义:不落库 / 只读 / 仅非公式变量)、技术方案约束(变量目录 + 表达式引擎 + 服务端现算);
- **历史需求取代**:R-003(策略 CRUD)→ 退役;R-011(Tab 统一管理)→ 策略条目动态化部分取代、显隐排序机制保留;R-013 → 字段模型/列机制保留、入口条款取代(均由 R-027 取代,索引标注追溯)。
## 9. 待确认/顺带发现
- 无(D-8 / D-9 为 AI 默认项,老师如有异议直接改判)。