Files
iboard/src/components/task/TaskCard.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.0 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'
// 看板卡片 (T-UI-02): 轻量化
// - 标题 + 状态点 (T-ST-09)
// - 负责人 12px/600 突出 (T-AS-01); 任务成员 10.5px 弱化置于下方 (T-AS-02)
// - 子任务内嵌简洁展示 + 可直接勾选 (T-ST-06/07)
const MAX_SUB = 3
export default function TaskCard({
task, bindDrag, onClick, onToggleSub, onToggle, onMore, dragging = false, highlight = false,
}) {
const children = task.children || []
const shown = children.slice(0, MAX_SUB)
const rest = children.length - shown.length
// 编号只显示 `#序号`; 完整编号 (项目编号-序号) 放详情页 (见 TaskDetail)
const shortId = taskShortId(task)
return (
<div
className={`bcard${dragging ? ' dragging' : ''}${highlight ? ' hl' : ''}`}
data-task-id={task.id}
onClick={onClick}
{...(bindDrag ? bindDrag(task) : {})}
>
{/* 左侧竖排: 状态点 + ⋯ */}
<div className="bcard-left">
<span
className="bstat"
onClick={(e) => { e.stopPropagation(); onToggle?.(task) }}
onPointerDown={(e) => e.stopPropagation()}
title="点按切换完成"
>
<StatusDot status={task.status} />
</span>
{onMore && (
<span
className="bmore-btn"
onClick={(e) => { e.stopPropagation(); onMore(task) }}
onPointerDown={(e) => e.stopPropagation()}
>⋯</span>
)}
</div>
<div className="bcard-body">
<div className="brow1">
<span className="btitle">{task.title}</span>
</div>
<div className="bmeta">
{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>
)}
<span style={{ display: 'flex', alignItems: 'center', gap: 6, marginLeft: 'auto', minWidth: 0, justifyContent: 'flex-end' }}>
<Avatar name={task.assignee?.name} size={18} />
{task.members?.length > 0 && (
<span className="bmembers">+ {task.members.map((m) => m.name).join('、')}</span>
)}
</span>
</div>
{shown.map((c) => (
<div
key={c.id}
className={`bsub${c.status === 'done' ? ' done' : ''}`}
onClick={(e) => { e.stopPropagation(); onToggleSub?.(c) }}
onPointerDown={(e) => e.stopPropagation()}
>
<span className="sbox" />
<span className="stitle">{c.title}</span>
<StatusDot status={c.status} />
</div>
))}
{rest > 0 && <div className="bmore">+{rest} 个子任务</div>}
</div>
</div>
)
}