npm package: @koi-br/office-sdk-wrapper
Description: 一个统一的办公服务SDK封装库，同时支持 **WPS Office** 和 **OnlyOffice**。
Author: br
Latest version: 1.0.4

# office-sdk-wrapper


一个统一的办公服务SDK封装库，同时支持 **WPS Office** 和 **OnlyOffice**。

## 📦 Installation / 安装

```bash
npm install @koi-br/office-sdk-wrapper
```

### Vue项目额外依赖

如果你的项目使用Vue，还需要安装OnlyOffice的Vue组件：

```bash
npm install @onlyoffice/document-editor-vue
```

## 🚀 Quick Start / 快速开始

### Using WPS Office / 使用WPS Office

```javascript
import { createWPSSDK, DocumentType } from '@koi-br/office-sdk-wrapper';

const sdk = await createWPSSDK({
  fileId: "your-file-id",
  appId: "your-app-id",
  containerSelector: ".office-container",
  token: "your-access-token",
  isReadOnly: false,
  documentType: DocumentType.WORD,
  onReady: (sdk) => {
    console.log('WPS SDK ready!', sdk);
  }
});

// Use unified API methods
await sdk.searchAndLocateText('Hello World', true);
await sdk.insertTextAtCursor('New text content');
await sdk.saveDocument();
```

### Using OnlyOffice / 使用OnlyOffice

```javascript
import { createOnlyOfficeSDK, DocumentType } from '@koi-br/office-sdk-wrapper';

const sdk = await createOnlyOfficeSDK({
  fileId: "your-file-id",
  appId: "your-app-id",
  containerSelector: ".office-container",
  isReadOnly: false,
  documentType: DocumentType.WORD,
  documentConfig: {
    title: 'My Document',
    url: 'https://your-server.com/documents/your-file-id'
  },
  editorConfig: {
    lang: 'zh-CN',
    mode: 'edit'
  },
  onReady: (sdk) => {
    console.log('OnlyOffice SDK ready!', sdk);
  }
});

// Same API as WPS!
await sdk.searchAndLocateText('Hello World', true);
await sdk.insertTextAtCursor('New text content');
await sdk.saveDocument();
```

### Universal Approach / 通用方法

```javascript
import { initOfficeSDK, OfficeProvider, DocumentType } from '@koi-br/office-sdk-wrapper';

// Let users choose their preferred office suite
const provider = OfficeProvider.WPS; // or OfficeProvider.ONLYOFFICE

const sdk = await initOfficeSDK({
  fileId: "your-file-id",
  appId: "your-app-id",
  containerSelector: ".office-container",
  provider: provider,
  isReadOnly: false,
  documentType: DocumentType.WORD,
  token: "your-access-token",
  onReady: (sdk) => {
    console.log(`${sdk.provider.toUpperCase()} SDK ready!`, sdk);
  }
});
```

### 🤖 Smart Configuration / 智能配置

使用智能配置管理器，让系统根据你的需求自动推荐最佳提供商：

```javascript
import { createSmartConfig, initOfficeSDK } from '@koi-br/office-sdk-wrapper';

// 智能配置推荐
const { config, recommendation } = createSmartConfig(
  {
    fileId: "your-file-id",
    appId: "your-app-id",
    containerSelector: ".office-container"
  },
  {
    needsAdvancedRevision: true,  // 需要高级修订功能
    needsCollaboration: false,    // 不需要多人协作
    isVueProject: false,         // 不是Vue项目
    hasWPSLicense: true          // 有WPS许可证
  }
);

console.log(`推荐使用: ${recommendation.provider}`);
console.log(`推荐理由: ${recommendation.reason}`);
console.log(`提示: ${recommendation.tips.join(', ')}`);

// 使用推荐的配置
const sdk = await initOfficeSDK(config);
```

### ⚙️ Environment-based Auto Selection / 基于环境的自动选择

```javascript
import { getConfigManager } from '@koi-br/office-sdk-wrapper';

// 设置环境变量或全局变量
process.env.OFFICE_PROVIDER = 'wps'; // or 'onlyoffice'
// 或者在浏览器中设置: window.__OFFICE_PROVIDER__ = 'wps';

// 自动选择提供商
const manager = getConfigManager();
const config = manager.autoSelectProvider({
  fileId: "your-file-id",
  appId: "your-app-id",
  containerSelector: ".office-container"
}, {
  fallback: OfficeProvider.WPS,
  checkAvailability: true
});

const sdk = await initOfficeSDK(config);
```

