# Совместное редактирование

- Human documentation: [https://docs.univer.ai/ru-RU/guides/bases/features/collaboration](https://docs.univer.ai/ru-RU/guides/bases/features/collaboration)

- Agent Markdown: [https://docs.univer.ai/ru-RU/guides/bases/features/collaboration.md](https://docs.univer.ai/ru-RU/guides/bases/features/collaboration.md)

- Requested language: `ru-RU`

- Content language: `ru-RU`

- Documentation version: `1.2.0`

- Source: [bases/features/collaboration.ru-RU.mdx](https://github.com/dream-num/documentation/blob/dev/content/guides/bases/features/collaboration.ru-RU.mdx)

---

#### Package metadata

```json
{
  "preset": [],
  "plugins": [
    {
      "client": "@univerjs-pro/collaboration"
    },
    {
      "client": "@univerjs-pro/collaboration-client",
      "facade": "@univerjs-pro/collaboration-client/facade",
      "locale": "@univerjs-pro/collaboration-client/locale/ru-RU"
    },
    {
      "client": "@univerjs-pro/collaboration-client-ui",
      "facade": "@univerjs-pro/collaboration-client-ui/facade",
      "locale": "@univerjs-pro/collaboration-client-ui/locale/ru-RU",
      "style": "@univerjs-pro/collaboration-client-ui/lib/index.css"
    }
  ],
  "license": true,
  "server": true
}
```

Пользователи могут открыть одну Base и совместно редактировать таблицы, поля, записи и представления. Они могут работать в разных представлениях, изменяя один документ.

## Шаги интеграции

1. Следуйте разделу [Интеграция совместного редактирования](https://docs.univer.ai/ru-RU/server/collaboration/browser-integration.md), чтобы запустить пример и подключить вход, права доступа и хранение в базе данных.
2. Добавьте настройки клиента Collaboration в существующий редактор Bases. Настройте браузер по инструкции [Быстрый старт совместного редактирования](https://docs.univer.ai/ru-RU/server/collaboration/quick-start.md), но создавайте и сохраняйте документы Bases вместо копирования данных Sheets из примера.
3. Создайте Base через серверную часть приложения и верните её ID браузеру.

## Открытие Base с сервера

Задайте текущего пользователя до загрузки документа. См. раздел [Интеграция совместного редактирования](https://docs.univer.ai/ru-RU/server/collaboration/browser-integration.md).

```ts
import '@univerjs-pro/bases/facade'
import { UserManagerService } from '@univerjs/core'
import '@univerjs-pro/collaboration-client/facade'

univer.__getInjector().get(UserManagerService).setCurrentUser({ userID, name, avatar })

const base = await univerAPI.getCollaboration().loadBaseAsync('your-base-id')
if (!base) throw new Error('Base could not be loaded')
```

Повторно используйте ID при открытии или совместном доступе к документу. Не заменяйте загрузку с сервера вызовом `createBase()` и созданием пустого локального документа.

Используйте [состояние, повторные отправки и восстановление конфликтов](https://docs.univer.ai/ru-RU/server/collaboration/browser-integration.md#sync-status) для индикации сохранения и защиты неподтверждённых изменений перед перезагрузкой.

## Удаление встроенной загрузки совместных документов по URL

> [!WARNING: Обратите внимание]
> Начиная с версии **1.2.0**, Univer больше не содержит встроенной логики загрузки совместных документов по URL. Параметры `unit` и `type` больше не считываются автоматически для загрузки совместного документа.

Если ваше приложение зависит от этого поведения, его можно восстановить с помощью собственного плагина.

### Подключение собственного плагина

Скопируйте четыре файла ниже в проект, сохранив следующую структуру каталогов:

```text
custom-url-load/
├── plugin.ts
├── controllers/
│   └── data-loader.controller.ts
└── services/
    ├── url.service.ts
    └── web-url.service.ts
```

После инициализации редактора и плагинов совместной работы задайте текущего пользователя, а затем зарегистрируйте `CustomUrlLoadPlugin`.

```ts
import { UserManagerService } from '@univerjs/core'
import { CustomUrlLoadPlugin } from './custom-url-load/plugin'

univer.__getInjector().get(UserManagerService).setCurrentUser({ userID, name, avatar })

univer.registerPlugin(CustomUrlLoadPlugin)
```

После регистрации плагин сам загружает документ, поэтому вызывать `loadBaseAsync()` для того же документа не требуется. Если приложение уже загружает документы через Facade, можно сохранить подход из предыдущего раздела и обрабатывать URL в соответствии с требованиями приложения.

### Полный код плагина

#### plugin.ts

```ts
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))
  }
}
```

#### controllers/data-loader.controller.ts

```ts
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)
      })
  }
}
```

#### services/url.service.ts

```ts
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')
```

#### services/web-url.service.ts

```ts
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())
    }
  }
}
```

## Проверка синхронизации и сохранения

Откройте один ID в двух независимых сеансах браузера. В одном добавьте запись или измените поле и убедитесь, что другой получил изменение. Проверьте сохранность изменений после обновления страницы и перезапуска сервера.

Проверяйте доступ читателей и пользователей без разрешений на сервере. [Локальные настройки только для чтения](https://docs.univer.ai/ru-RU/guides/bases/features/core/permissions.md) управляют лишь редактором. Преобразование файлов, комментарии и история требуют соответствующих серверных функций; см. [Импорт и экспорт](https://docs.univer.ai/ru-RU/guides/bases/features/import-export.md), [Комментарии](https://docs.univer.ai/ru-RU/guides/bases/features/thread-comment.md) и [История](https://docs.univer.ai/ru-RU/guides/bases/features/edit-history.md).
