Repository: wytxer/demo-onlyoffice Branch: main Commit: c676d197094c Files: 72 Total size: 68.6 KB Directory structure: gitextract_6eix9r5i/ ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── onlyoffice-server/ │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── nest-cli.json │ ├── package.json │ ├── src/ │ │ ├── app.controller.ts │ │ ├── app.module.ts │ │ ├── document/ │ │ │ ├── document.controller.ts │ │ │ ├── document.dto.ts │ │ │ ├── document.entity.ts │ │ │ ├── document.module.ts │ │ │ └── document.service.ts │ │ ├── main.ts │ │ ├── onlyoffice/ │ │ │ ├── onlyoffice.controller.ts │ │ │ ├── onlyoffice.dto.ts │ │ │ ├── onlyoffice.entity.ts │ │ │ ├── onlyoffice.interface.ts │ │ │ ├── onlyoffice.module.ts │ │ │ └── onlyoffice.service.ts │ │ └── shared/ │ │ ├── config.ts │ │ ├── interceptors/ │ │ │ ├── logger.interceptor.ts │ │ │ └── response.interceptor.ts │ │ └── shared.module.ts │ ├── static/ │ │ ├── docbuilder/ │ │ │ └── test1.docbuilder │ │ ├── plugins/ │ │ │ └── plugin-hello/ │ │ │ ├── config.json │ │ │ ├── index.html │ │ │ ├── main.js │ │ │ └── translations/ │ │ │ └── zh-CN.json │ │ ├── test1.docx │ │ ├── test1.xlsx │ │ ├── test2.docx │ │ ├── test2.xlsx │ │ ├── test3.docx │ │ ├── test4.docx │ │ ├── test5.docx │ │ └── test5.html │ ├── tsconfig.build.json │ └── tsconfig.json └── onlyoffice-vue/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── package.json ├── public/ │ └── index.html ├── src/ │ ├── api/ │ │ ├── onlyoffice.js │ │ └── request.js │ ├── app.vue │ ├── layouts/ │ │ └── block.vue │ ├── main.js │ ├── router/ │ │ ├── index.js │ │ └── routes.js │ ├── store/ │ │ └── index.js │ └── views/ │ ├── home.vue │ └── onlyoffice/ │ ├── document-conversion.vue │ ├── document-custom-config.vue │ ├── document-editor-jwt.vue │ ├── document-editor.vue │ ├── document-plugin.vue │ ├── document-quick-start.vue │ ├── excel-formula.vue │ ├── excel-quick-start.vue │ ├── modules/ │ │ └── onlyoffice-editor.vue │ └── onlyoffice-vue.vue └── vue.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store node_modules /dist /tar # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: .vscode/settings.json ================================================ { "eslint.workingDirectories": [ "./onlyoffice-server", "./onlyoffice-vue" ], "vetur.validation.interpolation": false } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 wytxer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # DEMO ONLYOFFICE 基于 Nest 和 Vue.js 的 Onlyoffice 示例。 > Onlyoffice@7.2 之后的版本默认开启了 JWT 加密,运行仓库例子时请先关闭 JWT 校验。 ## 快速开始 注意,在启动之前,需要将 **onlyoffice-vue/.env** 里面的 `VUE_APP_HOST` 和 **onlyoffice-server/.env** 里面的 `HOST` 字段统一替换成自己的本机 IP。 ```bash # 启动前端 cd onlyoffice-vue # 安装依赖 pnpm install # 启动开发服务 pnpm run dev # 启动后端 cd onlyoffice-server # 安装依赖 pnpm install # 启动开发服务 pnpm run dev ``` ## 相关文章 文章与仓库代码是一体的,强烈建议跟着文章步骤运行例子。 - [在 Vue 中接入 Onlyoffice](https://bszhct.com/2022/08/15/onlyoffice-quick-start/) - [Onlyoffice 二次开发指南,包含自定义 Onlyoffice 配置、使用 JWT 加密 Onlyoffice 配置、Onlyoffice 文档转换等](https://bszhct.com/2022/08/19/onlyoffice-usage/) - [Onlyoffice 插件开发指南](https://bszhct.com/2022/08/20/onlyoffice-plugin/) - [在线 Office 解决方案调研总结](https://bszhct.com/2022/08/14/online-office-summary/) ## 填坑交流群 添加后请备注「oo」 ## License [MIT](/LICENSE) ================================================ FILE: onlyoffice-server/.eslintrc.js ================================================ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', tsconfigRootDir : __dirname, sourceType: 'module', }, plugins: ['@typescript-eslint/eslint-plugin'], extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', ], root: true, env: { node: true, jest: true, }, ignorePatterns: ['.eslintrc.js'], rules: { '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-explicit-any': 'off', }, }; ================================================ FILE: onlyoffice-server/.gitignore ================================================ # compiled output /dist /node_modules # Logs logs *.log npm-debug.log* pnpm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # OS .DS_Store # Tests /coverage /.nyc_output # IDEs and editors /.idea .project .classpath .c9/ *.launch .settings/ *.sublime-workspace # IDE - VSCode .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json ================================================ FILE: onlyoffice-server/.prettierrc ================================================ { "singleQuote": true, "trailingComma": "all" } ================================================ FILE: onlyoffice-server/README.md ================================================ # Onlyoffice Server 基于 nest 的 Onlyoffice 示例。 ## 快速使用 ```bash # 安装依赖 pnpm install # 开发 pnpm run dev # 打包 pnpm run build ``` ## License [MIT](/LICENSE) ================================================ FILE: onlyoffice-server/nest-cli.json ================================================ { "$schema": "https://json.schemastore.org/nest-cli", "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { "plugins": [ { "name": "@nestjs/swagger", "options": { "classValidatorShim": true, "introspectComments": true } } ] } } ================================================ FILE: onlyoffice-server/package.json ================================================ { "name": "onlyoffice-server", "version": "1.0.0", "description": "", "author": "wytxer", "private": true, "license": "MIT", "scripts": { "prebuild": "rimraf dist", "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "dev": "pnpm run start:test", "start": "cross-env NODE_ENV=online nest start", "start:test": "cross-env NODE_ENV=test nest start --watch", "start:debug": "cross-env NODE_ENV=test nest start --debug --watch", "start:online": "cross-env NODE_ENV=online node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { "@nestjs/axios": "^0.1.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.2.0", "@nestjs/core": "^9.0.0", "@nestjs/jwt": "^9.0.0", "@nestjs/platform-express": "^9.0.0", "@nestjs/serve-static": "^3.0.0", "@nestjs/swagger": "^6.0.1", "axios": "^0.27.2", "chalk": "^5.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", "cross-env": "^7.0.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", "swagger-ui-express": "^4.4.0" }, "devDependencies": { "@nestjs/cli": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.4", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", "jest": "28.1.2", "prettier": "^2.3.2", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "28.0.5", "ts-loader": "^9.2.3", "ts-node": "^10.0.0", "tsconfig-paths": "4.0.0", "typescript": "^4.3.5" }, "jest": { "moduleFileExtensions": [ "js", "json", "ts" ], "rootDir": "src", "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": [ "**/*.(t|j)s" ], "coverageDirectory": "../coverage", "testEnvironment": "node" } } ================================================ FILE: onlyoffice-server/src/app.controller.ts ================================================ import { Controller } from '@nestjs/common'; @Controller() export class AppController {} ================================================ FILE: onlyoffice-server/src/app.module.ts ================================================ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; import config from './shared/config'; import { SharedModule } from './shared/shared.module'; import { AppController } from './app.controller'; import { OnlyofficeModule } from './onlyoffice/onlyoffice.module'; import { DocumentModule } from './document/document.module'; @Module({ imports: [ // 导入全局变量配置 ConfigModule.forRoot({ isGlobal: true, expandVariables: true, load: [config], }), // 静态资源服务配置 ServeStaticModule.forRoot({ serveRoot: '/static', rootPath: join(__dirname, '..', 'static'), }), SharedModule, OnlyofficeModule, DocumentModule, ], controllers: [AppController], providers: [], }) export class AppModule {} ================================================ FILE: onlyoffice-server/src/document/document.controller.ts ================================================ import { Controller, Post, Get, Body, Query } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { DocumentForceSaveDto, DocumentInfoDto } from './document.dto'; import { DocumentInfo } from './document.entity'; import { DocumentService } from './document.service'; @ApiTags('Document') @Controller({ path: 'document', version: '1', }) export class DocumentController { constructor(private documentService: DocumentService) {} @Post('forceSave') @ApiOperation({ summary: '强制保存文档', description: '通过调用 Onlyoffice 提供的指令接口间接保存文件,最终文件的报错操作还是在 editorConfig.callbackUrl 所指定的接口里面完成的', }) async forceSave(@Body() body: DocumentForceSaveDto): Promise { return await this.documentService.forceSave(body); } @Get('documentInfo') @ApiOperation({ summary: '获取文档信息', description: '仅构造 Onlyoffice 文档编辑器显示和保存需要的必要信息', }) async documentInfo(@Query() query: DocumentInfoDto): Promise { return await this.documentService.documentInfo(query); } @Get('excelInfo') @ApiOperation({ summary: '获取表格信息', description: '仅构造 Onlyoffice 表格编辑器显示和保存需要的必要信息', }) async excelInfo(@Query() query: DocumentInfoDto): Promise { return await this.documentService.excelInfo(query); } } ================================================ FILE: onlyoffice-server/src/document/document.dto.ts ================================================ import { IsString, IsNumber, IsNotEmpty, IsIn } from 'class-validator'; /** * 文档强制保存请求参数 */ export class DocumentForceSaveDto { /** * 业务 id */ @IsNumber() id: string; /** * 文档标识符 */ @IsString() key: string; /** * 使用 JWT 加密文档参数,默认不加密,需要配合 Onlyoffice 的 secret 配置使用。 */ @IsString() @IsIn(['y', 'n']) useJwtEncrypt?: string = 'n'; } /** * 获取文档信息请求参数 */ export class DocumentInfoDto { /** * 文档标识符(为了获取本地缓存的文档文件) * @example test1.docx */ @IsNotEmpty() @IsString() key: string; /** * 使用 JWT 加密文档参数,默认不加密,需要配合 Onlyoffice 的 secret 配置使用。 */ @IsString() @IsIn(['y', 'n']) useJwtEncrypt?: string = 'n'; /** * 使用插件。默认不返回插件配置 */ @IsString() @IsIn(['y', 'n']) usePlugin?: string = 'n'; } ================================================ FILE: onlyoffice-server/src/document/document.entity.ts ================================================ import { IsString, IsNumber, IsObject } from 'class-validator'; import { OnlyofficeEditorConfig } from '../onlyoffice/onlyoffice.entity'; export class DocumentInfo { /** * 业务 id */ @IsNumber() id?: number; /** * 备注 */ @IsNumber() remarks?: string; /** * 编辑器配置 */ @IsObject() editorConfig: OnlyofficeEditorConfig; } export class DocumentForceSave { /** * 状态码 */ @IsNumber() code: number; /** * 消息 */ @IsString() message?: string; } ================================================ FILE: onlyoffice-server/src/document/document.module.ts ================================================ import { Module } from '@nestjs/common'; import { DocumentController } from './document.controller'; import { DocumentService } from './document.service'; import { OnlyofficeService } from '../onlyoffice/onlyoffice.service'; @Module({ controllers: [DocumentController], providers: [DocumentService, OnlyofficeService], }) export class DocumentModule {} ================================================ FILE: onlyoffice-server/src/document/document.service.ts ================================================ import { ConfigService } from '@nestjs/config'; import { Injectable, HttpStatus, HttpException } from '@nestjs/common'; import { OnlyofficeService } from '../onlyoffice/onlyoffice.service'; import { DocumentForceSaveDto, DocumentInfoDto } from './document.dto'; import { DocumentForceSave, DocumentInfo } from './document.entity'; @Injectable() export class DocumentService { constructor( private readonly config: ConfigService, private onlyofficeService: OnlyofficeService, ) {} async forceSave(body: DocumentForceSaveDto): Promise { // 1、保存业务数据 // 2、调用 Onlyoffice 的强制保存,实际业务中可能还有更多的业务操作,可根据实际情况删改 const { id: userdata, key, useJwtEncrypt } = body; const data = await this.onlyofficeService.forceSave({ key, // 将业务参数传给 Onlyoffice 服务,当回调里面存在多个请求时,标识符将有助于区分特定请求 userdata, useJwtEncrypt, }); // 保存成功 if (data.error === 0) { return null; } throw new HttpException(data, HttpStatus.OK); } async documentInfo(query: DocumentInfoDto): Promise { const editorConfig = this.onlyofficeService.editorDefaultConfig(); // 添加文档 editorConfig.document = { ...editorConfig.document, fileType: 'docx', key: query.key, url: `${this.config.get('domain')}/static/${query.key}`, title: '测试文档.docx', }; // 添加用户信息 editorConfig.editorConfig.user = { group: '技术部', id: 'wytxer', name: '程序员未央', }; // 添加插件配置 if (query.usePlugin === 'y') { editorConfig.editorConfig.plugins = { autostart: [], pluginsData: [ `${this.config.get( 'domain', )}/static/plugins/plugin-hello/config.json`, ], }; } // 加密编辑器参数 if (query.useJwtEncrypt === 'y') { this.onlyofficeService.signJwt(editorConfig); } return { id: 1, remarks: '业务字段', editorConfig, }; } async excelInfo(query: DocumentInfoDto): Promise { const editorConfig = this.onlyofficeService.editorDefaultConfig(); // 添加文档 editorConfig.document = { ...editorConfig.document, fileType: 'xlsx', key: query.key, url: `${this.config.get('domain')}/static/${query.key}`, title: '测试表格.xlsx', }; // 修改文档宽度 editorConfig.width = '100%'; // 修改编辑器类型 editorConfig.documentType = 'cell'; // 添加用户信息 editorConfig.editorConfig.user = { group: '技术部', id: 'wytxer', name: '程序员未央', }; // 加密编辑器参数 if (query.useJwtEncrypt === 'y') { this.onlyofficeService.signJwt(editorConfig); } return { id: 1, remarks: '业务字段', editorConfig, }; } } ================================================ FILE: onlyoffice-server/src/main.ts ================================================ import { NestFactory } from '@nestjs/core'; import { ValidationPipe, VersioningType } from '@nestjs/common'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; import { ResponseInterceptor } from './shared/interceptors/response.interceptor'; import { LoggingInterceptor } from './shared/interceptors/logger.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); // 设置接口前缀 app.setGlobalPrefix(process.env.API_PREFIX); app.useGlobalPipes( new ValidationPipe({ // 跳过验证对象中值为 null 或 undefined 的属性的验证。完整配置文档参见:https://docs.nestjs.cn/9/techniques?id=%e9%aa%8c%e8%af%81 skipNullProperties: true, stopAtFirstError: true, transform: true, }), ); // 添加全局数据响应拦截器 app.useGlobalInterceptors( new ResponseInterceptor(), new LoggingInterceptor(), ); // 版本号配置 app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1', }); // 接口文档配置 const isTest = process.env.NODE_ENV === 'test'; if (isTest) { const options = new DocumentBuilder() .setTitle('Demo Onlyoffice 接口文档') .setVersion('1.0.0') .addTag('Onlyoffice') .addTag('Document') .build(); const document = SwaggerModule.createDocument(app, options); SwaggerModule.setup('docs', app, document); } // 启动服务 await app.listen(process.env.PORT); } bootstrap(); ================================================ FILE: onlyoffice-server/src/onlyoffice/onlyoffice.controller.ts ================================================ import { Controller, Post, Body, HttpCode, HttpStatus } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { OnlyofficeCallbackDto } from './onlyoffice.dto'; import { OnlyofficeCallback } from './onlyoffice.entity'; import { OnlyofficeService } from './onlyoffice.service'; @ApiTags('Onlyoffice') @Controller({ path: 'onlyoffice', version: '1', }) export class OnlyofficeController { constructor(private onlyofficeService: OnlyofficeService) {} @Post('callback') @ApiOperation({ summary: '文档回调地址', description: '对应 Onlyoffice 的 editorConfig.callbackUrl 字段', }) // 这里表示成功的 statusCode 状态不能返回 201,否则会报错「这份文件无法保存。请检查连接设置或联系您的管理员」,因为在 Onlyoffice 如果 statusCode 不等于 200 认为是失败 @HttpCode(HttpStatus.OK) async callback( // @Body() body: OnlyofficeCallbackDto, @Body() body: any, ): Promise { return await this.onlyofficeService.callback(body); } } ================================================ FILE: onlyoffice-server/src/onlyoffice/onlyoffice.dto.ts ================================================ import { IsString, IsNumber, IsObject, IsArray, IsDefined, IsIn, } from 'class-validator'; export class IActions { /** * 操作类型 */ @IsNumber() type: number; /** * 用户 id */ @IsNumber() userid?: string | number; } export class IUser { /** * 用户 id */ @IsNumber() id?: string | number; /** * 用户名,里面包含了组织信息和用户名 * @example 技术部 程序员未央 */ @IsString() name?: string; } class IChange { /** * 创建时间 */ @IsString() created?: string; /** * 用户信息 */ @IsObject() user?: IUser; } export class IHistory { /** * 当前 Onlyoffice 服务版本号 */ @IsString() serverVersion?: string; /** * 历史记录信息 */ @IsArray() changes?: IChange[]; } /** * 回调请求参数,文档地址:https://api.onlyoffice.com/editors/callback */ export class OnlyofficeCallbackDto { /** * 用户与文档的交互状态。0:用户断开与文档共同编辑的连接;1:新用户连接到文档共同编辑;2:用户单击强制保存按钮 */ @IsArray() actions?: IActions[] = null; /** * 字段已在 4.2 后版本废弃,请使用 history 代替 */ @IsObject() changeshistory?: IHistory = null; /** * 文档变更的历史记录,仅当 status 等于 2 或者 3 时该字段才有值。其中的 serverVersion 字段也是 refreshHistory 方法的入参 */ @IsObject() history?: IHistory = null; /** * 文档编辑的元数据信息,用来跟踪显示文档更改记录,仅当 status 等于 2 或者 2 时该字段才有值。该字段也是 setHistoryData(显示与特定文档版本对应的更改,类似 Git 历史记录)方法的入参 */ @IsString() changesurl?: string = null; /** * url 字段下载的文档扩展名,文件类型默认为 OOXML 格式,如果启用了 assemblyFormatAsOrigin(https://api.onlyoffice.com/editors/save#assemblyFormatAsOrigin) 服务器设置则文件以原始格式保存 */ @IsString() filetype?: string = null; /** * 文档强制保存类型。0:对命令服务(https://api.onlyoffice.com/editors/command/forcesave)执行强制保存;1:每次保存完成时都会执行强制保存请求,仅设置 forcesave 等于 true 时生效;2:强制保存请求由计时器使用服务器中的设置执行。该字段仅 status 等于 7 或者 7 时才有值 */ @IsNumber() forcesavetype?: number = null; /** * 文档标识符,类似 id,在 Onlyoffice 服务内部唯一 */ @IsDefined() @IsString() key: string; /** * 文档状态。1:文档编辑中;2:文档已准备好保存;3:文档保存出错;4:文档没有变化无需保存;6:正在编辑文档,但保存了当前文档状态;7:强制保存文档出错 */ @IsNumber() @IsIn([1, 2, 3, 4, 6, 7]) status?: number = null; /** * 已编辑文档的链接,可以通过它下载到最新的文档,仅当 status 等于 2、3、6 或 7 时该字段才有值 */ @IsString() url?: string = null; /** * 自定义参数,对应指令服务的 userdata 字段 */ @IsObject() userdata?: object = null; /** * 打开文档进行编辑的用户标识列表,当文档被修改时,该字段将返回最后编辑文档的用户标识符,当 status 字段等于 2 或者 6 时有值 */ @IsArray() users?: string[] = null; /** * 最近保存时间 */ @IsString() lastsave?: string = null; /** * 加密令牌 */ @IsString() token?: string = null; } /** * 强制保存请求参数 */ export class OnlyofficeForceSaveDto { /** * 文档标识符 */ @IsString() key: string; /** * 用户自定义的数据 */ @IsString() userdata?: string = null; /** * 使用 JWT 加密文档参数,默认不加密,需要配合 Onlyoffice 的 secret 配置使用。 */ @IsString() @IsIn(['y', 'n']) useJwtEncrypt?: string = 'n'; } ================================================ FILE: onlyoffice-server/src/onlyoffice/onlyoffice.entity.ts ================================================ import { IsString, IsNumber, IsObject, IsIn } from 'class-validator'; class IFeatures { /** * 是否启用拼写检查 */ spellcheck?: boolean = false; } class ICustomization { /** * 强制保存 */ forcesave?: boolean = true; /** * 自定义参数 */ features?: IFeatures = new IFeatures(); } class IPlugins { /** * 需要自动加载的插件列表 */ autostart: string[] = []; /** * 自定义的插件列表 */ pluginsData: string[] = []; } class IPermission { /** * 是否启用评论 */ comment?: boolean = false; /** * 是否启用下载 */ download?: boolean = true; /** * 是否启用编辑 */ edit?: boolean = true; /** * 是否启用导出 */ print?: boolean = true; /** * 是否启用预览 */ review?: boolean = true; } class IDocument { /** * 文件类型 */ fileType = 'docx'; /** * 文档标识符 */ key: string; /** * 文档地址,绝对路径 */ url: string; /** * 文档标题 */ title: string; /** * 文档权限配置 */ permissions: IPermission = new IPermission(); } class IEditorConfig { /** * 回调地址 */ callbackUrl: string = process.env.ONLYOFFICE_CALLBACK; /** * 语言 */ lang?: string = 'zh-CN'; /** * 用户信息 */ user: object = {}; /** * 模板列表 */ templates?: object[] = []; /** * 自定义配置。字段相关配置详解:https://api.onlyoffice.com/editors/config/editor/ */ customization?: ICustomization = new ICustomization(); /** * 插件列表 */ plugins?: IPlugins = new IPlugins(); } export class OnlyofficeCallback { /** * 返回 0 表示成功,否则表示失败 */ @IsNumber() error: number; } export class OnlyofficeForceSave { /** * 状态码 */ @IsNumber() code: number; /** * 状态码 */ @IsNumber() error: number; /** * 消息 */ @IsString() message?: string; } /** * 编辑器配置项,完整配置项参见:https://api.onlyoffice.com/editors/config/ */ export class OnlyofficeEditorConfig { /** * 编辑器宽度 */ @IsNumber() width?: number | string = 1200; /** * 编辑器高度 */ @IsNumber() height?: number | string = 800; /** * 文档类型。word:文档,cell:表格,slide:PPT */ @IsString() @IsIn(['word', 'cell', 'slide']) documentType = 'word'; /** * 文档配置 */ @IsObject() document: IDocument = new IDocument(); /** * 编辑器配置 */ @IsObject() editorConfig: IEditorConfig = new IEditorConfig(); /** * 编辑器加密令牌,开启加密时有值 */ @IsString() token?: string; } ================================================ FILE: onlyoffice-server/src/onlyoffice/onlyoffice.interface.ts ================================================ enum ErrorCode { NoError = 0, NoSecret = 1, ErrorCallbackUrl = 2, ServerError = 3, NoChange = 4, ErrorCommand = 5, InvalidToken = 6, } export interface IOnlyofficeCommand { error: ErrorCode; } export interface IOnlyofficeForceSave { c?: string; key?: string; userdata?: string; token?: string; } ================================================ FILE: onlyoffice-server/src/onlyoffice/onlyoffice.module.ts ================================================ import { Module } from '@nestjs/common'; import { OnlyofficeController } from './onlyoffice.controller'; import { OnlyofficeService } from './onlyoffice.service'; @Module({ controllers: [OnlyofficeController], providers: [OnlyofficeService], imports: [], }) export class OnlyofficeModule {} ================================================ FILE: onlyoffice-server/src/onlyoffice/onlyoffice.service.ts ================================================ import { HttpService } from '@nestjs/axios'; import { AxiosResponse } from 'axios'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { createWriteStream, WriteStream } from 'fs'; import { join } from 'path'; import { Injectable, Logger, HttpStatus, HttpException } from '@nestjs/common'; import { OnlyofficeCallbackDto, OnlyofficeForceSaveDto, } from './onlyoffice.dto'; import { OnlyofficeCallback, OnlyofficeForceSave, OnlyofficeEditorConfig, } from './onlyoffice.entity'; import { IOnlyofficeCommand, IOnlyofficeForceSave, } from './onlyoffice.interface'; @Injectable() export class OnlyofficeService { constructor( private readonly request: HttpService, private readonly config: ConfigService, private readonly jwt: JwtService, ) {} private readonly logger = new Logger(OnlyofficeService.name); async callback(body: OnlyofficeCallbackDto): Promise { const { url, status } = body; // 正在编辑文档但保存了当前文档状态 if (status === 6) { try { // 根据地址下载文档文件 const file: AxiosResponse = await this.request.axiosRef.get(url, { responseType: 'stream', }); const stream: WriteStream = createWriteStream( join(this.config.get('staticPath'), body.key), ); file.data.pipe(stream); } catch (error) { this.logger.error(error); // 返回 Onlyoffice 服务认识的报错 throw new HttpException({ error: 7 }, HttpStatus.OK); } } else if (status === 7) { // 强制保存文档出错 throw new HttpException({ error: status }, HttpStatus.OK); } // 默认返回成功的状态 throw new HttpException({ error: 0 }, HttpStatus.OK); } // 强制保存文档 async forceSave(body: OnlyofficeForceSaveDto): Promise { const { key, userdata, useJwtEncrypt } = body; let newBody: IOnlyofficeForceSave = { c: 'forcesave', key, userdata, }; // 如果是使用了 JWT 加密,重新组装请求参数 if (useJwtEncrypt === 'y') { newBody = { token: this.jwt.sign(newBody, { secret: this.config.get('onlyoffice.secret'), }), }; } const saveState: IOnlyofficeCommand = await this.request.axiosRef .post(this.config.get('onlyoffice.commandUrl'), newBody) .then((res) => res.data); // 组装返回值 const data: OnlyofficeForceSave = { error: saveState.error, code: 602, }; // 保存成功 if (saveState.error === 0) { data.code = 0; } else if (saveState.error === 4) { // 文档未改变无需保存 data.message = '文档未改变无需保存'; } else { // 文档保存失败 data.message = '文档保存失败'; } return data; } // 编辑器默认配置 editorDefaultConfig(): OnlyofficeEditorConfig { const { ...defaultConfig } = new OnlyofficeEditorConfig(); return defaultConfig; } // 以 JWT 加密方式签名编辑器配置 signJwt(editorConfig: OnlyofficeEditorConfig): OnlyofficeEditorConfig { editorConfig.token = this.jwt.sign(editorConfig, { secret: this.config.get('onlyoffice.secret'), }); return editorConfig; } } ================================================ FILE: onlyoffice-server/src/shared/config.ts ================================================ import { join } from 'path'; export default () => ({ isTest: process.env.NODE_ENV === 'test', isOnline: process.env.NODE_ENV === 'online', port: process.env.PORT, apiPrefix: process.env.API_PREFIX, domain: process.env.DOMAIN, staticPath: join(process.cwd(), '/static'), onlyoffice: { secret: process.env.ONLYOFFICE_SECRET, domain: process.env.ONLYOFFICE_DOMAIN, commandUrl: process.env.ONLYOFFICE_COMMAND_URL, callback: process.env.ONLYOFFICE_CALLBACK, }, }); ================================================ FILE: onlyoffice-server/src/shared/interceptors/logger.interceptor.ts ================================================ import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor, } from '@nestjs/common'; import { Request, Response } from 'express'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { private readonly logger: Logger = new Logger(LoggingInterceptor.name); intercept(context: ExecutionContext, next: CallHandler): Observable { const request: Request = context.switchToHttp().getRequest(); const { method, url, body, headers } = request; // 输出请求类型和请求参数等信息 this.logger.log( `Request {${method.toUpperCase()}, ${url}} ${JSON.stringify( body, )} ${JSON.stringify(headers)}`, ); return next.handle().pipe( tap({ next: (data: unknown): void => this.logResponse(data, context), }), ); } // 输出响应日志 private logResponse(data: unknown, context: ExecutionContext): void { const request: Request = context.switchToHttp().getRequest(); const response: Response = context.switchToHttp().getResponse(); this.logger.log( `Response {${response.statusCode}, ${request.method.toUpperCase()}, ${ request.url }} ${JSON.stringify(data)}`, ); } } ================================================ FILE: onlyoffice-server/src/shared/interceptors/response.interceptor.ts ================================================ import { Injectable, NestInterceptor, ExecutionContext, CallHandler, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface Response { data: T; } @Injectable() export class ResponseInterceptor implements NestInterceptor> { intercept( context: ExecutionContext, next: CallHandler, ): Observable> { return next.handle().pipe(map((data) => ({ code: 0, data }))); } } ================================================ FILE: onlyoffice-server/src/shared/shared.module.ts ================================================ import { Module, Global } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { JwtModule } from '@nestjs/jwt'; @Global() @Module({ imports: [HttpModule, JwtModule], exports: [HttpModule, JwtModule], providers: [], }) export class SharedModule {} ================================================ FILE: onlyoffice-server/static/docbuilder/test1.docbuilder ================================================ builder.CreateFile("docx") var oDocument = Api.GetDocument() // 添加标题 var oParagraph = oDocument.GetElement(0) oParagraph.AddText("标题") oParagraph.SetJc("center") oParagraph.SetFontSize(40) oParagraph.SetFontFamily("Arial") // 添加正文 oParagraph = Api.CreateParagraph() oParagraph.AddText("\n\n正文内容。\n正文内容。\n正文内容。\n\n\n此致\n") oParagraph.SetSpacingLine(400, "auto") oParagraph.SetFontSize(28) oParagraph.SetFontFamily("Arial") oDocument.Push(oParagraph) // 添加变量 var inlineContent = Api.CreateInlineLvlSdt() inlineContent.SetTag("var-customName") content = Api.CreateRun() content.AddText("自定义变量名称") content.SetFontSize(28) content.SetFontFamily("Arial") content.SetHighlight(255, 255, 0) inlineContent.AddElement(content, 0) oParagraph.AddInlineLvlSdt(inlineContent) // 添加落款 oParagraph = Api.CreateParagraph() oParagraph.AddText("落款:") oParagraph.SetSpacingLine(400, "auto") oParagraph.SetJc("right") oParagraph.SetFontSize(28) oParagraph.SetFontFamily("Arial") // 添加落款名称 inlineContent = Api.CreateInlineLvlSdt() inlineContent.SetTag("var-signName") content = Api.CreateRun() content.AddText("落款名称") content.SetFontSize(28) content.SetFontFamily("Arial") content.SetHighlight(255, 255, 0) inlineContent.AddElement(content, 0) oParagraph.AddInlineLvlSdt(inlineContent) oDocument.Push(oParagraph) // 添加日期 oParagraph = Api.CreateParagraph() oParagraph.SetSpacingLine(400, "auto") oParagraph.SetJc("right") inlineContent = Api.CreateInlineLvlSdt() inlineContent.SetTag("var-updatedTime") content = Api.CreateRun() content.AddText("当前日期") content.SetFontSize(28) content.SetFontFamily("Arial") content.SetHighlight(255, 255, 0) inlineContent.AddElement(content, 0) oParagraph.AddInlineLvlSdt(inlineContent) oDocument.Push(oParagraph) builder.SaveFile("docx", "test1.docx") builder.CloseFile() ================================================ FILE: onlyoffice-server/static/plugins/plugin-hello/config.json ================================================ { "name": "Hello", "guid": "asc.{11700c35-1fdb-4e37-9edb-b31637139601}", "variations": [ { "description": "Hello Word", "url": "index.html", "icons": [ "icon.png", "icon@2x.png" ], "isViewer": false, "EditorsSupport": [ "word" ], "isVisual": true, "isModal": false, "isInsideMode": true, "initDataType": "none", "initData": "", "isUpdateOleOnResize": false, "buttons": [], "events" : [ "onClick" ] } ] } ================================================ FILE: onlyoffice-server/static/plugins/plugin-hello/index.html ================================================ ================================================ FILE: onlyoffice-server/static/plugins/plugin-hello/main.js ================================================ /* eslint-disable */ (function(window, undefined) { window.Asc.plugin.init = function(initData) { console.log('插件开始初始化') console.log(window) var me = this $('#addText').click(function() { me.callCommand(function() { try { // 获取文档对象 var oDocument = Api.GetDocument() console.log('文档对象') console.log(oDocument) // 生成一个新的段落对象 var oParagraph = Api.CreateParagraph() // 往段落里面添加一个字符串文本 oParagraph.AddText('Hello world') // 最后往文档里面添加一个段落对象 oDocument.Push(oParagraph) } catch (error) { console.error(error) } }, false, true, function () { console.log('操作成功') }) }) // 在插件 iframe 之外释放鼠标按钮时调用的函数 window.Asc.plugin.onExternalMouseUp = function() { var event = document.createEvent('MouseEvents') event.initMouseEvent('mouseup', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null) document.dispatchEvent(event) } window.Asc.plugin.button = function(id) { // 被中断或关闭窗口 if (id === -1) { this.executeCommand('close', '') } } } })(window, undefined) ================================================ FILE: onlyoffice-server/static/plugins/plugin-hello/translations/zh-CN.json ================================================ {} ================================================ FILE: onlyoffice-server/static/test5.html ================================================ 

