Files
iboard/src/components/task/TaskRow.jsx
T
jif2.zhang 06e9a3e694 fix(任务): 编号改为 #序号展示、详情页完整编号可复制、修复详情改列报「看板列不存在」
- 列表/看板任务编号徽标改为 `#序号`(如 #18), 移除不再需要的 codePrefix 透传
- 详情页标题下新增完整任务编号胶囊 (项目编号-序号, 如 ADMIN-18), 点按复制:
  Clipboard API 优先, 非安全上下文/旧 WebView 回退 execCommand, 并提示已复制
- 修复详情页「看板列」切换报错: 0.2.1 本地库列 id 为 uuid 字符串,
  ActionSheet 的 key 恒为字符串, 旧代码 Number(key) 得到 NaN ->
  update 校验失败抛「看板列不存在」。改为字符串比对 (currentCol/勾选标记同步)
- 详情页保存后先就地替换该行再回源刷新
2026-10-27 10:06:12 +08:00

85 lines
3.4 KiB
React

import StatusDot from './StatusDot.jsx'
import Avatar from './Avatar.jsx'
import { kindColor } from '../../pages/task/kind.js'
import { taskShortId } from '../../pages/task/date.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'
// 编号只显示 `#序号`; 完整编号 (项目编号-序号) 放详情页 (见 TaskDetail)
const shortId = indent ? null : taskShortId(task)
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">
{shortId && <span className="num-tag">{shortId}</span>}
{task.markDate && (
<span className="date-tag">{task.markDate.slice(5, 10).replace('-', '/')}</span>
)}
{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>
<Avatar name={task.assignee?.name} size={20} />
</div>
</>
)}
</div>
{children}
</>
)
}