## 🛠️ API Reference / API 参考

### Unified Interface Methods / 统一接口方法

All SDK instances implement the same interface, regardless of the underlying office suite:

所有SDK实例都实现相同的接口，无论底层使用的是哪个办公套件：

```typescript
interface IOfficeSDK {
  // Document Operations / 文档操作
  saveDocument(): Promise<void>;
  setDocumentReadOnly(isReadOnly: boolean): Promise<void>;
  getDocumentLength(): Promise<number>;
  
  // Text Operations / 文本操作
  searchAndLocateText(content: string, highlight?: boolean): Promise<SearchResult | null>;
  insertTextAtCursor(text: string): Promise<boolean>;
  clearHighlight(): Promise<void>;
  replaceText(original: string, replacement: string, pos: number, length: number): Promise<{success: boolean, modifyDate?: string}>;
  
  // Formatting / 格式化
  formatDocumentFont(fontFormat: FontFormat): Promise<void>;
  
  // Revision Management / 修订管理
  getLatestRevisionDate(): Promise<string>;
  getRevisionsByDate(date: string): Promise<RevisionInfo[]>;
  handleRevisions(date: string, action: 'accept' | 'reject' | 'highlight'): Promise<void>;
  
  // Lifecycle / 生命周期
  destroy(): Promise<void>;
  isReady(): boolean;
}
```

### Configuration Interfaces / 配置接口

#### Base Configuration / 基础配置

```typescript
interface BaseInitConfig {
  fileId: string;                    // 文件ID
  appId: string;                     // 应用ID
  containerSelector: string;         // 容器选择器
  isReadOnly?: boolean;              // 是否只读模式
  token?: string;                    // 访问令牌
  onReady?: (sdk: IOfficeSDK) => void;      // 初始化完成回调
  onError?: (error: any) => void;           // 错误回调
  provider: OfficeProvider;          // 办公软件提供商
  documentType?: DocumentType;       // 文档类型
}
```

#### WPS Specific Configuration / WPS特定配置

```typescript
interface WPSInitConfig extends BaseInitConfig {
  provider: OfficeProvider.WPS;
  simple?: boolean;                  // 简单模式
  refreshToken?: (token: string, timeout?: number) => void;
}
```

#### OnlyOffice Specific Configuration / OnlyOffice特定配置

```typescript
interface OnlyOfficeInitConfig extends BaseInitConfig {
  provider: OfficeProvider.ONLYOFFICE;
  editorConfig?: {
    mode?: 'edit' | 'view' | 'review';
    lang?: string;
    callbackUrl?: string;
  };
  documentConfig?: {
    fileType?: string;
    key?: string;
    title?: string;
    url?: string;
  };
}
```

## 🖼️ React Example / React示例

```jsx
import React, { useEffect, useState } from 'react';
import { initOfficeSDK, OfficeProvider, DocumentType } from '@koi-br/office-sdk-wrapper';

function OfficeComponent() {
  const [sdk, setSdk] = useState(null);
  const [provider, setProvider] = useState(OfficeProvider.WPS);

  useEffect(() => {
    const initSDK = async () => {
      const sdkInstance = await initOfficeSDK({
        fileId: "your-file-id",
        appId: "your-app-id",
        containerSelector: ".office-container",
        provider: provider,
        documentType: DocumentType.WORD,
        onReady: setSdk,
        token: "your-token"
      });
    };

    initSDK();
    return () => sdk?.destroy();
  }, [provider]);

  const switchProvider = async (newProvider) => {
    if (sdk) {
      await sdk.destroy();
      setSdk(null);
    }
    setProvider(newProvider);
  };

  return (
    <div>
      <div className="controls">
        <button onClick={() => switchProvider(OfficeProvider.WPS)}>
          WPS Office
        </button>
        <button onClick={() => switchProvider(OfficeProvider.ONLYOFFICE)}>
          OnlyOffice
        </button>
        <button onClick={() => sdk?.insertTextAtCursor('Hello!')}>
          Insert Text
        </button>
        <button onClick={() => sdk?.saveDocument()}>
          Save
        </button>
      </div>
      <div className="office-container" style={{width: '100%', height: '600px'}} />
    </div>
  );
}
```

