- 数据: Project.autoArchive + Task.archived/archivedAt/archiveSource, 迁移 20261026000000_task_archive (两索引) - 领域: server/task-domain/archive.js —— hiddenAtOf 与保留口径精确同源, autoArchiveDue 到期判定, runAutoArchiveForProject (读时惰性) + runAutoArchiveSweep (每小时 cron 兜底) - 接口: POST /tasks/:id/archive|unarchive (连带子任务, 幂等); GET /projects/:id/tasks/archived (keyword/kind/columnId/from/to 过滤, archivedAt+id 游标分页); 项目 PUT 收 autoArchive (枚举校验); 主列表/子任务/项目 taskCount 全部排除归档 - 前端: ⋯ 菜单「归档」(确认含子任务条数); 项目栏「已归档」入口 → ArchivedTasks 弹层 (关键词/类型/列/归档时段过滤 + 加载更多 + 恢复/删除); 项目管理「归档设置」四档 Picker
This commit is contained in:
@@ -9,6 +9,7 @@ import BoardView from './BoardView.jsx'
|
||||
import TaskDetail from './TaskDetail.jsx'
|
||||
import ProjectManage from './ProjectManage.jsx'
|
||||
import CopyTaskPopup from '../../components/task/CopyTaskPopup.jsx'
|
||||
import ArchivedTasks from './ArchivedTasks.jsx'
|
||||
|
||||
// 项目视图容器: 列表 / 看板 切换 + 数据装载 + 乐观更新
|
||||
// 嵌在任务首屏下部 (T-VW-09); 头部仅保留「项目管理」与「+ 新增」(T-PJ-11)
|
||||
@@ -23,6 +24,7 @@ export default function ProjectView({ projectId, onProjectChanged, onProjectExit
|
||||
const [adding, setAdding] = useState(false)
|
||||
const [tick, setTick] = useState(0) // 保留窗口重算触发
|
||||
const [copyTask, setCopyTask] = useState(null) // ⋯菜单触发的跨项目拷贝
|
||||
const [archivedOpen, setArchivedOpen] = useState(false) // 已归档任务查询
|
||||
|
||||
const load = useCallback(() => {
|
||||
setErr('')
|
||||
@@ -169,6 +171,7 @@ export default function ProjectView({ projectId, onProjectChanged, onProjectExit
|
||||
const handler = ActionSheet.show({
|
||||
actions: [
|
||||
{ text: '编辑', key: 'edit' },
|
||||
{ text: '归档', key: 'archive' },
|
||||
{ text: '拷贝到其他项目', key: 'copy' },
|
||||
{ text: '删除', key: 'delete', bold: true, danger: true },
|
||||
],
|
||||
@@ -176,6 +179,21 @@ export default function ProjectView({ projectId, onProjectChanged, onProjectExit
|
||||
onAction: async (item) => {
|
||||
handler.close?.()
|
||||
if (item.key === 'edit') setOpenTask(task)
|
||||
else if (item.key === 'archive') {
|
||||
const subs = task.children?.length || 0
|
||||
const ok = await Dialog.confirm({
|
||||
content: subs ? `归档「${task.title}」?将连带归档 ${subs} 条子任务。` : `归档「${task.title}」?`,
|
||||
confirmText: '归档',
|
||||
})
|
||||
if (!ok) return
|
||||
try {
|
||||
await taskApi.archive(task.id)
|
||||
Toast.show({ content: '已归档' })
|
||||
load()
|
||||
} catch (e) {
|
||||
Toast.show({ content: e.message })
|
||||
}
|
||||
}
|
||||
else if (item.key === 'copy') setCopyTask(task)
|
||||
else if (item.key === 'delete') {
|
||||
const kids = task.children?.length || 0
|
||||
@@ -225,6 +243,7 @@ export default function ProjectView({ projectId, onProjectChanged, onProjectExit
|
||||
<div className="navbar-sub task-projbar">
|
||||
<span className="title">{project.name}</span>
|
||||
<div className="right">
|
||||
<span className="mini-btn" onClick={() => setArchivedOpen(true)}>已归档</span>
|
||||
<span className="mini-btn" onClick={() => setShowManage(true)}>项目管理</span>
|
||||
<span className="mini-btn solid" onClick={() => setAdding((v) => !v)}>+ 新增</span>
|
||||
</div>
|
||||
@@ -301,6 +320,15 @@ export default function ProjectView({ projectId, onProjectChanged, onProjectExit
|
||||
/>
|
||||
)}
|
||||
|
||||
{archivedOpen && (
|
||||
<ArchivedTasks
|
||||
project={project}
|
||||
visible={archivedOpen}
|
||||
onClose={() => setArchivedOpen(false)}
|
||||
onChanged={load}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showManage && (
|
||||
<ProjectManage
|
||||
project={project}
|
||||
|
||||
Reference in New Issue
Block a user