# 迭代 22 UI 交互设计:字段配置入口迁移 + 策略计算字段 > 依据:**R-027(重构·第一阶段,已定稿)** + **R-026(计算字段·第二阶段,已定稿)** + PLAN-019 | 日期:2026-09-10 > 状态:**已定稿**(D-1~D-7 老师逐项拍板;2026-09-10 因入口迁移(R-027)同步修订;D-8~D-9 为 AI 默认项,老师可否决) > 视觉基线:UI约束-004(--dsw-* token)、UI约束-006(ExpandChevron)、UI约束-007(间隙高亮线);UI约束-005 待随本次修订 > 复用:ColumnSettingsPopover(列设置)、StrategyFieldsEditor(改造为弹层内容)、Toast;FieldCellEditor 不参与(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 默认项,老师如有异议直接改判)。