diff --git a/libs/tiptap-editor/src/upload-coordinator.ts b/libs/tiptap-editor/src/upload-coordinator.ts index dfa252a..cf17024 100644 --- a/libs/tiptap-editor/src/upload-coordinator.ts +++ b/libs/tiptap-editor/src/upload-coordinator.ts @@ -58,6 +58,8 @@ export class UploadCoordinator { 'data-upload-id': uploadId, }, }).run() + + this.runUpload(uploadId) } /** 按 uploadId 删除节点(revoke blob、清 pending)。NodeView 移除按钮 / Rust ×关闭 共用。 */ @@ -70,6 +72,41 @@ export class UploadCoordinator { return true } + /** 核心上传逻辑:成功更新 src + 清上传属性,失败转 error 态。 */ + private async runUpload(uploadId: string): Promise { + const entry = this.pending.get(uploadId) + if (!entry) return + try { + const url = await this.onImageUpload(entry.file) + // 成功:替换 src,清除上传状态属性 + this.updateNodeAttrs(uploadId, { + src: url, + 'data-upload-state': null, + 'data-upload-id': null, + 'data-error-msg': null, + }) + URL.revokeObjectURL(entry.blobUrl) + this.pending.delete(uploadId) + } catch (err) { + const msg = this.extractErrorMessage(err) + this.updateNodeAttrs(uploadId, { + 'data-upload-state': 'error', + 'data-error-msg': msg, + }) + } + } + + /** 重试:从 pending 取回原 File,节点转回 uploading,重跑上传。 */ + retryUpload(uploadId: string): void { + const entry = this.pending.get(uploadId) + if (!entry) return + this.updateNodeAttrs(uploadId, { + 'data-upload-state': 'uploading', + 'data-error-msg': null, + }) + this.runUpload(uploadId) + } + /** 按 uploadId 在文档中定位节点并删除。 */ private removeNodeByUploadId(uploadId: string): void { let targetPos: number | null = null @@ -110,6 +147,12 @@ export class UploadCoordinator { } } + /** 从错误对象提取消息。改造后的 fetch 直接抛服务端中文(如"文件超过大小限制")。 */ + private extractErrorMessage(err: unknown): string { + if (err instanceof Error) return err.message + return String(err) + } + /** pending Map 查询(供内部/测试)。 */ hasPending(uploadId: string): boolean { return this.pending.has(uploadId)