迭代19-21收尾: 指示灯自适应探测(R-022/R-023)+ 列设置拖拽排序(R-024)+ Tab设置间隙线统一(R-025)+ 需求池归档(R-014/15/16/17/19 → 已完成)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# 技术实现方案:20-策略列设置拖拽排序
|
||||
|
||||
## 总体思路
|
||||
|
||||
在 ColumnSettingsPopover.jsx 内复用 Tab 设置(SettingsSection.jsx TabSettings)已验证的
|
||||
**原生 HTML5 Drag & Drop** 模式:行 draggable + onDragStart/onDragOver/onDrop + 落点高亮 + drop 重排,
|
||||
零第三方依赖。持久化沿用现有 strategy-columns/update 整表覆盖 RPC,把「保存」语义从手动按钮改为
|
||||
每次变更(drop / 勾选 / ↑↓)立即提交。
|
||||
|
||||
## 改动点(单文件:src/client/views/ColumnSettingsPopover.jsx)
|
||||
|
||||
### 1. 状态
|
||||
|
||||
- 已有 `dragKey`(未被使用,正好用于 DnD):当前被拖行的 column.key;
|
||||
- 新增 `overKey`:当前悬停落点的 column.key(用于高亮);
|
||||
- `saving` 保持:正在写库时禁止再次拖动/勾选。
|
||||
|
||||
### 2. 持久化函数(替换原 save)
|
||||
|
||||
- 新增 `persist(nextList, msg)`:提交整表 `{key, visible}` → strategy-columns/update;
|
||||
成功 Toast 提示 + onSaved() 刷新;失败 Toast 错误 + 本地回滚(还原为服务端最新列配置)。
|
||||
- 勾选显隐:切完立即 persist(不再等「保存」按钮);
|
||||
- ↑↓ 箭头:移动完立即 persist;
|
||||
- drop 重排:重排完立即 persist(对齐 Tab 设置)。
|
||||
|
||||
### 3. 拖拽事件(照搬 TabSettings 模式)
|
||||
|
||||
```jsx
|
||||
<div key={c.key} draggable={!saving}
|
||||
onDragStart={(e) => { setDragKey(c.key); e.dataTransfer.effectAllowed = 'move'; }}
|
||||
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; setOverKey(c.key); }}
|
||||
onDragLeave={() => { if (overKey === c.key) setOverKey(null); }}
|
||||
onDrop={(e) => { e.preventDefault(); handleDrop(c.key); }}
|
||||
style={{ cursor: saving ? 'default' : 'grab',
|
||||
background: overKey === c.key ? 'color-mix(in srgb, var(--dsw-alias-state-business-primary, #1565c0) 12%, transparent)' : 'transparent' }}>
|
||||
<span>≡</span>
|
||||
<button>↑</button><button>↓</button>
|
||||
<input type="checkbox" ... />
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4. handleDrop(照搬 TabSettings 逻辑,key 定位)
|
||||
|
||||
```js
|
||||
const handleDrop = (targetKey) => {
|
||||
if (!dragKey || dragKey === targetKey) { setDragKey(null); setOverKey(null); return; }
|
||||
const from = list.findIndex((c) => c.key === dragKey);
|
||||
const to = list.findIndex((c) => c.key === targetKey);
|
||||
if (from < 0 || to < 0) { setDragKey(null); setOverKey(null); return; }
|
||||
const next = list.slice();
|
||||
const [moved] = next.splice(from, 1);
|
||||
next.splice(to, 0, moved);
|
||||
setDragKey(null); setOverKey(null);
|
||||
persist(next, `「${moved.label}」已移动`, next);
|
||||
};
|
||||
```
|
||||
|
||||
### 5. UI 文案
|
||||
|
||||
- 标题下提示改为:「勾选显示列,按住 ≡ 拖动调整顺序;代码/名称/操作固定」;
|
||||
- 底部按钮区:去掉「保存」,保留「关闭」;
|
||||
- 每行最前加 ≡ 手柄(配色沿用 Tab 设置 `#999`/label-tertiary)。
|
||||
|
||||
## 边界与不做
|
||||
|
||||
- 服务端、API、表格渲染零改动(strategy-columns/update 已是整表覆盖语义);
|
||||
- 固定列不参与排序,不进列表(现状维持);
|
||||
- 不做方案 C(表格表头直接拖列头),不引入第三方 DnD 库;
|
||||
- `saving` 期间禁用拖拽与勾选,防并发写。
|
||||
|
||||
## 回归
|
||||
|
||||
- scripts/test-r013-columns.mjs(列配置归一化/读写的服务端逻辑,未改动应全绿);
|
||||
- npm run typecheck + build(tsdown 产物,弹层 JSX 变更需重编 web bundle);
|
||||
- 页面人工验收:拖拽重排 / 落点高亮 / drop 即存 / ↑↓ 微调 / 刷新保持。
|
||||
## 实现纪要(2026-09-10,老师验收反馈后修订)
|
||||
|
||||
> 老师反馈:初版按原方案做的是「整行高亮」(overKey → 目标行淡蓝底),拖动时无法判断会插入目标列的**前面还是后面**,
|
||||
> 观感不符合习惯。定稿修订为 **间隙高亮线**:
|
||||
|
||||
- 落点判定:拖动悬停时读鼠标在目标行内的 **Y 坐标**(getBoundingClientRect),上半 → 插入该行**之前**(线画行上缘),
|
||||
下半 → 插入该行**之后**(线画行下缘);两行之间显示 3px 主色圆角高亮线(absolute 定位在行自身 padding 内,left/right 6,
|
||||
无负偏移无裁剪风险),指示精确插入位置。
|
||||
- drop 判定:drop 事件内直接按坐标重算 before(不依赖 state 闭包,避免滞后);无操作原地(insertAt 等于原槽位)
|
||||
跳过写库;移除后目标下标左移(to > from 时 to-1)。
|
||||
- 状态:overKey(整行)→ overPos { idx, half }(间隙线锚点行 + 上下缘);保留 onDragEnd 清理防拖出弹层残留。
|
||||
- 边界维持:固定列不参与、服务端零改动、↑↓ 兜底保留、立即持久化不变。
|
||||
@@ -0,0 +1,47 @@
|
||||
# 迭代复盘:20-策略列设置拖拽排序
|
||||
|
||||
## 结论
|
||||
|
||||
达成(R-024):策略 tab「列设置」弹层的列排序从 ↑↓ 箭头点按升级为**鼠标选中后拖动排序**,
|
||||
复用 R-011 Tab 设置已验证的原生 HTML5 DnD 模式;老师 2026-09-10 页面确认交互直观,验收通过。
|
||||
|
||||
## 事实记录
|
||||
|
||||
### src/client/views/ColumnSettingsPopover.jsx(单文件改动,服务端零改动)
|
||||
|
||||
- 排序交互升级:每行新增 ≡ 拖拽手柄(整行 draggable),拖到目标位置松手即重排;↑↓ 箭头保留作兜底微调。
|
||||
- **落点立即持久化**:勾选显隐 / 拖拽 / ↑↓ 任一操作立即调 strategy-columns/update 整表提交;弹层去掉「保存」
|
||||
按钮只留「关闭」,Toast 反馈;saving 期间禁用拖拽与勾选防并发写;失败回滚到服务端最新配置。
|
||||
- **落点指示修订(老师验收反馈)**:初版为整行高亮(overKey → 目标行淡蓝底),老师反馈无法判断插入目标列的
|
||||
前/后;改为**行间间隙高亮线**——按鼠标在目标行内的 Y 坐标取上半/下半,决定线画在该行上缘(插其前)/下缘(插其后),
|
||||
明确指示精确插入位置;drop 事件内按坐标重算(不依赖 state 闭包),拖回原位置不重复写库,onDragEnd 清理残留。
|
||||
- 修复:间隙线渲染曾误将 style 对象直接作 React child(`{gapLineStyle(i)}`),改为条件渲染 `<div style={gapLineStyle(i)}/>`。
|
||||
|
||||
### 验证
|
||||
|
||||
- pnpm typecheck 0 错;pnpm build 通过(client bundle 185KB 含间隙线逻辑,grep 确认编译产物);
|
||||
- test-r013-columns.mjs 回归:11 过 / 5 败 —— **与本次改动无关**(git stash 基线验证同样 11/5);
|
||||
失败根因:脚本仍按 R-013 时期期望「6 个基础列」,而 COLUMN_META 已有 10 列(R-015 新增涨停/跌停/今开/最高 4 个默认隐藏列),
|
||||
测试期望未随迭代 13 更新的存量债务,不在本迭代范围,列为遗留事项。
|
||||
|
||||
### 文档
|
||||
|
||||
- docs/05-需求池/R-024.md:定稿 + 变更记录(整行高亮 → 间隙高亮线);
|
||||
- docs/03-设计约束/UI交互约束.md:新增 UI约束-007(列表/弹层排序交互规范:间隙高亮线标准,Tab 设置是否统一待老师评估);
|
||||
- docs/04-迭代记录/20-策略列设置拖拽排序/:迭代目标 + 技术实现方案(含实现纪要)+ 验收标准(10 项)。
|
||||
|
||||
## 遗留事项(待老师决策)
|
||||
|
||||
1. **test-r013-columns.mjs 期望过期**:COLUMN_META 现 10 列(R-015 新增 4 行情列默认隐藏),脚本期望仍为 6 列,
|
||||
默认列数相关 5 项断言失败。建议后续小迭代同步更新脚本期望(或重构为从 COLUMN_META 推导期望),本次未动。
|
||||
2. **Tab 设置整行高亮是否统一为间隙高亮线**:UI约束-007 列设置弹层已用间隙线,Tab 设置(UI约束-003)仍为整行高亮;
|
||||
老师反馈列设置「直观多了」——若 Tab 设置同样已存在「无法判断插入前/后」的观感问题,可同模式调整(独立小任务,本次未动)。
|
||||
|
||||
## 经验沉淀(候选)
|
||||
|
||||
- 排序类拖拽的落点指示,**间隙线(两行之间)比整行高亮更符合用户观感**:整行高亮只能表达「落在哪一行」,
|
||||
无法表达「插入该行前还是后」;主流列表/表格拖拽(如 Notion、表格列排序)均用间隙指示。
|
||||
- 服务端整表覆盖语义(updateStrategyColumns 整表提交)天然适配前端即时持久化,无需逐条增量接口;
|
||||
「保存按钮」在每次变更即写库后失去存在意义(本迭代据此移除)。
|
||||
- 回归脚本的期望值要随数据模型演进同步更新(test-r013-columns 教训:R-015 扩展 COLUMN_META 后脚本未跟随,
|
||||
留下 5 项静默失败,直到本迭代回归才暴露)。
|
||||
@@ -0,0 +1,30 @@
|
||||
# 迭代目标:20-策略列设置拖拽排序
|
||||
|
||||
## 目标
|
||||
|
||||
策略 tab「列设置」弹层(ColumnSettingsPopover)的列排序交互从 **↑↓ 箭头点按** 升级为
|
||||
**鼠标选中后拖动排序**(原生 HTML5 DnD),并对齐 Tab 设置的即时持久化体验。
|
||||
|
||||
## 目标描述
|
||||
|
||||
- **背景**:策略 tab 的可配置列(基础数据列 + 自定义字段列)在「列设置」弹层中管理,排序目前依赖
|
||||
↑↓ 箭头(R-013 迭代 11 产物,当时刻意简化,代码注释自述「用箭头替代 DnD」)。老师反馈希望
|
||||
改成鼠标选中后拖动排序(2026-09-10)。
|
||||
- **结论**:复用 R-011 Tab 设置已验证的原生 HTML5 DnD 模式(draggable 行 + 落点高亮 + drop 重排),
|
||||
零第三方依赖;落点立即持久化(strategy-columns/update 整表覆盖),弹层去掉「保存」按钮只留「关闭」。
|
||||
- **范围**:仅改 src/client/views/ColumnSettingsPopover.jsx 单文件;服务端/API/表格渲染零改动;
|
||||
固定列(代码/名称/操作/展开箭头)不参与排序,维持现状。
|
||||
- **验收线**:拖拽可重排列顺序且落点高亮可见;drop 后立即写库(无需点保存);↑↓ 箭头仍可微调;
|
||||
勾选显隐立即生效;刷新后列顺序保持。对应需求 R-024(已定稿)。
|
||||
|
||||
## 目标讨论过程
|
||||
|
||||
1. 老师反馈(2026-09-10):策略 tab 列设计可否做成鼠标选中后拖动排序。
|
||||
2. AI 调研:Tab 设置(R-011)已实现同款原生 HTML5 DnD(SettingsSection.jsx),可零依赖复用;
|
||||
列设置弹层存在未使用的 dragKey 状态(原计划 DnD 的残留)。
|
||||
3. 登记 R-024(讨论中)→ 方案草案 A/B/C → 老师确认方案 A(弹层内拖拽);
|
||||
保存时机 / 箭头去留按 AI 推荐默认(立即持久化 + ↑↓ 保留兜底)。
|
||||
|
||||
## 对老师(项目主理人)的配合需求
|
||||
|
||||
- 无(纯前端交互优化,服务端零改动,验收走页面人工确认)。
|
||||
@@ -0,0 +1,32 @@
|
||||
# 验收标准:20-策略列设置拖拽排序
|
||||
|
||||
## 验收线(在哪里验收)
|
||||
|
||||
DSH Web GUI → 神之一手 → 任一策略 tab → 工具条「列设置」按钮 → 弹层。
|
||||
页面侧人工验收;服务端逻辑零改动,跑 scripts/test-r013-columns.mjs 回归确认无破坏。
|
||||
|
||||
## 验收方法(逐项操作)
|
||||
|
||||
| # | 操作 | 预期 |
|
||||
|---|---|---|
|
||||
| 1 | 打开「列设置」弹层,鼠标按住某行 ≡ 手柄(含整行)拖动到另一行附近移动鼠标 | 目标行上缘/下缘显示**主色间隙高亮线**(插入线),明确指示落点在目标列之前还是之后,非整行高亮 |
|
||||
| 2 | drop 后直接关闭弹层(不点任何保存) | 策略表格表头列顺序已按新顺序展示(立即持久化生效) |
|
||||
| 3 | 刷新页面 / 重新打开弹层 | 列顺序保持拖拽后的结果(写库成功) |
|
||||
| 4 | 点 ↑ / ↓ 箭头 | 仍可逐格微调,且立即持久化(关闭弹层后表格列顺序同步变化) |
|
||||
| 5 | 勾选/取消勾选某一列 | 显隐立即生效(关闭弹层后,表格对应列隐藏/出现) |
|
||||
| 6 | 拖动到目标行上半区 vs 下半区各松手一次 | 上半区 = 插入该目标列**之前**;下半区 = 插入该目标列**之后**(以落点线位置为准) |
|
||||
| 7 | 拖动期间把行拖回原位置或原地松手 | 无异常,列顺序不变(不重复写库) |
|
||||
| 8 | 固定列(代码/名称/操作) | 弹层列表不含固定列,不参与拖拽,恒显示(维持现状) |
|
||||
| 9 | 自定义字段列(某策略配了 configSchema) | 与基础列一样可拖拽排序、可勾选显隐 |
|
||||
| 10 | 保存中(快速连续拖动) | 无并发写异常;saving 期间拖拽/勾选禁用,Toast 提示保存结果 |
|
||||
|
||||
## 验收目标
|
||||
|
||||
- 功能正交:拖拽排序 / ↑↓ 微调 / 显隐勾选 三者并存且各自立即持久化;
|
||||
- 零回归:scripts/test-r013-columns.mjs 全绿、typecheck + build 通过;
|
||||
- 视觉一致性:≡ 手柄沿用 Tab 设置(UI约束-003)同套样式;落点为**间隙高亮线**(主色 token,UI约束-004 无硬编码色值);
|
||||
- 服务端零改动确认:git diff 无 src/api / src/settings.js 变更。
|
||||
|
||||
## 判定
|
||||
|
||||
- 老师页面人工确认 10 项全过 + 服务端回归全绿 ⇒ 迭代 20 验收通过,R-024 归档至 已完成/ 并更新索引实现状态。
|
||||
Reference in New Issue
Block a user