标题

内容

内容

内容

内容内容内容内容内容内容内容,内容内容内容内容内容内容,内容内容内容内容内容内容内容内容内容。内容内容内容内容内容内容内容。内容内容内容内容内容内容内容。内容内容内容内容内容内容内容内容内容,内容内容内容内容内容。

内容

 

内容

内容

内容

 

================================================ FILE: onlyoffice-server/tsconfig.build.json ================================================ { "extends": "./tsconfig.json", "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] } ================================================ FILE: onlyoffice-server/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "target": "es2017", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", "incremental": true, "skipLibCheck": true, "strictNullChecks": false, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false } } ================================================ FILE: onlyoffice-vue/.editorconfig ================================================ [*.{js,jsx,ts,tsx,vue}] indent_style = space indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: onlyoffice-vue/.eslintignore ================================================ lib/ dist/ ================================================ FILE: onlyoffice-vue/.eslintrc.js ================================================ module.exports = { root: true, env: { node: true }, extends: [ 'plugin:vue/essential', '@vue/standard' ], parserOptions: { parser: 'babel-eslint' }, rules: { 'no-unused-vars': 'warn', 'vue/no-unused-components': 'warn', 'no-trailing-spaces': 'warn', 'import/newline-after-import': 'error', 'vue/mustache-interpolation-spacing': 'warn', 'vue/no-multi-spaces': 'warn' } } ================================================ FILE: onlyoffice-vue/.gitignore ================================================ .DS_Store node_modules /dist package-lock.json yarn.lock # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: onlyoffice-vue/LICENSE ================================================ MIT License Copyright (c) 2022 wytxer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: onlyoffice-vue/README.md ================================================ # Onlyoffice Vue 基于 Vue.js 的 Onlyoffice 示例。 ### 快速使用 ```bash # 安装依赖 pnpm install # 开发 pnpm run dev # 打包 pnpm run build ``` ## License [MIT](/LICENSE) ================================================ FILE: onlyoffice-vue/babel.config.js ================================================ module.exports = { presets: [ '@vue/cli-plugin-babel/preset' ], plugins: [ [ 'import', { libraryName: 'ant-design-vue', libraryDirectory: 'es', style: true } ] ] } ================================================ FILE: onlyoffice-vue/package.json ================================================ { "name": "onlyoffice-vue", "version": "1.0.0", "description": "", "private": true, "scripts": { "dev": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "@wytxer/style-utils": "^1.0.2", "ant-design-vue": "^1.7.8", "axios": "^0.27.2", "core-js": "^3.6.5", "lodash.merge": "^4.6.2", "onlyoffice-vue": "^1.0.1", "vue": "^2.6.14", "vue-router": "^3.5.3", "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-plugin-router": "~4.5.0", "@vue/cli-plugin-vuex": "~4.5.0", "@vue/cli-service": "~4.5.0", "@vue/eslint-config-standard": "^5.1.2", "babel-eslint": "^10.1.0", "babel-plugin-import": "^1.13.5", "eslint": "^6.7.2", "eslint-plugin-import": "^2.20.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.0", "eslint-plugin-vue": "^6.2.2", "less": "^3.13.1", "less-loader": "^4.1.0", "lint-staged": "^9.5.0", "vue-template-compiler": "^2.6.14" }, "engines": { "node": ">=10" }, "license": "MIT", "homepage": "https://github.com/wytxer/demo-onlyoffice/#readme", "keywords": [ "onlyoffice", "demo", "office", "vue", "nestjs", "docx" ], "author": { "name": "wytxer", "url": "https://github.com/wytxer" }, "repository": { "type": "git", "url": "git@github.com:wytxer/demo-onlyoffice.git" }, "bugs": { "url": "https://github.com/wytxer/demo-onlyoffice/issues" }, "browserslist": [ "> 1%", "last 2 versions", "not dead" ], "gitHooks": { "pre-commit": "lint-staged" }, "lint-staged": { "*.{js,jsx,vue}": [ "vue-cli-service lint", "git add" ] } } ================================================ FILE: onlyoffice-vue/public/index.html ================================================ <%= htmlWebpackPlugin.options.title %>
================================================ FILE: onlyoffice-vue/src/api/onlyoffice.js ================================================ import request from './request' // Onlyoffice 文档转换接口地址 export function onlyofficeConversion (data) { return request({ method: 'post', url: process.env.VUE_APP_ONLYOFFICE_CONVERT, data }) } // Onlyoffice 文档构建接口地址 export function onlyofficeBuilder (data) { return request({ method: 'post', url: process.env.VUE_APP_ONLYOFFICE_DOCBUILDER, data }) } // 保存文档信息 export function forceSaveDocumentInfo (data) { return request({ method: 'post', url: '/api/v1/document/forceSave', data }) } // 获取文档信息 export function queryDocumentInfo (params) { return request({ method: 'get', url: '/api/v1/document/documentInfo', params }) } // 获取表格信息 export function queryExcelInfo (params) { return request({ method: 'get', url: '/api/v1/document/excelInfo', params }) } ================================================ FILE: onlyoffice-vue/src/api/request.js ================================================ import axios from 'axios' const request = axios.create({ timeout: 60000, baseURL: '' }) // 请求拦截器 request.interceptors.request.use(config => config) // 响应拦截器 request.interceptors.response.use(res => res.data) export default request ================================================ FILE: onlyoffice-vue/src/app.vue ================================================ ================================================ FILE: onlyoffice-vue/src/layouts/block.vue ================================================ ================================================ FILE: onlyoffice-vue/src/main.js ================================================ import Vue from 'vue' import { ConfigProvider, Button, Skeleton, message } from 'ant-design-vue' import App from '@/app.vue' import router from '@/router' import '@wytxer/style-utils/lib/common.less' import 'ant-design-vue/dist/antd.less' Vue.use(ConfigProvider) Vue.use(Button) Vue.use(Skeleton) Vue.prototype.$message = message Vue.config.productionTip = false new Vue({ router, render: h => h(App) }).$mount('#app') ================================================ FILE: onlyoffice-vue/src/router/index.js ================================================ import Vue from 'vue' import VueRouter from 'vue-router' import routes from './routes' Vue.use(VueRouter) const router = new VueRouter({ base: process.env.BASE_URL, mode: 'history', routes }) export default router ================================================ FILE: onlyoffice-vue/src/router/routes.js ================================================ import LayoutBlock from '@/layouts/block.vue' const routes = [{ path: '/', redirect: '/home' }, { path: '/home', component: () => import(/* webpackChunkName: "home" */ '@/views/home.vue'), meta: { title: '首页' } }, { path: '/onlyoffice', component: LayoutBlock, children: [{ path: 'document', component: LayoutBlock, meta: { title: '文档编辑器' }, children: [{ path: 'quick-start', component: () => import(/* webpackChunkName: "document-quick-start" */ '@/views/onlyoffice/document-quick-start.vue'), meta: { title: '快速接入' } }, { path: 'custom-config', component: () => import(/* webpackChunkName: "document-custom-config" */ '@/views/onlyoffice/document-custom-config.vue'), meta: { title: '自定义 onlyoffice 配置' } }, { path: 'editor', component: () => import(/* webpackChunkName: "document-editor" */ '@/views/onlyoffice/document-editor.vue'), meta: { title: '接口获取配置项' } }, { path: 'editor-jwt', component: () => import(/* webpackChunkName: "document-editor-jwt" */ '@/views/onlyoffice/document-editor-jwt.vue'), meta: { title: '开启 JWT 加密' } }, { path: 'plugin', component: () => import(/* webpackChunkName: "document-plugin" */ '@/views/onlyoffice/document-plugin.vue'), meta: { title: '插件' } }, { path: 'conversion', component: () => import(/* webpackChunkName: "document-conversion" */ '@/views/onlyoffice/document-conversion.vue'), meta: { title: '文档转换' } }, { path: 'onlyoffice-vue', component: () => import(/* webpackChunkName: "onlyoffice-vue" */ '@/views/onlyoffice/onlyoffice-vue.vue'), meta: { title: '使用 onlyoffice-vue 组件' } }] }, { path: 'excel', component: LayoutBlock, meta: { title: '表格编辑器' }, children: [{ path: 'quick-start', component: () => import(/* webpackChunkName: "excel-quick-start" */ '@/views/onlyoffice/excel-quick-start.vue'), meta: { title: '快速接入' } }, { path: 'formula', component: () => import(/* webpackChunkName: "excel-formula" */ '@/views/onlyoffice/excel-formula.vue'), meta: { title: '自动公式' } }] }] }] export default routes ================================================ FILE: onlyoffice-vue/src/store/index.js ================================================ import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules: {} }) ================================================ FILE: onlyoffice-vue/src/views/home.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/document-conversion.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/document-custom-config.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/document-editor-jwt.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/document-editor.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/document-plugin.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/document-quick-start.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/excel-formula.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/excel-quick-start.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/modules/onlyoffice-editor.vue ================================================ ================================================ FILE: onlyoffice-vue/src/views/onlyoffice/onlyoffice-vue.vue ================================================ ================================================ FILE: onlyoffice-vue/vue.config.js ================================================ /** * vue.config.js * https://cli.vuejs.org/zh/config/ */ // 默认的接口前缀,在 .env 中设置 const apiPrefix = `^${process.env.VUE_APP_API_PREFIX}` // Onlyoffice 接口前缀,在 .env 中设置 const onlyofficeApiPrefix = `^${process.env.VUE_APP_ONLYOFFICE_API_PREFIX}` module.exports = { css: { loaderOptions: { less: { modifyVars: {}, javascriptEnabled: true } } }, devServer: { port: process.env.VUE_APP_PORT, proxy: { [apiPrefix]: { target: 'http://127.0.0.1:3000', ws: false, changeOrigin: true, logLevel: 'debug' }, [onlyofficeApiPrefix]: { target: 'http://127.0.0.1:8701', ws: false, changeOrigin: true, pathRewrite: { [onlyofficeApiPrefix]: '' } } } }, chainWebpack: config => {} }