From 50ea94ce3c16e8847760e929a48b59096105f627 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 17:22:00 +0800 Subject: [PATCH] feat: add work review revision submission flow --- components/work-review/WorkReviewBoard.tsx | 24 ++-- components/work-review/WorkReviewDialogs.tsx | 53 +++++++-- components/work-review/WorkReviewHeader.tsx | 30 ++++- .../WorkReviewReviewerWorkspace.tsx | 8 +- .../work-review-resubmission-ui.test.tsx | 111 ++++++++++++++++++ .../work-review-reviewer-workspace.test.tsx | 35 ++++++ lib/work-review-service.ts | 21 +++- types/work-review.ts | 1 + 8 files changed, 259 insertions(+), 24 deletions(-) create mode 100644 lib/__tests__/work-review-resubmission-ui.test.tsx diff --git a/components/work-review/WorkReviewBoard.tsx b/components/work-review/WorkReviewBoard.tsx index 541779f..b947dc3 100644 --- a/components/work-review/WorkReviewBoard.tsx +++ b/components/work-review/WorkReviewBoard.tsx @@ -241,23 +241,29 @@ export default function WorkReviewBoard() { const requestedProject = expandedProjectId ? data?.items.find((item) => item.id === expandedProjectId) : null - const reviewerCandidate = requestedProject + const focusedCandidate = requestedProject const reviewerEmployeeId = me?.employee?.id - const reviewerFocusProject = reviewerCandidate && ( - reviewerCandidate.permissions.pendingReviewRoles.length > 0 + const focusedProject = focusedCandidate && ( + focusedCandidate.permissions.canResubmit + || focusedCandidate.permissions.pendingReviewRoles.length > 0 || Boolean(reviewerEmployeeId && ( - reviewerCandidate.manager.id === reviewerEmployeeId - || reviewerCandidate.finalReviewer.id === reviewerEmployeeId + focusedCandidate.manager.id === reviewerEmployeeId + || focusedCandidate.finalReviewer.id === reviewerEmployeeId )) - ) ? reviewerCandidate : null + ) ? focusedCandidate : null - if (reviewerFocusProject) { + if (focusedProject) { return (
setSubmissionDialog({ mode: 'resubmit', project: focusedProject }), + } : undefined} primaryAction={{ label: '返回看板', icon: 'back', @@ -275,13 +281,14 @@ export default function WorkReviewBoard() { />
submitReview(version.id, input)} />
{submissionDialog && ( setSubmissionDialog(null)} @@ -500,6 +507,7 @@ export default function WorkReviewBoard() { {submissionDialog && ( setSubmissionDialog(null)} diff --git a/components/work-review/WorkReviewDialogs.tsx b/components/work-review/WorkReviewDialogs.tsx index 1daae1c..dc6567a 100644 --- a/components/work-review/WorkReviewDialogs.tsx +++ b/components/work-review/WorkReviewDialogs.tsx @@ -62,6 +62,7 @@ function WorkReviewDialogShell({ }) { const [ready, setReady] = useState(false) const titleId = useId() + const subtitleId = useId() const dialogRef = useRef(null) const closeRef = useRef(null) @@ -117,6 +118,7 @@ function WorkReviewDialogShell({ role="dialog" aria-modal="true" aria-labelledby={titleId} + aria-describedby={subtitle ? subtitleId : undefined} aria-busy={busy} tabIndex={-1} className={`flex max-h-[94dvh] w-full flex-col overflow-hidden rounded-t-2xl bg-white text-[#101828] shadow-2xl sm:max-h-[calc(100dvh-40px)] sm:rounded-xl ${widthClass}`} @@ -124,7 +126,7 @@ function WorkReviewDialogShell({

{title}

- {subtitle &&

{subtitle}

} + {subtitle &&

{subtitle}

}
{children}
-
{footer}
+
{footer}
, document.body, @@ -170,7 +172,9 @@ export function WorkReviewSubmissionDialog({ const [busy, setBusy] = useState(false) const [error, setError] = useState(null) const [progress, setProgress] = useState<{ message: string; percent?: number } | null>(null) + const submitLockRef = useRef(false) const isCreate = mode === 'create' + const nextVersion = Number(project?.latestVersion || 0) + 1 useEffect(() => { if (!isCreate) return @@ -195,6 +199,7 @@ export function WorkReviewSubmissionDialog({ }, [isCreate, reviewerLoadKey]) const submit = async () => { + if (submitLockRef.current) return if (isCreate && !title.trim()) { setError('请填写作品名称') return @@ -216,6 +221,7 @@ export function WorkReviewSubmissionDialog({ return } + submitLockRef.current = true setBusy(true) setError(null) setProgress({ message: '正在准备图片' }) @@ -245,24 +251,28 @@ export function WorkReviewSubmissionDialog({ workDate, reviewerEmployeeId, } - : {}), + : { + expectedLatestVersion: project?.latestVersion, + }), submissionNote: submissionNote.trim(), processedImages, processedReferenceImages, }) } catch (submitError) { + setProgress(null) setError(submitError instanceof Error ? submitError.message : '提交失败,请重试') } finally { + submitLockRef.current = false setBusy(false) } } return ( {progress.message} {typeof progress.percent === 'number' && {progress.percent}%} -
+
void submit()} className="min-h-11 rounded bg-[#0050cb] px-5 text-[13px] font-bold text-white hover:bg-[#003fa3] disabled:cursor-not-allowed disabled:bg-[#9bbcec]" > - {busy ? '正在提交...' : isCreate ? '提交并通知评审人' : '提交新版本'} + {busy ? '正在提交...' : isCreate ? '提交并通知评审人' : `提交修改 V${nextVersion}`}
@@ -369,9 +386,25 @@ export function WorkReviewSubmissionDialog({ ) : ( -
- 请确认已处理当前版本中的指定评审人和元泽评审建议。提交后,两位评审人会收到新一轮通知。 -
+ <> +
+
+
作品名称
+
{project?.title || '-'}
+
+
+
评审人
+
{project?.manager.name || '-'}
+
+
+
修改提交日期
+
{workDate}
+
+
+
+ 请确认已处理 V{project?.latestVersion || 1} 中的评审建议。提交后,原评审人与元泽会收到 V{nextVersion} 的新一轮评审通知。 +
+ )}