feat(V0.2): 任务模块 —— 项目 / 列表与看板 / 列-状态绑定 / 一级子任务
Docker Build and Push / build-image (push) Failing after 3m51s
Docker Build and Push / build-image (push) Failing after 3m51s
需求 142 条 (T-*), 设计见 docs/V0.2/任务模块/ 数据层: - 新增 6 张表: Project / ProjectMember / BoardColumn / Task / TaskMember / TaskTag - 看板列是实体(可增删改排序), Task.columnId 外键 RESTRICT 防误删列丢任务 - Task.status(todo/doing/done) 与 columnId 双落库, 二者正交 领域层 server/task-domain/ (唯一口径, 前后端不重复实现): - resolveStatus: 列决定状态(只看目标列, 无拖出回退); 状态与列不匹配时按列序取第一个绑定该状态的列自动归位, 无匹配列则不移动; 子任务完全豁免 - retention: 逐列 6 档保留窗口(永不显示/当天/3天/本周/本月/永久显示), 以 doneAt 判定 - copyTask: 跨项目深拷贝, 人员按目标项目成员过滤并回报丢弃名单 接口: /api/projects, /api/columns, /api/tasks (均校验项目成员) 前端: - 底部 Tab 插入「任务」, 位于「打卡」之后; 默认首页仍为打卡 - 列表视图(子任务缩进+折叠) / 看板视图(长按拖拽换列、拖到卡片降为子任务) - 列设置(列管理+状态绑定+保留选项) / 任务详情(扩展信息卡片默认收起) / 成员管理 - 视图记忆与上次项目存设备端 已验证: 95 文件语法 0 错误、相对导入 0 缺失、领域逻辑用例全部通过
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import StatusDot from './StatusDot.jsx'
|
||||
|
||||
// 看板卡片 (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, onAddSub, dragging = false, highlight = false,
|
||||
}) {
|
||||
const children = task.children || []
|
||||
const shown = children.slice(0, MAX_SUB)
|
||||
const rest = children.length - shown.length
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`bcard${dragging ? ' dragging' : ''}${highlight ? ' hl' : ''}`}
|
||||
data-task-id={task.id}
|
||||
onClick={onClick}
|
||||
{...(bindDrag ? bindDrag(task) : {})}
|
||||
>
|
||||
<div className="brow1">
|
||||
<span className="btitle">{task.title}</span>
|
||||
<span
|
||||
className="bstat"
|
||||
onClick={(e) => { e.stopPropagation(); onToggle?.(task) }}
|
||||
title="点按切换完成"
|
||||
>
|
||||
<StatusDot status={task.status} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="bmeta">
|
||||
<span className={`bassignee${task.assignee ? '' : ' none'}`}>
|
||||
{task.assignee?.name || '未指派'}
|
||||
</span>
|
||||
{task.members?.length > 0 && (
|
||||
<span className="bmembers">+ {task.members.map((m) => m.name).join('、')}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{shown.map((c) => (
|
||||
<div
|
||||
key={c.id}
|
||||
className={`bsub${c.status === 'done' ? ' done' : ''}`}
|
||||
onClick={(e) => { e.stopPropagation(); onToggleSub?.(c) }}
|
||||
>
|
||||
<span className="sbox" />
|
||||
<span className="stitle">{c.title}</span>
|
||||
<StatusDot status={c.status} />
|
||||
</div>
|
||||
))}
|
||||
{rest > 0 && <div className="bmore">+{rest} 个子任务</div>}
|
||||
|
||||
{onAddSub && (
|
||||
<div
|
||||
className="bsub-add"
|
||||
onClick={(e) => { e.stopPropagation(); onAddSub(task) }}
|
||||
>
|
||||
+ 子任务
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user