'use client' import Image from 'next/image' import { useEffect, useMemo, useState } from 'react' import type { WorkReviewProject, WorkReviewReview, WorkReviewRole, WorkReviewVersion, } from '@/types/work-review' import { WorkReviewImageGallery } from './WorkReviewImageGallery' import { WorkReviewDecisionBadge, WorkReviewStatusBadge } from './WorkReviewStatusBadge' function formatDateOnly(value: string) { const raw = String(value || '') const datePart = raw.match(/^\d{4}-\d{2}-\d{2}/)?.[0] return datePart || '-' } function formatDateTime(value?: string | null) { if (!value) return '-' const date = new Date(value) if (Number.isNaN(date.getTime())) return '-' return new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false, }).format(date) } function proxyImageUrl(value: string) { if (!/^https?:\/\//i.test(value)) return value try { const parsed = new URL(value) if (['s3.jiansun.vip', 's3.yydd.ai', 's3.1080p.hk'].includes(parsed.hostname.toLowerCase())) { return `/api/proxy-media?url=${encodeURIComponent(value)}` } } catch { // 使用原地址。 } return value } function getVersionReview(version: WorkReviewVersion, role: WorkReviewRole) { return version.reviews.find((review) => review.reviewerRole === role) || null } function ReviewProgress({ project, version, }: { project: WorkReviewProject version: WorkReviewVersion }) { const roles: Array<{ role: WorkReviewRole; label: string; name: string }> = [ { role: 'manager', label: '主管', name: project.manager.name }, { role: 'final_reviewer', label: '元泽', name: project.finalReviewer.name }, ] return (
{roles.map((item) => { const review = getVersionReview(version, item.role) return ( {item.label}{review ? '已评' : '待评'} ) })}
) } export function WorkReviewProjectCard({ project, expanded, onToggle, onResubmit, onReview, }: { project: WorkReviewProject expanded: boolean onToggle: () => void onResubmit: () => void onReview: (version: WorkReviewVersion, role: WorkReviewRole) => void }) { const latestVersion = project.versions[0] const [selectedVersionId, setSelectedVersionId] = useState(latestVersion?.id || '') useEffect(() => { if (!project.versions.some((version) => version.id === selectedVersionId)) { setSelectedVersionId(project.versions[0]?.id || '') } }, [project.versions, selectedVersionId]) const selectedVersion = useMemo( () => project.versions.find((version) => version.id === selectedVersionId) || latestVersion, [latestVersion, project.versions, selectedVersionId], ) const cover = latestVersion?.imageGroups?.[0]?.previewUrl || latestVersion?.imageUrls?.[0] || '' const isLatestSelected = selectedVersion?.id === latestVersion?.id if (!latestVersion || !selectedVersion) return null return (
{expanded && (

V{selectedVersion.version} 作品内容

{!isLatestSelected && 历史版本 · 只读}

提交于 {formatDateTime(selectedVersion.submittedAt)} {selectedVersion.completedAt ? ` · 评审完成于 ${formatDateTime(selectedVersion.completedAt)}` : ''}

{isLatestSelected && project.permissions.canResubmit && ( )}
{selectedVersion.submissionNote && (
本次提交说明

{selectedVersion.submissionNote}

)}

双评审意见

onReview(selectedVersion, 'manager')} /> onReview(selectedVersion, 'final_reviewer')} />
)}
) } function ReviewPanel({ label, reviewerName, review, canReview, onReview, }: { label: string reviewerName: string review: WorkReviewReview | null canReview: boolean onReview: () => void }) { return (

{label}

{review?.reviewerName || reviewerName}
{review ? ( ) : ( 待评审 )}
{review ? (
{(review.imageComments || []).map((item) => (
图 {item.imageIndex + 1}{item.sourceName ? ` · ${item.sourceName}` : ''}

{item.comment}

))} {review.generalComment && (
总体意见

{review.generalComment}

)}
提交于 {formatDateTime(review.reviewedAt)}
) : (

{canReview ? '当前版本等待你填写逐图评审建议' : '等待评审人提交'}

{canReview && ( )}
)}
) }