fix(V0.2): 二轮评审 6 项 —— BUILD_ID 格式 / 菜单与详情精简 / 拖动重做 / 键盘统一方案
Docker Build and Push / build-image (push) Successful in 4m5s
Docker Build and Push / build-image (push) Successful in 4m5s
1. BUILD_ID 改为 yyyyMMddHHmmss (去掉中间点), 仍 UTC+8
2. 任务 ⋯ 菜单去掉顶部任务名标题
3. 任务详情去掉底部「拷贝到其他项目」「删除任务」按钮 (⋯ 菜单已有)
4+6. 键盘遮挡统一方案 (重点设计 §7.2):
- index.html 加 interactive-widget=resizes-content
- App 级 useKeyboardInsetWatcher 写 --kb-inset (visualViewport+window 双源)
- 底部弹层统一 .kb-safe 类 (安全区+20px+键盘插入量, dvh 收缩+内部滚动)
- TaskDetail/UserPicker/CopyTaskPopup/信息块弹层全部迁移, 删除 per-component hook
- 信息块 textarea 补 focus scrollIntoView 兜底
5. 拖动方案重做 (重点设计 §7.1):
- 新 useColumnDrag: Pointer Events + setPointerCapture, 长按400ms原位抬起,
elementFromPoint 高亮目标列, 边缘 rAF 自动横滚; 移除手指跟随浮层(任务名)
- 列表行长按 500ms → 「移动到列」ActionSheet (不做自由拖动)
- 移除「拖到卡片转子任务」误触路径, 统一走详情页层级区
- 删除旧 useLongPressDrag
已验证: 99 文件语法 0 错误、导入 0 缺失
This commit is contained in:
@@ -1,78 +1,57 @@
|
||||
import { useState } from 'react'
|
||||
import { Toast } from 'antd-mobile'
|
||||
import TaskCard from '../../components/task/TaskCard.jsx'
|
||||
import useLongPressDrag from '../../components/task/useLongPressDrag.js'
|
||||
import useColumnDrag from '../../components/task/useColumnDrag.js'
|
||||
|
||||
// 看板视图 (T-BD-01, T-UI-07, T-ST-06/12)
|
||||
// 列横向滚动; 长按卡片拖拽换列 / 降为子任务
|
||||
// 看板视图 (T-BD-01, T-UI-07)
|
||||
// 拖动 = 长按 400ms 激活, 卡片原位抬起, 指针划过列高亮, 松手落列 (重点设计 §7.1)
|
||||
// 不做"拖到卡片上转子任务" —— 统一走详情页「转为子任务」
|
||||
export default function BoardView({
|
||||
tasks, columns, onMoveColumn, onMakeSubtask, onOpen, onToggleSub, onToggle, onMore, onQuickAdd,
|
||||
tasks, columns, onMoveColumn, onOpen, onToggleSub, onToggle, onMore, onQuickAdd,
|
||||
}) {
|
||||
const [overCol, setOverCol] = useState(null)
|
||||
|
||||
const drag = useLongPressDrag({
|
||||
onDropColumn: (task, columnId) => { setOverCol(null); onMoveColumn?.(task, columnId) },
|
||||
onDropTask: (task, parentId) => { setOverCol(null); onMakeSubtask?.(task, parentId) },
|
||||
onReject: (msg) => { setOverCol(null); Toast.show({ content: msg }) },
|
||||
})
|
||||
const drag = useColumnDrag({ onDrop: onMoveColumn })
|
||||
|
||||
const ordered = [...(columns || [])].sort((a, b) => a.sortOrder - b.sortOrder)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="board-wrap">
|
||||
{ordered.map((col) => {
|
||||
const list = (tasks || []).filter((t) => t.columnId === col.id)
|
||||
return (
|
||||
<div
|
||||
className={`bcol${overCol === col.id ? ' dragover' : ''}`}
|
||||
key={col.id}
|
||||
data-column-id={col.id}
|
||||
onDragEnter={() => setOverCol(col.id)}
|
||||
onDragLeave={() => setOverCol((c) => (c === col.id ? null : c))}
|
||||
>
|
||||
<div className="bcol-head">
|
||||
<span className="bcol-name">{col.name}</span>
|
||||
<span className="bcol-cnt">{list.length}</span>
|
||||
{col.boundStatus ? (
|
||||
<span className="bcol-bind">
|
||||
↔ {col.boundStatus === 'done' ? '完成' : col.boundStatus === 'doing' ? '执行中' : '未开始'}
|
||||
</span>
|
||||
) : (
|
||||
<span className="bcol-bind off">未绑定</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bcol-body">
|
||||
{list.map((t) => (
|
||||
<TaskCard
|
||||
key={t.id}
|
||||
task={t}
|
||||
dragging={drag.dragging?.task?.id === t.id}
|
||||
highlight={drag.dragging && drag.dragging.task.id !== t.id && overCol === col.id}
|
||||
bindDrag={drag.bind}
|
||||
onClick={() => { if (!drag.dragging) onOpen?.(t) }}
|
||||
onToggleSub={onToggleSub}
|
||||
onToggle={onToggle}
|
||||
onMore={onMore}
|
||||
/>
|
||||
))}
|
||||
{list.length === 0 && <div className="bcol-empty">暂无任务</div>}
|
||||
<div className="badd" onClick={() => onQuickAdd?.(col.id)}>+ 添加任务</div>
|
||||
</div>
|
||||
<div className="board-wrap">
|
||||
{ordered.map((col) => {
|
||||
const list = (tasks || []).filter((t) => t.columnId === col.id)
|
||||
return (
|
||||
<div
|
||||
className={`bcol${drag.overCol === col.id ? ' dragover' : ''}`}
|
||||
key={col.id}
|
||||
data-column-id={col.id}
|
||||
>
|
||||
<div className="bcol-head">
|
||||
<span className="bcol-name">{col.name}</span>
|
||||
<span className="bcol-cnt">{list.length}</span>
|
||||
{col.boundStatus ? (
|
||||
<span className="bcol-bind">
|
||||
↔ {col.boundStatus === 'done' ? '完成' : col.boundStatus === 'doing' ? '执行中' : '未开始'}
|
||||
</span>
|
||||
) : (
|
||||
<span className="bcol-bind off">未绑定</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{drag.dragging && (
|
||||
<div
|
||||
className="drag-ghost"
|
||||
style={{ left: drag.dragging.x, top: drag.dragging.y }}
|
||||
>
|
||||
{drag.dragging.task.title}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<div className="bcol-body">
|
||||
{list.map((t) => (
|
||||
<TaskCard
|
||||
key={t.id}
|
||||
task={t}
|
||||
dragging={drag.dragTask?.id === t.id}
|
||||
bindDrag={drag.bind}
|
||||
onClick={() => { if (!drag.dragTask) onOpen?.(t) }}
|
||||
onToggleSub={onToggleSub}
|
||||
onToggle={onToggle}
|
||||
onMore={onMore}
|
||||
/>
|
||||
))}
|
||||
{list.length === 0 && <div className="bcol-empty">暂无任务</div>}
|
||||
<div className="badd" onClick={() => onQuickAdd?.(col.id)}>+ 添加任务</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user