diff --git a/src/components/image-editor/model3d-generation/Model3dGenerationModal.test.tsx b/src/components/image-editor/model3d-generation/Model3dGenerationModal.test.tsx index b4b57f8e2..56d97f232 100644 --- a/src/components/image-editor/model3d-generation/Model3dGenerationModal.test.tsx +++ b/src/components/image-editor/model3d-generation/Model3dGenerationModal.test.tsx @@ -298,4 +298,28 @@ describe('Model3dGenerationModal', () => { ); expect(onSubmit.mock.calls[0][0].model3dAttemptNonce).toBeTruthy(); }); + + it('失败态下主按钮也开新一代,不会重现同一次失败', () => { + const onSubmit = vi.fn(); + const failedDialog = createDialog('model3d-text-to-model', { + prompt: '一把木椅', + status: 'failed', + errorMessage: '3D 模型生成失败,请稍后重试。', + }); + render( + , + ); + + // 主按钮在失败态仍是「再生成一次」的自然入口,可以点;但不能沿用旧代次, + // 否则幂等键不变、服务端只会把原来那个终态 operation 还回来。 + expect((submitButton() as HTMLButtonElement).disabled).toBe(false); + fireEvent.click(submitButton()); + + expect(onSubmit).toHaveBeenCalledTimes(1); + expect(onSubmit.mock.calls[0][0].model3dAttemptNonce).not.toBe( + failedDialog.model3dAttemptNonce, + ); + expect(onSubmit.mock.calls[0][0].model3dAttemptNonce).toBeTruthy(); + expect(onSubmit.mock.calls[0][0].status).toBe('idle'); + }); }); diff --git a/src/components/image-editor/model3d-generation/Model3dGenerationModal.tsx b/src/components/image-editor/model3d-generation/Model3dGenerationModal.tsx index 67f9a9c74..57d8ffa5c 100644 --- a/src/components/image-editor/model3d-generation/Model3dGenerationModal.tsx +++ b/src/components/image-editor/model3d-generation/Model3dGenerationModal.tsx @@ -71,6 +71,11 @@ export function Model3dGenerationModal({ hasRequiredInput && !isGenerating && !hasPendingImageReferenceUploads; + // 幂等键 = 代次 + 请求内容指纹,同键同请求只会拿回原来那个终态 operation。 + // 所以失败态下的任何提交(主按钮或「重试」)都要开新一代,否则点下去只是重现同一次失败; + // 参数一改本来就会换代号次,这里补的是「不改参数直接再点一次」这条路。 + const submitDialog = + dialog.status === 'failed' ? refreshModel3dAttemptNonce(dialog) : dialog; useImageCanvasFloatingOptionDismiss({ isOpen: isImageMode && isGenerationReferenceMenuOpen, @@ -114,7 +119,7 @@ export function Model3dGenerationModal({ className="image-canvas-editor__generation-submit" disabled={!canSubmit} aria-label={MODEL3D_GENERATION_SUBMIT_LABEL} - onClick={() => onSubmit(dialog)} + onClick={() => onSubmit(submitDialog)} > {isGenerating ? ( '生成中' @@ -135,7 +140,7 @@ export function Model3dGenerationModal({ size="xs" shape="pill" aria-label="重试生成 3D 模型" - onClick={() => onSubmit(refreshModel3dAttemptNonce(dialog))} + onClick={() => onSubmit(submitDialog)} > 重试