@koi-br/office-sdk-wrapper
v1.0.4
Published
一个统一的办公服务SDK封装库,同时支持 **WPS Office** 和 **OnlyOffice**。
Downloads
18
Readme
office-sdk-wrapper
一个统一的办公服务SDK封装库,同时支持 WPS Office 和 OnlyOffice。
📦 Installation / 安装
npm install @koi-br/office-sdk-wrapperVue项目额外依赖
如果你的项目使用Vue,还需要安装OnlyOffice的Vue组件:
npm install @onlyoffice/document-editor-vue🚀 Quick Start / 快速开始
Using WPS Office / 使用WPS Office
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
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 / 通用方法
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 / 智能配置
使用智能配置管理器,让系统根据你的需求自动推荐最佳提供商:
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 / 基于环境的自动选择
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实例都实现相同的接口,无论底层使用的是哪个办公套件:
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 / 基础配置
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特定配置
interface WPSInitConfig extends BaseInitConfig {
provider: OfficeProvider.WPS;
simple?: boolean; // 简单模式
refreshToken?: (token: string, timeout?: number) => void;
}OnlyOffice Specific Configuration / OnlyOffice特定配置
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示例
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' |
// ✅ 正确的导入方式
// 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';// ❌ 错误的导入方式(不要这样做)
import {
OnlyOfficeVueEditor // 这个导入会失败!
} from '@koi-br/office-sdk-wrapper/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组件
<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项目快速开始
- 安装依赖
npm install @koi-br/office-sdk-wrapper @onlyoffice/document-editor-vue vue- 在Vue项目中配置
// 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'
}
}
})- 在main.ts中注册组件
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')- 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>- 运行Vue示例
# 克隆项目
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项目中的提供商切换
<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
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
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
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 / 错误处理
统一错误处理
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);
// 初始化失败的处理
}错误重试机制
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"
});📱 响应式设计
// 响应式容器大小调整
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
}
}
};