공동 편집
공동 편집 통합에 따라 예제 실행, 앱 연결, 영속성과 권한 검증을 진행하세요. 구현 세부 정보와 Web/Server 예제는 협업 빠른 시작를 참고하세요.
인증된 비즈니스 API에서 Collaboration Service로 Unit을 생성한 뒤 반환된 ID를 브라우저에서 로드하세요. Quick Start에 따라 HTTP, Session Ticket, WebSocket을 설정하고 Database Adapter로 저장하세요. 읽기, JOIN, 수정은 서버 권한으로 각각 보호해야 합니다.
이 API를 사용하기 전에 대상 백엔드가 PDF Unit을 지원하는지 확인하세요. Sheets 협업 예제의 성공이 PDF 지원을 의미하지는 않습니다.
협업 PDF 불러오기
서버 API 또는 importPdfToUnitIdAsync를 통해 PDF 유닛을 만든 후 협업 퍼사드로 불러오세요.
문서를 로드하기 전에 현재 사용자를 설정하세요. 자세한 내용은 공동 편집 통합을 참고하세요.
import { UserManagerService } from '@univerjs/core'import '@univerjs-pro/collaboration-client/facade'univer.__getInjector().get(UserManagerService).setCurrentUser({ userID, name, avatar })const pdf = await univerAPI.getCollaboration().loadPdfAsync(unitId)if (!pdf) { throw new Error(`PDF unit not found: ${unitId}`)}console.log(pdf.getId(), pdf.getName())loadPdfAsync는 서버 스냅샷을 불러오고 협업 세션을 준비한 다음 FPdf를 반환합니다. 유닛을 PDF와 연결할 수 없으면 null을 반환합니다.
동기화 상태, 재전송, 충돌 복구로 저장 상태를 표시하고 새로고침 전에 미확인 변경을 보호하세요.
공동 편집 문서의 기본 URL 로드 기능 제거
주의 사항
1.2.0 버전부터 Univer의 기본 URL 공동 편집 문서 로드 로직이 제거되었습니다. 더 이상 URL의 unit과 type 매개변수를 자동으로 읽어 공동 편집 문서를 로드하지 않습니다.
애플리케이션이 이 동작에 의존한다면 사용자 정의 플러그인으로 복원할 수 있습니다.
사용자 정의 플러그인 연결
아래 네 파일을 다음 디렉터리 구조에 맞춰 프로젝트에 복사하세요.
custom-url-load/├── plugin.ts├── controllers/│ └── data-loader.controller.ts└── services/ ├── url.service.ts └── web-url.service.ts편집기와 공동 편집 플러그인을 초기화한 후 현재 사용자를 설정하고 CustomUrlLoadPlugin을 등록하세요.
import { UserManagerService } from '@univerjs/core'import { CustomUrlLoadPlugin } from './custom-url-load/plugin'univer.__getInjector().get(UserManagerService).setCurrentUser({ userID, name, avatar })univer.registerPlugin(CustomUrlLoadPlugin)등록 후에는 플러그인이 문서를 로드하므로 같은 문서에 대해 loadPdfAsync()을 호출할 필요가 없습니다. 이미 Facade로 문서를 로드하는 애플리케이션이라면 이전 절의 방식을 유지하고 애플리케이션 요구 사항에 맞춰 URL을 처리할 수 있습니다.
전체 플러그인 코드
import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client'import { DependentOn, Inject, Injector, Plugin } from '@univerjs/core'import { CollaborationDataLoaderController } from './controllers/data-loader.controller'import { IURLService } from './services/url.service'import { WebURLService } from './services/web-url.service'@DependentOn(UniverCollaborationClientPlugin)export class CustomUrlLoadPlugin extends Plugin { static override pluginName = 'CUSTOM_URL_LOAD_PLUGIN' constructor( _config: undefined, @Inject(Injector) protected override readonly _injector: Injector ) { super() } override onStarting(): void { this._injector.add([IURLService, { useClass: WebURLService }]) this._injector.add([CollaborationDataLoaderController]) this.disposeWithMe(this._injector.get(IURLService)) this.disposeWithMe(this._injector.get(CollaborationDataLoaderController)) }}import { DataLoaderService } from '@univerjs-pro/collaboration-client'import { ILogService, Inject, RxDisposable } from '@univerjs/core'import { IURLService } from '../services/url.service'/** * URL 매개변수로 공동 편집 문서를 로드한다. */export class CollaborationDataLoaderController extends RxDisposable { constructor( @IURLService private readonly _urlService: IURLService, @ILogService private readonly _logService: ILogService, @Inject(DataLoaderService) private readonly _dataLoaderService: DataLoaderService ) { super() const unitId = this._urlService.getParam('unit') const type = this._urlService.getParam('type') if (!unitId || !type) { return } this._dataLoaderService.loadUnit(unitId, Number(type)) .catch((error: unknown) => { this._logService.error('[CollaborationDataLoaderController]', error) }) }}import type { IDisposable } from '@univerjs/core'import type { Observable } from 'rxjs'import { createIdentifier } from '@univerjs/core'export interface IURLService extends IDisposable { getParam: (key: string) => string | undefined setParam: (key: string, value: string, useReplace?: boolean) => void removeParam: (key: string, useReplace?: boolean) => void readonly urlChange$: Observable<void>}export const IURLService = createIdentifier<IURLService>('demo.custom-url-load.url-service')import type { IURLService } from './url.service'import { RxDisposable } from '@univerjs/core'import { fromEvent, map, shareReplay, takeUntil } from 'rxjs'export class WebURLService extends RxDisposable implements IURLService { readonly urlChange$ = fromEvent(window, 'popstate').pipe( map(() => undefined), takeUntil(this.dispose$), shareReplay({ bufferSize: 1, refCount: true }) ) getParam(key: string): string | undefined { return new URL(window.location.href).searchParams.get(key) ?? undefined } setParam(key: string, value: string, useReplace = false): void { const url = new URL(window.location.href) url.searchParams.set(key, value) this._updateUrl(url, useReplace) } removeParam(key: string, useReplace = false): void { const url = new URL(window.location.href) url.searchParams.delete(key) this._updateUrl(url, useReplace) } private _updateUrl(url: URL, useReplace: boolean): void { if (useReplace) { window.history.replaceState(window.history.state, '', url.toString()) } else { window.history.pushState(window.history.state, '', url.toString()) } }}이 가이드는 어떠셨나요?