Files
iboard/src/components/task/TaskRow.jsx
T
2026-10-25 02:59:52 +08:00

79 lines
3.1 KiB
React

import StatusDot from './StatusDot.jsx'
import { kindColor } from '../../pages/task/kind.js'
const statusLabel = (s) => (s === 'done' ? '完成' : s === 'doing' ? '执行中' : '未开始')
// 列表视图的一行 (Todoist 风格, T-UI-01)
// 点击行体 → 打开详情; 长按 400ms 后左右拖动换列 (bindDrag 来自 useRowDrag)
// 子任务与普通任务同构: 标题一行 + 状态元信息一行 (字体略小), 不显示分组标签
// 复选框 / ⋯ 是独立控件: pointerdown 不冒泡, 不发起拖动,
// 避免长按行后抬手时合成的 click 落在控件上误触状态切换
export default function TaskRow({
task, onToggle, onClick, onMore, bindDrag, bindDragHandle, dragging = false, dragOver, indent = false, children,
}) {
const done = task.status === 'done'
return (
<>
<div
className={
`${indent ? 'sub-row' : 'task-row'}${done ? ' done' : ''}`
+ (dragging ? ' dragging' : '')
+ (dragOver ? ` dragover ${dragOver.after ? 'after' : 'before'}` : '')
}
data-task-id={indent ? undefined : task.id}
onClick={onClick}
{...(!indent && bindDrag ? bindDrag(task) : {})}
>
{!indent && bindDragHandle && (
<span className="grip" title="拖动排序 / 换列" {...bindDragHandle(task)}>⠿</span>
)}
<div
className="box"
onClick={(e) => { e.stopPropagation(); onToggle?.(task) }}
onPointerDown={(e) => e.stopPropagation()}
>
<svg viewBox="0 0 14 14" width={indent ? 10 : 11} height={indent ? 10 : 11}>
<path d="M2.5 7.2l3 3 6-6.4" stroke="#fff" strokeWidth="2" fill="none" strokeLinecap="round" />
</svg>
</div>
{indent ? (
<div className="sub-main">
<span className="sub-title">{task.title}</span>
<div className="task-meta sub">
<StatusDot status={task.status} />
<span>{statusLabel(task.status)}</span>
</div>
</div>
) : (
<>
<div className="task-main">
<div className="task-title">{task.title}</div>
{/* 无类型/成员也保留该行, 与有标签的行等高 */}
<div className="task-meta">
{task.kind && (
<span className="kind-tag" style={{ background: kindColor(task.kind) }}>{task.kind}</span>
)}
{task.members?.length > 0 && (
<span className="members">+ {task.members.map((m) => m.name).join('、')}</span>
)}
</div>
</div>
<div className="task-right">
<span
className="more"
onClick={(e) => { e.stopPropagation(); onMore?.(task) }}
onPointerDown={(e) => e.stopPropagation()}
>⋯</span>
<span className={`assignee${task.assignee ? '' : ' none'}`}>
{task.assignee?.name || '未指派'}
</span>
</div>
</>
)}
</div>
{children}
</>
)
}