Docker Build and Push / build-image (push) Successful in 1m3s
- 打卡与列表: 去掉卡片整体长按拖动路径, 只有 ⠿ 把手可拖; 把手缩小 - 列表行去掉跟动的蓝色目标列徽标 (drag-pill), 占位/克隆浮层承担全部反馈 - 打卡支持跨组拖动: 落到其他周期组的目标卡 = 改周期 + 落位一次完成 (updateHabit period + reorder), 组内排序不变 - 看板: 去掉 ⠿ 把手, 恢复长按拖动
80 lines
3.0 KiB
React
80 lines
3.0 KiB
React
import { useState } from 'react'
|
|
import TaskRow from '../../components/task/TaskRow.jsx'
|
|
import useRowDrag from '../../components/task/useRowDrag.js'
|
|
|
|
// 列表视图 (T-VW-01, T-ST-04/05)
|
|
// 按看板列分组展示; 子任务缩进 + 折叠展开
|
|
// 换列: 行/把手拖动, 跟手浮层, 手指划到哪个分组松手就落到哪 (与看板一致)
|
|
// (列表只展示有任务的列, 空列需到看板视图拖入)
|
|
export default function ListView({
|
|
tasks, columns, onToggle, onOpen, onMore, onMoveColumn, onReorder,
|
|
}) {
|
|
const [collapsed, setCollapsed] = useState({})
|
|
const drag = useRowDrag({ columns, onDrop: onMoveColumn, onReorder })
|
|
|
|
if (!tasks || tasks.length === 0) {
|
|
return (
|
|
<div className="empty" style={{ paddingTop: 90 }}>
|
|
<div className="ico">✓</div>
|
|
<p>这个项目还没有任务</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// 分组: 按列顺序, 未匹配列的任务归到一起
|
|
const groups = []
|
|
const orderedCols = [...(columns || [])].sort((a, b) => a.sortOrder - b.sortOrder)
|
|
for (const c of orderedCols) {
|
|
const list = tasks.filter((t) => t.columnId === c.id)
|
|
if (list.length) groups.push({ key: c.id, name: c.name, list })
|
|
}
|
|
const known = new Set(orderedCols.map((c) => c.id))
|
|
const orphans = tasks.filter((t) => !known.has(t.columnId))
|
|
if (orphans.length) groups.push({ key: 'other', name: '其他', list: orphans })
|
|
|
|
return (
|
|
<div className="list-body">
|
|
{groups.map((g) => (
|
|
<div key={g.key} data-column-id={g.key === 'other' ? undefined : g.key}>
|
|
<div className="group-title">
|
|
{g.name}
|
|
<span className="cnt">{g.list.length}</span>
|
|
</div>
|
|
|
|
{g.list.map((t) => {
|
|
const kids = t.children || []
|
|
const isCollapsed = !!collapsed[t.id]
|
|
const dragging = drag.drag?.task?.id === t.id ? drag.drag : null
|
|
return (
|
|
<div key={t.id}>
|
|
<TaskRow
|
|
task={t}
|
|
onToggle={onToggle}
|
|
onClick={() => { if (!drag.justEnded()) onOpen(t) }}
|
|
onMore={onMore}
|
|
bindDragHandle={drag.bindHandle}
|
|
dragging={!!dragging}
|
|
dragOver={drag.over?.id === t.id ? drag.over : null}
|
|
/>
|
|
{kids.length > 0 && (
|
|
<>
|
|
{!isCollapsed && kids.map((c) => (
|
|
<TaskRow key={c.id} task={c} indent onToggle={onToggle} onClick={() => onOpen(c)} onMore={onMore} />
|
|
))}
|
|
<div
|
|
className="collapse"
|
|
onClick={() => setCollapsed((p) => ({ ...p, [t.id]: !p[t.id] }))}
|
|
>
|
|
{isCollapsed ? `展开 ${kids.length} 个子任务 ⌄` : `收起 ${kids.length} 个子任务 ⌃`}
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|