Files

3.2 KiB
Raw Permalink Blame History

技术实现方案: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 全同)

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 闭包)

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 高亮线(结构合规、视觉连续),实现纪要记录最终选型。

<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 即存、 拖回原位不重复写库、刷新页面后顺序保持。