Совместное редактирование
Следуйте руководству по Интеграция совместного редактирования: запустите пример, подключите приложение и проверьте хранение данных и права доступа. Реализация и примеры Web/Server находятся в Быстрый старт совместного редактирования.
Создайте Unit через бизнес-API с аутентификацией, вызывающий Collaboration Service, затем загрузите полученный ID в браузере. Настройте HTTP, Session Ticket и WebSocket по Quick Start, хранение через Database Adapter и отдельные проверки прав на чтение, JOIN и изменение.
Загрузка документа
Задайте текущего пользователя до загрузки документа. Он определяет личность участника и владельца локального офлайн-кэша. После этого загрузите документ явно через Facade:
import { UserManagerService } from '@univerjs/core'import '@univerjs-pro/collaboration-client/facade'univer.__getInjector().get(UserManagerService).setCurrentUser({ userID, name, avatar })const collaboration = univerAPI.getCollaboration()const unit = await collaboration.loadDocAsync('your-unit-id')Используйте состояние, повторные отправки и восстановление конфликтов для индикации сохранения и защиты неподтверждённых изменений перед перезагрузкой.
Удаление встроенной загрузки совместных документов по URL
Обратите внимание
Начиная с версии 1.2.0, Univer больше не содержит встроенной логики загрузки совместных документов по 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)После регистрации плагин сам загружает документ, поэтому вызывать loadDocAsync() для того же документа не требуется. Если приложение уже загружает документы через 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()) } }}Насколько полезно это руководство?