'use client'; import { useState } from 'react'; import { X, Check, Link2, FileUp, ExternalLink, Play, ArrowRightLeft } from 'lucide-react'; import { useVersionPlanStore } from '@/stores/useVersionPlanStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useMemberStore } from '@/stores/useMemberStore'; import { calcPlanProgress, calcLinkedReqProgress } from '@/lib/version-plan'; import type { PlanTask, VersionPlan } from '@/lib/version-plan'; interface Props { planId: string; onClose: () => void; } const 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 STATUS_LABEL: Record = { pending: '未开始', in_progress: '进行中', completed: '已完成' }; const TYPE_LABEL: Record = { research: '调研', product: '产品方案', ui: 'UI设计' }; export function PlanDetailDrawer({ planId, onClose }: Props) { const { plans, updatePlan, completePlan } = useVersionPlanStore(); const { requirements } = useRequirementStore(); const { members } = useMemberStore(); const [showTransfer, setShowTransfer] = useState(false); const [transferTo, setTransferTo] = useState(''); const [resultType, setResultType] = useState<'link' | 'file'>('link'); const [resultUrl, setResultUrl] = useState(''); const [fileName, setFileName] = useState(''); const [fileData, setFileData] = useState(''); const [showComplete, setShowComplete] = useState(false); const plan = plans.find((p) => p.id === planId); if (!plan) return null; const isResearch = plan.type === 'research'; const progress = isResearch ? calcPlanProgress(plan.tasks) : calcLinkedReqProgress(plan.linkedRequirementIds, plan.completedRequirementIds); const linkedReqs = (plan.linkedRequirementIds || []).map((id) => requirements.find((r) => r.id === id)).filter(Boolean) as { id: string; code: string; title: string }[]; const canInteract = plan.status === 'in_progress' || (plan.status === 'pending' && plan.startTime && new Date(plan.startTime) <= new Date()); const handleToggleTask = (task: PlanTask) => { if (!canInteract) return; const nextStatus = task.status === 'completed' ? 'pending' : 'completed'; const updatedTasks = (plan.tasks || []).map((t) => t.id === task.id ? { ...t, status: nextStatus as PlanTask['status'] } : t); updatePlan(plan.id, { tasks: updatedTasks }); }; const handleToggleReq = (reqId: string) => { if (!canInteract) return; const current = plan.completedRequirementIds || []; const next = current.includes(reqId) ? current.filter((id) => id !== reqId) : [...current, reqId]; updatePlan(plan.id, { completedRequirementIds: next }); }; const handleFile = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; setFileName(file.name); const reader = new FileReader(); reader.onload = () => setFileData(reader.result as string); reader.readAsDataURL(file); }; const handleSubmitResult = () => { const url = resultType === 'link' ? resultUrl.trim() : fileData; if (!url) return; completePlan(plan.id, { resultType, resultUrl: url, resultFileName: fileName || undefined, resultFileData: resultType === 'file' ? fileData : undefined }); setShowComplete(false); }; const handleTransfer = () => { if (!transferTo) return; updatePlan(plan.id, { owner: transferTo }); setShowTransfer(false); setTransferTo(''); }; return (
e.stopPropagation()}> {/* Header */}
{TYPE_LABEL[plan.type]} {STATUS_LABEL[plan.status]}
{/* Body */}

{plan.title}

{/* Info */}
负责人
{plan.owner}
进度
{progress}%
计划时间
{plan.startTime.slice(0, 16).replace('T', ' ')} → {plan.endTime.slice(0, 16).replace('T', ' ')}
{plan.actualStartAt && (
实际开始
{plan.actualStartAt.slice(0, 16).replace('T', ' ')}
)} {plan.completedAt && (
完成时间
{plan.completedAt.slice(0, 16).replace('T', ' ')}
)}
{/* Progress Bar */}
{/* Research Tasks */} {isResearch && plan.tasks && plan.tasks.length > 0 && (
任务清单
{plan.tasks.map((task) => (
{task.title}
))}
)} {/* Product/UI: Linked Requirements */} {!isResearch && linkedReqs.length > 0 && (
关联需求
{linkedReqs.map((req) => { const isDone = (plan.completedRequirementIds || []).includes(req.id); return (
{req.code} {req.title}
); })}
)} {/* Result */} {plan.status === 'completed' && plan.resultUrl && (
成果
{plan.resultType === 'link' ? : } {plan.resultFileName || '查看成果'}
)} {plan.remark && (
备注
{plan.remark}
)} {/* Transfer Section */} {showTransfer && (
转交给
)} {/* Complete with result */} {showComplete && (
提交成果
{resultType === 'link' ? ( setResultUrl(e.target.value)} placeholder="https://..." className="h-8 w-full rounded-lg border border-[var(--line)] px-2 text-[12px] focus:border-[var(--accent)] focus:outline-none" /> ) : (
{fileName &&

{fileName}

}
)}
)}
{/* Footer Actions */} {plan.status !== 'completed' && (
{plan.status === 'pending' && ( )} {plan.status === 'in_progress' && ( )}
)}
); }