'use client'; import { useState, useMemo, useEffect } from 'react'; import { Plus, Code2, Trash2 } from 'lucide-react'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore'; import { useTaskWorklogStore } from '@/stores/useTaskWorklogStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useMemberStore } from '@/stores/useMemberStore'; import { useAuthStore } from '@/stores/useAuthStore'; import { DevTaskRow } from './DevTaskRow'; import { DevTaskCreateModal } from './DevTaskCreateModal'; import { DevTaskDetailDrawer } from './DevTaskDetailDrawer'; import { calcGroupProgress, DEV_TASK_STATUS_LABEL } from '@/lib/dev-task'; import { Pagination, usePagination } from '@/components/Pagination'; import type { DevTask, DevTaskStatus } from '@/lib/dev-task'; interface Props { versionId: string; requirementIds: string[]; versionDeadline?: string; } export function DevTaskTab({ versionId, requirementIds, versionDeadline }: Props) { const { tasks, fetchTasks, deleteTask } = useDevTaskStore(); const { categories, fetchCategories } = useTaskCategoryStore(); const { fetchWorklogs } = useTaskWorklogStore(); const { requirements } = useRequirementStore(); const { members } = useMemberStore(); const user = useAuthStore((s) => s.user); useEffect(() => { fetchTasks(); }, [fetchTasks]); useEffect(() => { fetchCategories(); }, [fetchCategories]); useEffect(() => { fetchWorklogs(); }, [fetchWorklogs]); const versionTasks = useMemo( () => tasks.filter((t) => requirementIds.includes(t.requirementId)), [tasks, requirementIds], ); // 筛选 const [filterAssignee, setFilterAssignee] = useState(''); const [filterStatus, setFilterStatus] = useState(''); const [filterBlocked, setFilterBlocked] = useState(''); const [filterCategory, setFilterCategory] = useState(''); const filteredTasks = useMemo(() => { let result = versionTasks; if (filterAssignee) result = result.filter((t) => t.assigneeId === filterAssignee); if (filterStatus) result = result.filter((t) => t.status === filterStatus); if (filterBlocked === 'yes') result = result.filter((t) => t.isBlocked); if (filterBlocked === 'no') result = result.filter((t) => !t.isBlocked); if (filterCategory) result = result.filter((t) => t.categoryId === filterCategory); return result; }, [versionTasks, filterAssignee, filterStatus, filterBlocked, filterCategory]); const { paged, page, setPage, total, pageSize, setPageSize } = usePagination(filteredTasks, 20); const progress = calcGroupProgress(versionTasks); const totalEstimate = versionTasks.reduce((s, t) => s + t.estimateHours, 0); const blockedCount = versionTasks.filter((t) => t.isBlocked).length; const [showCreate, setShowCreate] = useState(false); const [selectedTaskId, setSelectedTaskId] = useState(null); // 批量选择 const [selectedIds, setSelectedIds] = useState>(new Set()); const isAllSelected = paged.length > 0 && paged.every((t) => selectedIds.has(t.id)); const toggleSelect = (id: string) => { const next = new Set(selectedIds); if (next.has(id)) next.delete(id); else next.add(id); setSelectedIds(next); }; const toggleAll = () => { setSelectedIds(isAllSelected ? new Set() : new Set(paged.map((t) => t.id))); }; const handleBatchDelete = () => { if (selectedIds.size === 0) return; if (!confirm(`确定删除选中的 ${selectedIds.size} 个任务?`)) return; selectedIds.forEach((id) => deleteTask(id)); setSelectedIds(new Set()); }; const groupedByReq = useMemo(() => { const map = new Map(); for (const t of paged) { const list = map.get(t.requirementId) || []; list.push(t); map.set(t.requirementId, list); } return map; }, [paged]); const assignees = useMemo(() => Array.from(new Set(versionTasks.map((t) => t.assigneeId))), [versionTasks]); const hasFilter = !!(filterAssignee || filterStatus || filterBlocked || filterCategory); return (
{/* 工具栏:进度 + 筛选 + 操作 合为一行 */}
{/* 进度 */} {progress}%
{versionTasks.length}任务 · {totalEstimate}h {blockedCount > 0 && {blockedCount}阻塞} {/* 筛选 */} {hasFilter && } {/* 右侧操作 */}
{selectedIds.size > 0 && ( )}
{/* 列表 */} {filteredTasks.length === 0 ? (

{hasFilter ? '没有匹配的任务' : '暂无开发任务'}

{!hasFilter && }
) : ( Array.from(groupedByReq.entries()).map(([reqId, reqTasks]) => { const req = requirements.find((r) => r.id === reqId); const reqProgress = calcGroupProgress(reqTasks); return (
selectedIds.has(t.id))} onChange={() => { const ids = reqTasks.map((t) => t.id); const allSelected = ids.every((id) => selectedIds.has(id)); const next = new Set(selectedIds); if (allSelected) ids.forEach((id) => next.delete(id)); else ids.forEach((id) => next.add(id)); setSelectedIds(next); }} className="h-3.5 w-3.5 rounded border-[var(--line)]" /> {req?.code} {req?.title} {reqProgress}%
{reqTasks.map((t) => (
toggleSelect(t.id)} className="h-3.5 w-3.5 rounded border-[var(--line)]" onClick={(e) => e.stopPropagation()} />
c.id === t.categoryId)} onClick={() => setSelectedTaskId(t.id)} />
))}
); }) )} {total > 20 && } {showCreate && setShowCreate(false)} />} {selectedTaskId && t.id)} onClose={() => setSelectedTaskId(null)} />}
); }