'use client'; import { useEffect, useMemo, useState } from 'react'; import { useRouter } from 'next/navigation'; import { Search, FileText, Palette, ClipboardList, Code2, ClipboardCheck, Bug as BugIcon, ExternalLink, CheckCircle2 } from 'lucide-react'; import { useProductStore } from '@/stores/useProductStore'; import { useVersionPlanStore } from '@/stores/useVersionPlanStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useTestCaseStore } from '@/stores/useTestCaseStore'; import { useBugStore } from '@/stores/useBugStore'; import { useAuthStore } from '@/stores/useAuthStore'; import { flattenVersions } from '@/lib/derive'; import { aggregateWorkItems, WORK_ITEM_TYPE_LABEL } from '@/lib/workspace-engine'; import type { WorkItem, WorkItemType } from '@/lib/workspace-engine'; import { DEV_TASK_STATUS_LABEL, DEV_TASK_STATUS_COLOR } from '@/lib/dev-task'; import { TEST_CASE_STATUS_LABEL, TEST_CASE_STATUS_COLOR } from '@/lib/test-case'; import { BUG_STATUS_LABEL, BUG_STATUS_COLOR, BUG_SEVERITY_LABEL, BUG_SEVERITY_COLOR } from '@/lib/bug'; type TabKey = 'all' | 'plan_research' | 'plan_product' | 'plan_ui' | 'devTask' | 'testCase' | 'bug'; const TABS: { key: TabKey; label: string; icon: any }[] = [ { key: 'all', label: '全部', icon: ClipboardList }, { key: 'plan_research', label: '调研', icon: Search }, { key: 'plan_product', label: '产品方案', icon: FileText }, { key: 'plan_ui', label: 'UI设计', icon: Palette }, { key: 'devTask', label: '开发任务', icon: Code2 }, { key: 'testCase', label: '测试用例', icon: ClipboardCheck }, { key: 'bug', label: 'Bug', icon: BugIcon }, ]; const PLAN_STATUS_STYLE: Record = { pending: 'bg-zinc-100 text-zinc-600', in_progress: 'bg-blue-50 text-blue-600', completed: 'bg-emerald-50 text-emerald-600', }; const PLAN_STATUS_LABEL: Record = { pending: '未开始', in_progress: '进行中', completed: '已完成' }; export default function WorkspacePage() { const router = useRouter(); const { overview, fetchOverview } = useProductStore(); const { plans, fetchPlans } = useVersionPlanStore(); const { requirements, fetchRequirements } = useRequirementStore(); const { tasks: devTasks, fetchTasks } = useDevTaskStore(); const { testCases, fetchTestCases } = useTestCaseStore(); const { bugs, fetchBugs } = useBugStore(); const user = useAuthStore((s) => s.user); const [activeTab, setActiveTab] = useState('all'); const [showCompleted, setShowCompleted] = useState(true); useEffect(() => { fetchOverview(); }, [fetchOverview]); useEffect(() => { fetchPlans(); }, [fetchPlans]); useEffect(() => { fetchRequirements(); }, [fetchRequirements]); useEffect(() => { fetchTasks(); }, [fetchTasks]); useEffect(() => { fetchTestCases(); }, [fetchTestCases]); useEffect(() => { fetchBugs(); }, [fetchBugs]); const userName = user?.name ?? ''; const allVersions = useMemo(() => flattenVersions(overview), [overview]); const versionMap = useMemo(() => { const map = new Map(); allVersions.forEach((v) => map.set(v.id, { id: v.id, name: v.name, productName: v.productName, projectName: v.projectName })); return map; }, [allVersions]); const requirementVersionMap = useMemo(() => { const map = new Map(); requirements.forEach((r) => { if (r.versionId) map.set(r.id, r.versionId); }); return map; }, [requirements]); const workItems = useMemo(() => aggregateWorkItems(userName, plans, devTasks, testCases, bugs, versionMap, requirementVersionMap), [userName, plans, devTasks, testCases, bugs, versionMap, requirementVersionMap] ); const filteredItems = useMemo(() => { let items = activeTab === 'all' ? workItems : workItems.filter((i) => i.type === activeTab); if (!showCompleted) items = items.filter((i) => !i.completed); return items.sort((a, b) => { if (a.completed !== b.completed) return a.completed ? 1 : -1; return 0; }); }, [workItems, activeTab, showCompleted]); const counts = useMemo(() => ({ all: workItems.length, plan_research: workItems.filter((i) => i.type === 'plan_research').length, plan_product: workItems.filter((i) => i.type === 'plan_product').length, plan_ui: workItems.filter((i) => i.type === 'plan_ui').length, devTask: workItems.filter((i) => i.type === 'devTask').length, testCase: workItems.filter((i) => i.type === 'testCase').length, bug: workItems.filter((i) => i.type === 'bug').length, }), [workItems]); const pendingCount = workItems.filter((i) => !i.completed).length; const completedCount = workItems.filter((i) => i.completed).length; return (

与我相关

待办 {pendingCount} 已完成 {completedCount}

{TABS.find((t) => t.key === activeTab)?.label}

{filteredItems.length} 项
{filteredItems.length === 0 ? (

暂无相关任务

) : (
{filteredItems.map((item) => ( item.versionId && router.push(`/versions/${item.versionId}`)} /> ))}
)}
); } function WorkItemCard({ item, onNavigate }: { item: WorkItem; onNavigate: () => void }) { const statusBadge = getStatusBadge(item); return (
{/* 完成标记 */} {item.completed && } {/* 类型标签 */} {WORK_ITEM_TYPE_LABEL[item.type]} {/* 状态 */} {statusBadge} {/* 编号 */} {item.extra?.taskNo && {item.extra.taskNo}} {item.extra?.caseNo && {item.extra.caseNo}} {item.extra?.bugNo && {item.extra.bugNo}} {/* 标题 */} {item.title} {/* 优先级 */} {item.priority && {item.priority}} {/* Bug 严重程度 */} {item.extra?.severity && ( {BUG_SEVERITY_LABEL[item.extra.severity as keyof typeof BUG_SEVERITY_LABEL] || ''} )} {/* 跳转 */}
{/* 上下文:产品 / 项目 / 版本 */}
{item.productName} / {item.projectName} / {item.extra?.dueDate && 截止 {item.extra.dueDate}} {item.extra?.startTime && {item.extra.startTime.slice(0, 16).replace('T', ' ')} → {item.extra.endTime?.slice(0, 16).replace('T', ' ')}}
); } function getStatusBadge(item: WorkItem) { if (item.type === 'devTask') { return ( {DEV_TASK_STATUS_LABEL[item.status as keyof typeof DEV_TASK_STATUS_LABEL] || item.status} ); } if (item.type === 'testCase') { return ( {TEST_CASE_STATUS_LABEL[item.status as keyof typeof TEST_CASE_STATUS_LABEL] || item.status} ); } if (item.type === 'bug') { return ( {BUG_STATUS_LABEL[item.status as keyof typeof BUG_STATUS_LABEL] || item.status} ); } return ( {PLAN_STATUS_LABEL[item.status] || item.status} ); }