diff --git a/src/components/onlyoffice-web-comp/core/onlyoffice-manager.ts b/src/components/onlyoffice-web-comp/core/onlyoffice-manager.ts index 41600a3a..1088dd8e 100644 --- a/src/components/onlyoffice-web-comp/core/onlyoffice-manager.ts +++ b/src/components/onlyoffice-web-comp/core/onlyoffice-manager.ts @@ -17,7 +17,11 @@ import { type OnlyOfficeStaticResourceOptions, } from "../const"; import type { OfficeTheme } from "../internal/editor/types"; -import { getDocmentObj, setDocmentObj } from "../store/document"; +import { + clearDocmentObj, + getDocmentObj, + setDocmentObj, +} from "../store/document"; import { getCurrentLang, getOnlyOfficeLang, @@ -63,6 +67,14 @@ export type OpenDocumentInput = { officeXmlEvent?: OfficeXmlEventConfig; }; +export type OnlyOfficeExportBlobResult = { + blob: Blob; + fileName: string; + /** true 表示未经过编辑器导出转换,而是返回当前打开的原始文件。 */ + isOriginalFileFallback?: boolean; + fallbackReason?: "officeXmlSizeLimitExceeded"; +}; + export class OnlyOfficeManager { readonly containerId: string; readonly fileType: FileType; @@ -71,8 +83,6 @@ export class OnlyOfficeManager { private readOnly: boolean; private theme: OfficeTheme; private officeXmlEvent?: OfficeXmlEventConfig; - private sourceFile: File | null = null; - private sourceFileName = ""; private ready = false; private constructor( @@ -157,16 +167,17 @@ export class OnlyOfficeManager { /** 打开/切换文档(上传、新建、重开) */ async openDocument(input: OpenDocumentInput) { const readOnly = input.readOnly ?? this.readOnly; - this.sourceFile = input.file ?? null; - this.sourceFileName = input.fileName; - setDocmentObj({ - fileName: input.fileName, - file: input.file, - isNew: input.isNew ?? !input.file, - }); + setDocmentObj( + { + fileName: input.fileName, + file: input.file, + isNew: input.isNew ?? !input.file, + }, + this.containerId, + ); - const { fileName, file } = getDocmentObj(); + const { fileName, file } = getDocmentObj(this.containerId); await this.editor.create({ file, @@ -270,7 +281,19 @@ export class OnlyOfficeManager { } /** 导出为 Office 文件 Blob:Editor.bin → x2t → doc.{fileType}。 */ - async exportAsBlob() { + async exportAsBlob(): Promise { + if (this.editor.isOfficeXmlSizeLimitExceeded()) { + const { file, fileName } = getDocmentObj(this.containerId); + if (file) { + return { + blob: file, + fileName: fileName || file.name, + isOriginalFileFallback: true, + fallbackReason: "officeXmlSizeLimitExceeded", + }; + } + } + const binData = await this.editor.export(); const exportFileType = binData.fileType || this.fileType.toLowerCase(); const result = await convertBinToDocument( @@ -291,16 +314,6 @@ export class OnlyOfficeManager { /** 触发浏览器下载;内部先走 exportAsBlob 完整链路。 */ async downloadExport() { - if (this.editor.isOfficeXmlSizeLimitExceeded()) { - if (this.sourceFile) { - downloadBlob( - this.sourceFile, - this.sourceFileName || this.sourceFile.name, - ); - return; - } - } - const { blob, fileName } = await this.exportAsBlob(); downloadBlob(blob, fileName); } @@ -329,6 +342,7 @@ export class OnlyOfficeManager { destroy() { this.editor.destroy(); + clearDocmentObj(this.containerId); this.ready = false; } } diff --git a/src/components/onlyoffice-web-comp/store/document.ts b/src/components/onlyoffice-web-comp/store/document.ts index e96299fb..fda5f050 100644 --- a/src/components/onlyoffice-web-comp/store/document.ts +++ b/src/components/onlyoffice-web-comp/store/document.ts @@ -17,47 +17,84 @@ export type SetOnlyOfficeDocumentStateInput = Omit< fileName: string; }; -let documentState: OnlyOfficeDocumentState = { - isNew: true, - fileName: "New Document.docx", - fileType: "docx", -}; +const DEFAULT_DOCUMENT_SCOPE = "__default__"; + +function createDefaultDocumentState(): OnlyOfficeDocumentState { + return { + isNew: true, + fileName: "New Document.docx", + fileType: "docx", + }; +} -export function setDocmentObj(state: SetOnlyOfficeDocumentStateInput) { - documentState = { +function normalizeDocumentState( + state: SetOnlyOfficeDocumentStateInput, +): OnlyOfficeDocumentState { + return { ...state, isNew: state.isNew ?? !state.file, fileType: state.fileType || getFileExt(state.fileName) || "docx", }; } -export function getDocmentObj() { - return documentState; +const documentStates = new Map(); + +documentStates.set(DEFAULT_DOCUMENT_SCOPE, createDefaultDocumentState()); + +function getScopeId(scopeId?: string) { + return scopeId || DEFAULT_DOCUMENT_SCOPE; } -export function clearDocmentObj() { - documentState = { - isNew: true, - fileName: "New Document.docx", - fileType: "docx", - }; +export function setDocmentObj( + state: SetOnlyOfficeDocumentStateInput, + scopeId?: string, +) { + documentStates.set(getScopeId(scopeId), normalizeDocumentState(state)); } -export function setNewDocument(fileType = "docx") { - setDocmentObj({ - isNew: true, - fileName: `New Document.${fileType}`, - fileType, - }); +export function getDocmentObj(scopeId?: string) { + const key = getScopeId(scopeId); + let state = documentStates.get(key); + if (!state) { + state = createDefaultDocumentState(); + documentStates.set(key, state); + } + return state; } +export function clearDocmentObj(scopeId?: string) { + if (scopeId) { + documentStates.delete(scopeId); + return; + } -export function setDocumentFile(file: File, fileName = file.name) { - setDocmentObj({ - isNew: false, - file, - fileName, - fileType: getFileExt(fileName) || getFileExt(file.name) || "docx", - }); + documentStates.set(DEFAULT_DOCUMENT_SCOPE, createDefaultDocumentState()); +} + +export function setNewDocument(fileType = "docx", scopeId?: string) { + setDocmentObj( + { + isNew: true, + fileName: `New Document.${fileType}`, + fileType, + }, + scopeId, + ); +} + +export function setDocumentFile( + file: File, + fileName = file.name, + scopeId?: string, +) { + setDocmentObj( + { + isNew: false, + file, + fileName, + fileType: getFileExt(fileName) || getFileExt(file.name) || "docx", + }, + scopeId, + ); } export function setDocumentUrl( @@ -71,14 +108,27 @@ export function setDocumentUrl( fileName?: string; loader?: (url: string) => Promise; } = {}, + scopeId?: string, ) { const name = fileName || decodeURIComponent(url.split("/").pop() || "Document"); - setDocmentObj({ - isNew: false, - url, - loader, - fileName: name, - fileType: fileType || getFileExt(name) || "docx", - }); + setDocmentObj( + { + isNew: false, + url, + loader, + fileName: name, + fileType: fileType || getFileExt(name) || "docx", + }, + scopeId, + ); +} + +export function clearAllDocmentObjs() { + documentStates.clear(); + documentStates.set(DEFAULT_DOCUMENT_SCOPE, createDefaultDocumentState()); +} + +export function getAllDocmentObjs() { + return new Map(documentStates); }