## 🖼️ Vue Example / Vue示例

### 重要说明：Vue组件的正确导入方式

由于 Vue 单文件组件的特殊性，请按照下表正确导入：

| 功能 | 导入来源 | 示例 |
|------|------|------|
| Vue组件 | 组件路径 | `import OnlyOfficeVueEditor from '@koi-br/office-sdk-wrapper/src/components/OnlyOfficeVueEditor.vue'` |
| Vue适配器 | vue 模块 | `import { createOnlyOfficeVueSDK } from '@koi-br/office-sdk-wrapper/vue'` |
| 类型定义 | vue 模块 | `import type { OnlyOfficeVueAdapter } from '@koi-br/office-sdk-wrapper/vue'` |
| 通用功能 | 主模块 | `import { OfficeProvider, DocumentType } from '@koi-br/office-sdk-wrapper'` |

```typescript
// ✅ 正确的导入方式

// 1. Vue组件从组件路径直接导入
import OnlyOfficeVueEditor from '@koi-br/office-sdk-wrapper/src/components/OnlyOfficeVueEditor.vue';

// 2. 适配器和类型从 vue 模块导入
import { 
  createOnlyOfficeVueSDK,
  type OnlyOfficeVueAdapter,
  DocumentType 
} from '@koi-br/office-sdk-wrapper/vue';

// 3. 通用功能从主模块导入
import { 
  OfficeProvider,
  createWPSSDK
} from '@koi-br/office-sdk-wrapper';
```

```typescript
// ❌ 错误的导入方式（不要这样做）
import { 
  OnlyOfficeVueEditor  // 这个导入会失败！
} from '@koi-br/office-sdk-wrapper/vue';
```

### 方法一：使用封装的Vue组件（推荐）

```vue
<template>
  <div class="office-app">
    <div class="controls">
      <button @click="handleInsertText">插入文本</button>
      <button @click="handleSaveDocument">保存文档</button>
      <button @click="handleSearch">搜索文本</button>
    </div>
    
    <OnlyOfficeVueEditor
      :init-config="editorConfig"
      :editor-id="editorId"
      @ready="handleEditorReady"
      @error="handleEditorError"
      @document-ready="handleDocumentReady"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
// Vue组件从组件路径导入
import OnlyOfficeVueEditor from '@koi-br/office-sdk-wrapper/src/components/OnlyOfficeVueEditor.vue';
// 适配器和类型从vue模块导入
import { 
  createOnlyOfficeVueSDK,
  type OnlyOfficeVueAdapter,
  DocumentType 
} from '@koi-br/office-sdk-wrapper/vue';

const fileId = ref('your-file-id');
const appId = ref('your-app-id');
const editorId = ref(`onlyoffice-${Date.now()}`);
const sdkRef = ref<OnlyOfficeVueAdapter | null>(null);
const editorRef = ref<any>(null);

const editorConfig = computed(() => ({
  fileId: fileId.value,
  appId: appId.value,
  containerSelector: '.office-container',
  isReadOnly: false,
  documentType: DocumentType.WORD,
  documentConfig: {
    title: 'My Document',
    url: 'https://example.com/document.docx',
    key: fileId.value
  },
  editorConfig: {
    lang: 'zh-CN',
    mode: 'edit' as const
  },
  onReady: (sdk: OnlyOfficeVueAdapter) => {
    sdkRef.value = sdk;
    console.log('ONLYOFFICE SDK 准备就绪');
  },
  onError: (error: any) => {
    console.error('SDK 错误:', error);
  }
}));

// 创建适配器实例
onMounted(async () => {
  sdkRef.value = createOnlyOfficeVueSDK(editorConfig.value);
  await sdkRef.value.initialize();
});

const handleInsertText = async () => {
  if (sdkRef.value?.isReady()) {
    await sdkRef.value.insertTextAtCursor('Hello from Vue!');
  }
};

const handleSaveDocument = async () => {
  if (sdkRef.value?.isReady()) {
    await sdkRef.value.saveDocument();
  }
};

const handleSearch = async () => {
  if (sdkRef.value?.isReady()) {
    const result = await sdkRef.value.searchAndLocateText('搜索内容', true);
    console.log('搜索结果:', result);
  }
};

const handleEditorReady = (event: any) => {
  console.log('编辑器准备就绪:', event);
  // 设置Vue组件实例引用
  if (sdkRef.value) {
    sdkRef.value.setVueComponentInstance(editorRef.value);
  }
};

const handleEditorError = (error: any) => {
  console.error('编辑器错误:', error);
};

const handleDocumentReady = (event: any) => {
  console.log('文档准备就绪:', event);
};
</script>

<style scoped>
.office-app {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.controls {
  padding: 10px;
  background: #f5f5f5;
  display: flex;
  gap: 10px;
}

.controls button {
  padding: 8px 16px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.controls button:hover {
  background: #0056b3;
}
</style>
```

