完整示例

← 事件系统 | API 参考 →

以下示例对齐仓库演示页 src/features/demo/office-preview-page.tsx

React 单实例页面

"use client";

import { memo, useEffect, useRef, useState } from "react";
import {
  ONLYOFFICE_CONTAINER_CONFIG,
  ONLYOFFICE_ID,
  ONLYOFFICE_LANG_KEY,
  OnlyOfficeManager,
  FILE_TYPE,
  type FileType,
} from "@/components/onlyoffice-web-comp";

const OnlyOfficeHost = memo(function OnlyOfficeHost() {
  return (
    <div
      className={`${ONLYOFFICE_CONTAINER_CONFIG.PARENT_CLASS_NAME} absolute inset-0`}
    >
      <div id={ONLYOFFICE_ID} className="absolute inset-0" />
    </div>
  );
});

async function fetchPublicFile(url: string, fileName: string) {
  const response = await fetch(url);
  if (!response.ok) throw new Error(`Failed to fetch ${url}`);
  const blob = await response.blob();
  return new File([blob], fileName, { type: blob.type });
}

export default function EditorPage({
  defaultFileName = "New_Document.docx",
  fileType = FILE_TYPE.DOCX,
  initialFileUrl,
}: {
  defaultFileName?: string;
  fileType?: FileType;
  initialFileUrl?: string;
}) {
  const fileInputRef = useRef<HTMLInputElement>(null);
  const managerRef = useRef<OnlyOfficeManager | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [readOnly, setReadOnly] = useState(false);
  const [editorReady, setEditorReady] = useState(false);

  useEffect(() => {
    let unsubscribeLoading: (() => void) | undefined;
    let cancelled = false;

    const init = async () => {
      let manager: OnlyOfficeManager;

      if (initialFileUrl) {
        const file = await fetchPublicFile(initialFileUrl, defaultFileName);
        if (cancelled) return;
        manager = await OnlyOfficeManager.createWithFile(
          { containerId: ONLYOFFICE_ID, fileType, defaultFileName, readOnly },
          file,
        );
      } else {
        manager = await OnlyOfficeManager.create({
          containerId: ONLYOFFICE_ID,
          fileType,
          defaultFileName,
          readOnly,
        });
      }

      if (cancelled) {
        manager.destroy();
        return;
      }

      managerRef.current = manager;
      setEditorReady(true);
      unsubscribeLoading = manager.onLoadingChange(({ loading: next }) => {
        setLoading(next);
      });
    };

    init().catch(() => setError("无法加载编辑器组件"));

    return () => {
      cancelled = true;
      unsubscribeLoading?.();
      managerRef.current?.destroy();
      managerRef.current = null;
    };
  }, []);

  const openDocument = async (fileName: string, file?: File) => {
    const manager = managerRef.current;
    if (!manager) return;
    await manager.openDocument({ fileName, file, readOnly });
  };

  return (
    <div className="flex h-screen flex-col">
      <div className="flex gap-2 p-4">
        <button type="button" onClick={() => fileInputRef.current?.click()}>
          上传文档
        </button>
        <button type="button" onClick={() => openDocument(defaultFileName)}>
          新建文档
        </button>
        {editorReady && (
          <>
            <button type="button" onClick={() => managerRef.current?.downloadExport()}>
              导出
            </button>
            <button
              type="button"
              onClick={() => {
                managerRef.current?.toggleReadOnly();
                setReadOnly(managerRef.current?.getReadOnly() ?? false);
              }}
            >
              {readOnly ? "只读" : "编辑"}
            </button>
          </>
        )}
      </div>

      {error && <p className="px-4 text-red-600">{error}</p>}

      <div className="relative flex-1">
        <OnlyOfficeHost />
        {loading && <div className="absolute inset-0 bg-white/70" />}
      </div>

      <input
        ref={fileInputRef}
        type="file"
        className="hidden"
        onChange={(e) => {
          const file = e.target.files?.[0];
          if (file) openDocument(file.name, file);
        }}
      />
    </div>
  );
}

多实例 Tab(onlyOfficeManagerFactory

import {
  onlyOfficeManagerFactory,
  FILE_TYPE,
} from "@/components/onlyoffice-web-comp";

// 按 containerId 打开,切换 Tab 时保留实例(隐藏而非销毁)
await onlyOfficeManagerFactory.open(
  {
    containerId: "tab-editor-abc",
    fileType: FILE_TYPE.DOCX,
    defaultFileName: "New_Document.docx",
  },
  { fileName: "New_Document.docx", isNew: true },
);

// 页面卸载
onlyOfficeManagerFactory.destroyAll();

完整实现见 src/features/demo/tabs-multi-page.tsx

何时用底层 API

以下场景可直接使用 EditorManager / createEditorView

  • 需要 url + 自定义 loader 打开远程文档
  • 批注修订、Word SDK 回调(subscribe

一般业务集成优先 OnlyOfficeManager,见 核心 API