fix(任务): 修复列表与看板拖拽「能触发但放不下」—— uuid 主键被 Number() 成 NaN
Docker Build and Push / build-image (push) Successful in 1m34s
Docker Build and Push / build-image (push) Successful in 1m34s
0.2.1 切本地库后 Task.id / BoardColumn.id 为 uuid 字符串, 拖拽落点却用 Number(dataset.xxxId) 取值得到 NaN: - 看板 hitColumn 返回 NaN -> NaN 为 falsy, onDrop 永不触发; 目标列也不高亮 - 列表 colId/taskId 为 NaN -> 组内命中与换列 findIndex 恒失败, 松手无操作 (打卡侧仍走 REST 数字 id, 故未受影响) - 新增 src/pages/task/ids.js: idStr / sameId / datasetId, 统一按字符串比对 - useColumnDrag / useRowDrag: 落点、组内命中、换列、提交判定改用 sameId - ProjectView: moveToColumn / reorderTask / changeStatus 改用 sameId, 落点为空直接忽略, 不把坏 id 写进本地库 - api/task.js: update 拦截 "NaN"/"undefined" 列 id, 报「看板列无效」 验证: headless Edge + CDP 派发真实指针事件驱动两个 hook (uuid 数据), 修复前 4 条落点断言全挂, 修复后全部通过。
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { datasetId, sameId } from '../../pages/task/ids.js'
|
||||
|
||||
// 看板卡片列拖动 (重点设计 §7.1)
|
||||
// 方案要点:
|
||||
@@ -60,10 +61,11 @@ export default function useColumnDrag({ onDrop, scrollSelector = '.board-wrap' }
|
||||
/** 拖动刚结束 (350ms 内): 抑制松手合成的 click 误开详情页 */
|
||||
const justEnded = useCallback(() => Date.now() - lastEnd.current < 350, [])
|
||||
|
||||
// 命中列 id: 本地库列 id 是 uuid 字符串, 不能 Number() (会变 NaN -> 落不下去)
|
||||
const hitColumn = (x, y) => {
|
||||
const el = document.elementFromPoint(x, y)
|
||||
const colEl = el?.closest?.('[data-column-id]')
|
||||
return colEl ? Number(colEl.dataset.columnId) : null
|
||||
return colEl ? datasetId(colEl.dataset.columnId) : null
|
||||
}
|
||||
|
||||
// 边缘自动滚动 (±36px 触发, 每帧 10px)
|
||||
@@ -105,7 +107,7 @@ export default function useColumnDrag({ onDrop, scrollSelector = '.board-wrap' }
|
||||
if (s.active) {
|
||||
lastEnd.current = Date.now()
|
||||
const col = hitColumn(ev.clientX, ev.clientY)
|
||||
if (col && s.task && col !== s.task.columnId) onDropRef.current?.(s.task, col)
|
||||
if (col && s.task && !sameId(col, s.task.columnId)) onDropRef.current?.(s.task, col)
|
||||
}
|
||||
cleanup()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { datasetId, sameId } from '../../pages/task/ids.js'
|
||||
|
||||
// 列表视图任务行拖动: 组间换列 + 组内排序
|
||||
// 触发: 行长按 400ms, 或按住行首 ⠿ 把手 (touch-action:none + touchstart
|
||||
@@ -78,15 +79,15 @@ export default function useRowDrag({ columns, onDrop, onReorder }) {
|
||||
moveGhost(ev.clientX, ev.clientY)
|
||||
|
||||
// 组内落点: 命中本组 ([data-column-id]=当前列) 的某条顶层行
|
||||
// 本地库的列/任务 id 均为 uuid 字符串 -> 一律按字符串比对, 不要 Number()
|
||||
const hitEl = document.elementFromPoint(ev.clientX, ev.clientY)
|
||||
const rowEl = hitEl?.closest?.('[data-task-id]')
|
||||
const colEl = hitEl?.closest?.('[data-column-id]')
|
||||
const colId = colEl ? Number(colEl.dataset.columnId) : null
|
||||
const curCol = orderedRef.current[s.toIdx]?.id
|
||||
const colId = colEl ? datasetId(colEl.dataset.columnId) : null
|
||||
|
||||
if (rowEl && colId === s.task.columnId) {
|
||||
const id = Number(rowEl.dataset.taskId)
|
||||
if (id !== s.task.id) {
|
||||
if (rowEl && sameId(colId, s.task.columnId)) {
|
||||
const id = datasetId(rowEl.dataset.taskId)
|
||||
if (id && !sameId(id, s.task.id)) {
|
||||
const r = rowEl.getBoundingClientRect()
|
||||
const hit = { id, after: ev.clientY > r.top + r.height / 2 }
|
||||
if (!s.over || s.over.id !== hit.id || s.over.after !== hit.after) {
|
||||
@@ -100,7 +101,7 @@ export default function useRowDrag({ columns, onDrop, onReorder }) {
|
||||
|
||||
// 换列: 手指划到哪个分组就是目标列, 划出所有分组时保持上一目标
|
||||
if (colId != null) {
|
||||
const to = orderedRef.current.findIndex((c) => c.id === colId)
|
||||
const to = orderedRef.current.findIndex((c) => sameId(c.id, colId))
|
||||
if (to >= 0 && to !== s.toIdx) {
|
||||
s.toIdx = to
|
||||
setDrag({ task: s.task, fromIdx: s.fromIdx, toIdx: to })
|
||||
@@ -116,7 +117,7 @@ export default function useRowDrag({ columns, onDrop, onReorder }) {
|
||||
onReorderRef.current?.(s.task, s.over.id, s.over.after)
|
||||
} else {
|
||||
const target = orderedRef.current[s.toIdx]
|
||||
if (target && target.id !== s.task.columnId) onDropRef.current?.(s.task, target.id)
|
||||
if (target && !sameId(target.id, s.task.columnId)) onDropRef.current?.(s.task, target.id)
|
||||
}
|
||||
}
|
||||
cleanup()
|
||||
@@ -152,7 +153,7 @@ export default function useRowDrag({ columns, onDrop, onReorder }) {
|
||||
}
|
||||
|
||||
const startDrag = (ev, task, instant) => {
|
||||
const fromIdx = orderedRef.current.findIndex((c) => c.id === task.columnId)
|
||||
const fromIdx = orderedRef.current.findIndex((c) => sameId(c.id, task.columnId))
|
||||
if (fromIdx < 0) return // 不在任何列 (如「其他」组) 的任务不支持拖动
|
||||
st.current = {
|
||||
...st.current, startX: ev.clientX, startY: ev.clientY, task, fromIdx, toIdx: fromIdx,
|
||||
|
||||
Reference in New Issue
Block a user