### 方法二：直接使用原生ONLYOFFICE Vue组件

```vue
<template>
  <div class="office-app">
    <DocumentEditor
      id="onlyoffice-editor"
      documentServerUrl="http://documentserver/"
      :config="onlyOfficeConfig"
      :events_onDocumentReady="onDocumentReady"
      :events_onAppReady="onAppReady"
      :events_onError="onError"
      :onLoadComponentError="onLoadComponentError"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
import { DocumentEditor } from '@onlyoffice/document-editor-vue';

const fileId = ref('your-file-id');
const appId = ref('your-app-id');

const onlyOfficeConfig = computed(() => ({
  document: {
    fileType: "docx",
    key: fileId.value,
    title: "Example Document Title.docx",
    url: "https://example.com/url-to-example-document.docx"
  },
  documentType: "word",
  editorConfig: {
    mode: 'edit',
    lang: 'zh-CN',
    callbackUrl: "https://example.com/url-to-callback.ashx"
  }
}));

const onDocumentReady = (event: any) => {
  console.log("文档准备就绪:", event);
  
  // 获取编辑器实例
  const documentEditor = window.DocEditor.instances["onlyoffice-editor"];
  
  // 调用编辑器方法
  documentEditor.showMessage("欢迎使用 ONLYOFFICE 编辑器!");
};

const onAppReady = (event: any) => {
  console.log("应用程序准备就绪:", event);
};

const onError = (event: any) => {
  console.error("ONLYOFFICE 错误:", event);
};

const onLoadComponentError = (errorCode: number, errorDescription: string) => {
  switch(errorCode) {
    case -1: // 未知错误
      console.log(errorDescription);
      break;
    case -2: // 从文档服务器加载DocsAPI时出错
      console.log(errorDescription);
      break;
    case -3: // DocsAPI未定义
      console.log(errorDescription);
      break;
  }
};
</script>
```

### Vue项目快速开始

1. **安装依赖**
```bash
npm install @koi-br/office-sdk-wrapper @onlyoffice/document-editor-vue vue
```

2. **在Vue项目中配置**
```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  optimizeDeps: {
    include: ['@onlyoffice/document-editor-vue']
  },
  // 确保可以正确解析.vue文件
  resolve: {
    alias: {
      '@koi-br/office-sdk-wrapper': '@koi-br/office-sdk-wrapper'
    }
  }
})
```

3. **在main.ts中注册组件**
```typescript
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mount('#app')
```

4. **Vue组件使用示例**

在你的Vue组件中：

```vue
<template>
  <div class="vue-office-demo">
    <!-- 正确使用Vue组件 -->
    <OnlyOfficeVueEditor
      :init-config="editorConfig"
      editor-id="demo-editor"
      @document-ready="handleDocumentReady"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
// 正确的导入方式
import OnlyOfficeVueEditor from '@koi-br/office-sdk-wrapper/src/components/OnlyOfficeVueEditor.vue';
import { DocumentType } from '@koi-br/office-sdk-wrapper/vue';

const editorConfig = ref({
  fileId: 'your-file-id',
  appId: 'your-app-id',
  containerSelector: '.office-container',
  isReadOnly: false,
  documentType: DocumentType.WORD,
  documentConfig: {
    title: 'My Document',
    url: 'https://example.com/document.docx'
  }
});

const handleDocumentReady = (event: any) => {
  console.log('文档准备就绪:', event);
};
</script>
```

