'use client'; import { useState } from 'react'; import { X, Link2, ChevronRight, ArrowRightLeft } from 'lucide-react'; import { BugStatusBadge } from './BugStatusBadge'; import { useBugStore } from '@/stores/useBugStore'; import { useTestCaseStore } from '@/stores/useTestCaseStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useMemberStore } from '@/stores/useMemberStore'; import { useAuthStore } from '@/stores/useAuthStore'; import { BUG_ALLOWED_TRANSITIONS, BUG_STATUS_LABEL, BUG_SEVERITY_LABEL, BUG_SEVERITY_COLOR } from '@/lib/bug'; import { formatDateTime } from '@/lib/format'; import type { BugStatus } from '@/lib/bug'; const LOG_ACTION_LABEL: Record = { create: '创建', status_change: '状态变更', transfer: '转交', resolve: '修复', }; interface Props { bugId: string; onClose: () => void; contextLabel?: string; } export function BugDetailDrawer({ bugId, onClose, contextLabel }: Props) { const { bugs, changeStatus, transferBug } = useBugStore(); const { testCases } = useTestCaseStore(); const { requirements } = useRequirementStore(); const { members } = useMemberStore(); const user = useAuthStore((s) => s.user); const operator = user?.name || '系统'; const bug = bugs.find((b) => b.id === bugId); if (!bug) return null; const tc = testCases.find((c) => c.id === bug.testCaseId); const requirement = tc ? requirements.find((r) => r.id === tc.requirementId) : null; const nextStatuses = BUG_ALLOWED_TRANSITIONS[bug.status]; const [resolution, setResolution] = useState(bug.resolution || ''); const [showResolutionInput, setShowResolutionInput] = useState(false); const [showTransfer, setShowTransfer] = useState(false); const [transferTo, setTransferTo] = useState(''); const [transferRemark, setTransferRemark] = useState(''); const [lightboxSrc, setLightboxSrc] = useState(null); const handleTransition = (to: BugStatus) => { if (to === 'fixed') { setShowResolutionInput(true); return; } changeStatus(bug.id, to, operator); }; const confirmFix = () => { changeStatus(bug.id, 'fixed', operator, { resolution: resolution.trim() || undefined }); setShowResolutionInput(false); }; const handleTransfer = () => { if (!transferTo) return; transferBug(bug.id, transferTo, operator, transferRemark.trim() || undefined); setShowTransfer(false); setTransferTo(''); setTransferRemark(''); }; return (
e.stopPropagation()}> {contextLabel && (
{contextLabel}
)}
{bug.bugNo} {bug.title}
{/* 关联链路 */}
关联链路
{tc && (
{tc.caseNo} {tc.title}
)} {requirement && (
{requirement.code} {requirement.title}
)}
{/* 状态 & 操作 */}
状态 & 操作
{BUG_SEVERITY_LABEL[bug.severity]} {bug.priority}
{nextStatuses.length > 0 && !showResolutionInput && operator === bug.assigneeId && (
{nextStatuses.map((s) => ( ))} {bug.status !== 'closed' && bug.status !== 'rejected' && ( )}
)} {nextStatuses.length > 0 && !showResolutionInput && operator !== bug.assigneeId && (
当前修复人为 {bug.assigneeId},仅修复人可操作
)} {showResolutionInput && (
setResolution(e.target.value)} placeholder="修复说明" className="h-8 w-full rounded-lg border border-[var(--line)] px-3 text-[12px] focus:border-[var(--accent)] focus:outline-none" autoFocus />
)} {showTransfer && (
转交给:
setTransferRemark(e.target.value)} placeholder="转交备注(可选)" className="h-8 w-full rounded-lg border border-[var(--line)] px-3 text-[12px] focus:border-[var(--accent)] focus:outline-none" />
)}
{/* 基本信息 */}
基本信息
提交人:{bug.reportedBy}
修复人:{bug.assigneeId}
提交:{formatDateTime(bug.createdAt)}
{bug.resolvedAt &&
修复:{formatDateTime(bug.resolvedAt)}
} {bug.closedAt &&
关闭:{formatDateTime(bug.closedAt)}
}
{/* 描述 */}
Bug 描述

{bug.description}

{/* 截图 */} {bug.images && bug.images.length > 0 && (
截图附件
{bug.images.map((src, i) => ( {`截图${i setLightboxSrc(src)} /> ))}
)} {/* 修复说明 */} {bug.resolution && (
修复说明

{bug.resolution}

)} {/* 操作日志 */} {bug.logs && bug.logs.length > 0 && (
操作日志
{[...bug.logs].reverse().map((log) => (
{formatDateTime(log.createdAt)}
{log.operator} {LOG_ACTION_LABEL[log.action] || log.action} {log.fromValue && log.toValue && ( {log.fromValue} → {log.toValue} )} {log.remark && ({log.remark})}
))}
)}
{/* Lightbox */} {lightboxSrc && (
{ e.stopPropagation(); setLightboxSrc(null); }}> 大图预览 e.stopPropagation()} />
)}
); }