迭代19-21收尾: 指示灯自适应探测(R-022/R-023)+ 列设置拖拽排序(R-024)+ Tab设置间隙线统一(R-025)+ 需求池归档(R-014/15/16/17/19 → 已完成)

This commit is contained in:
2026-09-10 14:04:51 +08:00
parent 8782aa9c50
commit 3dfa39b8bf
29 changed files with 1216 additions and 72 deletions
@@ -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 tokentop:0(插前)/ bottom:0(插后)。
## 边界与不做
- 服务端/API/表格列结构/持久化逻辑零改动;
- 不动「策略分组」子 tab(该处无排序交互);
- 不引入第三方 DnD 库。
## 回归
- pnpm typecheck + buildSettingsSection 变更需重编 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 归档至 已完成/ 并更新索引实现状态。