5. **运行Vue示例**
```bash
# 克隆项目
git clone https://github.com/your-repo/@koi-br/office-sdk-wrapper.git
cd office-sdk-wrapper

# 安装依赖
npm install

# 运行Vue示例
cd examples/vue-example
npm install
npm run dev
```

### 🔄 Vue项目中的提供商切换

```vue
<template>
  <div class="office-app">
    <!-- 提供商选择 -->
    <div class="provider-selector">
      <button 
        @click="switchProvider(OfficeProvider.WPS)"
        :class="{ active: currentProvider === OfficeProvider.WPS }"
      >
        WPS Office
      </button>
      <button 
        @click="switchProvider(OfficeProvider.ONLYOFFICE)"
        :class="{ active: currentProvider === OfficeProvider.ONLYOFFICE }"
      >
        OnlyOffice
      </button>
    </div>
    
    <!-- WPS容器 -->
    <div 
      v-show="currentProvider === OfficeProvider.WPS"
      class="wps-container"
      style="width: 100%; height: 600px;"
    ></div>
    
    <!-- OnlyOffice Vue组件 -->
    <OnlyOfficeVueEditor
      v-show="currentProvider === OfficeProvider.ONLYOFFICE"
      :init-config="onlyOfficeConfig"
      :editor-id="`onlyoffice-${Date.now()}`"
      @document-ready="handleDocumentReady"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch } from 'vue';
// Vue组件从组件路径导入
import OnlyOfficeVueEditor from '@koi-br/office-sdk-wrapper/src/components/OnlyOfficeVueEditor.vue';
// 其他功能从主模块导入
import { 
  createWPSSDK,
  OfficeProvider,
  DocumentType,
  type IOfficeSDK
} from '@koi-br/office-sdk-wrapper';
// Vue适配器从vue模块导入
import { createOnlyOfficeVueSDK } from '@koi-br/office-sdk-wrapper/vue';

const currentProvider = ref(OfficeProvider.WPS);
const currentSDK = ref<IOfficeSDK | null>(null);

const baseConfig = {
  fileId: "your-file-id",
  appId: "your-app-id",
  isReadOnly: false,
  documentType: DocumentType.WORD
};

const onlyOfficeConfig = reactive({
  ...baseConfig,
  containerSelector: ".onlyoffice-container",
  documentConfig: {
    title: 'My Document',
    url: 'https://example.com/document.docx'
  },
  editorConfig: {
    lang: 'zh-CN',
    mode: 'edit' as const
  }
});

const switchProvider = async (provider: OfficeProvider) => {
  // 销毁当前SDK
  if (currentSDK.value) {
    await currentSDK.value.destroy();
    currentSDK.value = null;
  }
  
  currentProvider.value = provider;
  
  // 创建新的SDK
  if (provider === OfficeProvider.WPS) {
    currentSDK.value = await createWPSSDK({
      ...baseConfig,
      containerSelector: ".wps-container",
      token: "your-wps-token"
    });
  }
  // OnlyOffice由Vue组件管理，无需手动创建
};

const handleDocumentReady = (event: any) => {
  console.log('OnlyOffice文档准备就绪:', event);
};
</script>
```

## 🔧 Configuration Management / 配置管理

### 配置验证 / Configuration Validation

```javascript
import { getConfigManager } from 'office-sdk-wrapper';

const manager = getConfigManager();

// 验证配置
const validation = manager.validateConfig({
  fileId: "your-file-id",
  appId: "your-app-id", 
  containerSelector: ".office-container",
  provider: OfficeProvider.WPS
});

if (!validation.isValid) {
  console.error('配置错误:', validation.errors);
  console.warn('配置警告:', validation.warnings);
  console.log('建议:', validation.suggestions);
}
```

### 推荐配置生成 / Recommended Configuration

