From 04fd25b60e87af2e580822f7736f8a59f8985a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E6=9E=97=E9=93=96?= Date: Sat, 1 Aug 2026 16:16:20 +0800 Subject: [PATCH] feat: reposition draft review annotations --- .../WorkReviewAnnotationMarker.tsx | 9 +- .../WorkReviewReviewerWorkspace.tsx | 202 ++++++++++++++---- .../work-review-reviewer-workspace.test.tsx | 41 +++- 3 files changed, 201 insertions(+), 51 deletions(-) diff --git a/components/work-review/WorkReviewAnnotationMarker.tsx b/components/work-review/WorkReviewAnnotationMarker.tsx index e5dbc95..50d7228 100644 --- a/components/work-review/WorkReviewAnnotationMarker.tsx +++ b/components/work-review/WorkReviewAnnotationMarker.tsx @@ -8,6 +8,7 @@ export type WorkReviewDisplayAnnotation = { reviewerName?: string reviewerRole?: WorkReviewRole draft?: boolean + positionEditable?: boolean } function markerTone(item: WorkReviewDisplayAnnotation) { @@ -33,7 +34,8 @@ export function WorkReviewAnnotationMarker({ ? '元泽' : '评审人' const summary = annotation.comment.trim() || '待填写修改意见' - const label = `${roleLabel}标注 ${item.markerNumber}${item.reviewerName ? `,${item.reviewerName}` : ''}:${summary}` + const positionState = item.positionEditable ? ',位置可调整' : '' + const label = `${roleLabel}标注 ${item.markerNumber}${positionState}${item.reviewerName ? `,${item.reviewerName}` : ''}:${summary}` const visualTitle = showComment ? label : `${roleLabel}标注 ${item.markerNumber}` return ( @@ -44,12 +46,13 @@ export function WorkReviewAnnotationMarker({ onClick?.() }} onPointerDown={(event) => event.stopPropagation()} + onPointerUp={(event) => event.stopPropagation()} aria-label={label} title={visualTitle} - className="group absolute z-20 h-11 w-11 -translate-x-1/2 -translate-y-1/2 outline-none" + className={`group absolute z-20 h-11 w-11 -translate-x-1/2 -translate-y-1/2 outline-none ${item.positionEditable ? 'cursor-move' : 'cursor-pointer'}`} style={{ left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%` }} > - + {item.markerNumber} annotationCount?: number showAnnotationComments?: boolean onToggleAnnotationMode?: () => void onToggleAnnotationComments?: () => void onAddAnnotation?: (x: number, y: number) => void + onMoveAnnotation?: (x: number, y: number) => void onSelectDraftAnnotation?: (annotationId: string) => void }) { const displayGroups = useMemo( @@ -121,6 +127,7 @@ function LongImagePane({ Math.max(0, displayGroups.length - 1), ) const activeGroup = displayGroups[activeIndex] + const placementMode = annotationMode || annotationRepositionMode useEffect(() => { if (controlledActiveIndex == null) setInternalActiveIndex(0) @@ -136,7 +143,11 @@ function LongImagePane({ if (bounds.width <= 0 || bounds.height <= 0) return const x = Math.min(1, Math.max(0, (clientX - bounds.left) / bounds.width)) const y = Math.min(1, Math.max(0, (clientY - bounds.top) / bounds.height)) - onAddAnnotation?.(x, y) + if (annotationRepositionMode) { + onMoveAnnotation?.(x, y) + } else { + onAddAnnotation?.(x, y) + } } return ( @@ -148,8 +159,9 @@ function LongImagePane({