迭代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,84 @@
|
||||
# 技术实现方案:21-Tab设置排序间隙高亮统一
|
||||
|
||||
## 总体思路
|
||||
|
||||
把 SettingsSection.jsx 的 TabSettings 组件落点指示从「整行高亮(overId → 行背景 color-mix 12%)」
|
||||
改为「行间间隙高亮线(overPos { idx, half } → 目标行上缘/下缘 3px 主色线)」,与 ColumnSettingsPopover.jsx
|
||||
(R-024)逐点对齐;其余交互(≡ 手柄、开关、立即持久化、徽标、saving 禁用)不动。
|
||||
|
||||
## 改动点(单文件:src/client/views/SettingsSection.jsx,仅 TabSettings 组件)
|
||||
|
||||
### 1. 状态
|
||||
|
||||
- `overId`(整行高亮锚点)→ `overPos { idx, half }`(间隙线锚点行下标 + 上/下缘);
|
||||
- `dragId` 保留。
|
||||
|
||||
### 2. 悬停判定(与 R-024 全同)
|
||||
|
||||
```jsx
|
||||
const handleOver = (e, index) => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const half = e.clientY - rect.top < rect.height / 2 ? 'top' : 'bottom';
|
||||
setOverPos({ idx: index, half });
|
||||
};
|
||||
```
|
||||
|
||||
### 3. drop(事件内按坐标重算,不依赖 state 闭包)
|
||||
|
||||
```jsx
|
||||
const handleDrop = async (e, index) => {
|
||||
e.preventDefault();
|
||||
if (!tabs || !dragId) { setDragId(null); setOverPos(null); return; }
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
const before = e.clientY - rect.top < rect.height / 2;
|
||||
let to = before ? index : index + 1;
|
||||
const from = tabs.findIndex((x) => x.id === dragId);
|
||||
if (from < 0 || to < 0 || to > tabs.length) { setDragId(null); setOverPos(null); return; }
|
||||
if (to === from || to === from + 1) { setDragId(null); setOverPos(null); return; } // 原地跳过写库
|
||||
const next = tabs.slice();
|
||||
const [moved] = next.splice(from, 1);
|
||||
const insertAt = to > from ? to - 1 : to;
|
||||
next.splice(insertAt, 0, moved);
|
||||
const ordered = next.map((x, i) => ({ ...x, order: i }));
|
||||
setDragId(null);
|
||||
setOverPos(null);
|
||||
await persist(ordered, '「' + moved.name + '」已移动');
|
||||
};
|
||||
```
|
||||
|
||||
### 4. 行渲染(tr 上挂间隙线样式)
|
||||
|
||||
tr 为表格行,直接内嵌 div 会破坏表格结构(tr 只允许 td/th)——**间隙线以 td 为定位锚点绘制**:
|
||||
首选在每个 td 内各画一段 3px 高亮线(结构合规、视觉连续),实现纪要记录最终选型。
|
||||
|
||||
```jsx
|
||||
<tr
|
||||
key={t.id}
|
||||
draggable={!saving}
|
||||
onDragStart={(e) => { setDragId(t.id); setOverPos(null); e.dataTransfer.effectAllowed = 'move'; }}
|
||||
onDragOver={(e) => handleOver(e, i)}
|
||||
onDrop={(e) => handleDrop(e, i)}
|
||||
onDragEnd={() => { setDragId(null); setOverPos(null); }}
|
||||
style={{ cursor: saving ? 'default' : 'grab' }}
|
||||
>
|
||||
<td style={{ position: 'relative', padding: '8px 6px' }}> {gapLine(i, 'td1')} ≡</td>
|
||||
...
|
||||
</tr>
|
||||
```
|
||||
|
||||
间隙线样式(与 R-024 全同):absolute、left/right 内缩 6、height 3、borderRadius 2、
|
||||
背景 primary token;top:0(插前)/ bottom:0(插后)。
|
||||
|
||||
## 边界与不做
|
||||
|
||||
- 服务端/API/表格列结构/持久化逻辑零改动;
|
||||
- 不动「策略分组」子 tab(该处无排序交互);
|
||||
- 不引入第三方 DnD 库。
|
||||
|
||||
## 回归
|
||||
|
||||
- pnpm typecheck + build(SettingsSection 变更需重编 web bundle);
|
||||
- 页面人工验收:Tab 设置拖动 → 间隙线位置正确(上=前/下=后)、整行高亮消失、drop 即存、
|
||||
拖回原位不重复写库、刷新页面后顺序保持。
|
||||
@@ -0,0 +1,26 @@
|
||||
# 迭代目标:21-Tab设置排序间隙高亮统一
|
||||
|
||||
## 目标
|
||||
|
||||
设置页「Tab 设置」的分组 tab 拖拽排序落点指示,从**整行高亮**统一为**行间间隙高亮线**,
|
||||
与策略 tab 列设置弹层(R-024/UI约束-007)完全一致——明确指示插入目标行的前/后。
|
||||
|
||||
## 目标描述
|
||||
|
||||
- **背景**:迭代 20 复盘遗留第 2 项——R-024 将列设置弹层落点改为间隙高亮线后老师确认「直观多了」,
|
||||
但设置页 Tab 设置(R-011/UI约束-003)仍是整行高亮(overId → 目标行淡蓝底),无法判断插入前/后,
|
||||
两处观感不一致。2026-09-10 老师指令:Tab 设置也统一为间隙高亮线形式。
|
||||
- **范围**:仅改 src/client/views/SettingsSection.jsx 的 TabSettings 组件落点指示;表格结构、≡ 手柄、
|
||||
显隐开关、立即持久化(tabs/update)、徽标、saving 禁用全部维持不变;服务端/API 零改动。
|
||||
- **验收线**:拖动时目标行上缘/下缘显示主色间隙线(上半区=插前、下半区=插后);整行高亮消失;
|
||||
拖回原位不重复写库;drop 后立即持久化。对应需求 R-025(已定稿)。
|
||||
|
||||
## 目标讨论过程
|
||||
|
||||
1. 迭代 20 复盘(2026-09-10)列遗留第 2 项:Tab 设置整行高亮是否统一为间隙线,留待老师评估。
|
||||
2. 老师指令(2026-09-10):设置页分组 tab 排序也改上面(列设置弹层)的拖动高亮形式 → 登记 R-025 已定稿,
|
||||
迭代 21 实施。
|
||||
|
||||
## 对老师(项目主理人)的配合需求
|
||||
|
||||
- 无(纯前端交互统一,服务端零改动,验收走页面人工确认)。
|
||||
@@ -0,0 +1,27 @@
|
||||
# 验收标准:21-Tab设置排序间隙高亮统一
|
||||
|
||||
## 验收线(在哪里验收)
|
||||
|
||||
DSH Web GUI → 设置页 → 「Tab 设置」子 tab → 拖动任一标签行排序。
|
||||
页面侧人工验收;服务端逻辑零改动。
|
||||
|
||||
## 验收方法(逐项操作)
|
||||
|
||||
| # | 操作 | 预期 |
|
||||
|---|---|---|
|
||||
| 1 | 打开「Tab 设置」,按住某行 ≡ 手柄(含整行)拖动到另一行附近移动鼠标 | 目标行上缘/下缘显示**主色间隙高亮线**(插入线),明确指示落点在目标 tab 之前还是之后;原整行淡蓝高亮消失 |
|
||||
| 2 | 拖动到目标行上半区 vs 下半区各松手一次 | 上半区 = 插入该 tab **之前**;下半区 = 插入该 tab **之后**(以落点线位置为准) |
|
||||
| 3 | drop 后落点立即生效 | Toast「已移动」,刷新页面后 tab 顺序保持(tabs/update 已写库) |
|
||||
| 4 | 拖动期间把行拖回原位置或原地松手 | 无异常,顺序不变,且不触发重复写库 |
|
||||
| 5 | 既有交互回归 | ≡ 手柄、显示/隐藏开关、内置/策略徽标、saving 期间拖拽禁用——全部维持原行为 |
|
||||
| 6 | 策略分组子 tab | 无排序交互,不受影响(仍为 新增/重命名/删除) |
|
||||
|
||||
## 验收目标
|
||||
|
||||
- 功能对准:Tab 设置落点与列设置弹层(R-024)**同一套间隙线交互**,观感一致;
|
||||
- 零回归:既有 tabs 持久化(整表 order + visible)、显隐开关、策略改名联动 name join 不受影响;
|
||||
- typecheck + build 通过;无新增硬编码色值(UI约束-004)。
|
||||
|
||||
## 判定
|
||||
|
||||
- 老师页面人工确认 6 项全过 + build 通过 ⇒ 迭代 21 验收通过,R-025 归档至 已完成/ 并更新索引实现状态。
|
||||
Reference in New Issue
Block a user