```javascript
import { getConfigManager, OfficeProvider } from 'office-sdk-wrapper';

const manager = getConfigManager();

// 获取基于场景的推荐
const recommendation = manager.recommendProvider({
  needsAdvancedRevision: true,    // 需要高级修订功能
  needsCollaboration: false,      // 不需要协作功能
  isVueProject: true,            // Vue项目
  hasWPSLicense: false,          // 没有WPS许可证
  documentType: DocumentType.WORD,
  expectedFileSize: 'large'       // 大文件处理
});

console.log(`推荐使用: ${recommendation.provider}`);
console.log(`推荐理由: ${recommendation.reason}`);
recommendation.tips.forEach(tip => console.log(`💡 ${tip}`));
```

### 快速配置生成器 / Quick Config Generator

```javascript
import { createQuickConfig, getConfigManager } from 'office-sdk-wrapper';

// 快速创建WPS配置
const wpsConfig = createQuickConfig(
  {
    fileId: "doc-001",
    appId: "app-001", 
    containerSelector: ".office-container"
  },
  OfficeProvider.WPS,
  {
    isReadOnly: false,
    documentType: DocumentType.WORD
  }
);

// 快速创建OnlyOffice配置
const onlyOfficeConfig = createQuickConfig(
  {
    fileId: "doc-001",
    appId: "app-001",
    containerSelector: ".office-container" 
  },
  OfficeProvider.ONLYOFFICE,
  {
    isReadOnly: false,
    documentType: DocumentType.WORD,
    documentUrl: 'https://example.com/document.docx'
  }
);
```

## 🚨 Error Handling / 错误处理

### 统一错误处理

```javascript
import { createWPSSDK, createOnlyOfficeSDK } from 'office-sdk-wrapper';

try {
  const sdk = await createWPSSDK({
    fileId: "your-file-id",
    appId: "your-app-id",
    containerSelector: ".office-container",
    token: "your-token",
    onError: (error) => {
      // 统一错误处理
      console.error('SDK错误:', error);
      
      // 根据错误类型处理
      if (error.code === 'NETWORK_ERROR') {
        // 网络错误处理
        showNetworkErrorMessage();
      } else if (error.code === 'AUTH_ERROR') {
        // 认证错误处理
        redirectToLogin();
      }
    }
  });
  
  // 使用SDK方法时的错误处理
  try {
    await sdk.insertTextAtCursor('Hello World');
  } catch (methodError) {
    console.error('方法调用失败:', methodError);
    // 具体方法的错误处理
  }
  
} catch (initError) {
  console.error('SDK初始化失败:', initError);
  // 初始化失败的处理
}
```

### 错误重试机制

```javascript
import { createWPSSDK } from 'office-sdk-wrapper';

async function createSDKWithRetry(config, maxRetries = 3) {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      const sdk = await createWPSSDK(config);
      console.log(`SDK初始化成功，第${i + 1}次尝试`);
      return sdk;
    } catch (error) {
      lastError = error;
      console.warn(`第${i + 1}次初始化失败:`, error);
      
      // 如果不是最后一次重试，等待后重试
      if (i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
      }
    }
  }
  
  throw new Error(`SDK初始化失败，已重试${maxRetries}次: ${lastError.message}`);
}

// 使用重试机制
const sdk = await createSDKWithRetry({
  fileId: "your-file-id",
  appId: "your-app-id",
  containerSelector: ".office-container"
});
```

### 📱 响应式设计

```javascript
// 响应式容器大小调整
const setupResponsiveContainer = (sdk) => {
  const resizeObserver = new ResizeObserver(entries => {
    for (let entry of entries) {
      const { width, height } = entry.contentRect;
      
      // 调整编辑器尺寸
      if (sdk.nativeInstance && sdk.nativeInstance.resize) {
        sdk.nativeInstance.resize(width, height);
      }
    }
  });
  
  const container = document.querySelector('.office-container');
  if (container) {
    resizeObserver.observe(container);
  }
  
  return () => resizeObserver.disconnect();
};

// 移动设备优化
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

const config = {
  ...baseConfig,
  editorConfig: {
    ...baseConfig.editorConfig,
    // 移动设备优化
    customization: {
      toolbarNoTabs: isMobile,
      toolbarHideFileName: isMobile,
      compactToolbar: isMobile
    }
  }
};
```