# WPS 加载项开发 (`WPS Add-in`) 完整指南 ## 概述 WPS 加载项是一套基于 Web 技术用来扩展 WPS Office 应用程序的解决方案。每个加载项本质上对应一个网页,通过调用网页中的 JavaScript 方法来完成功能逻辑。底层基于 **Chromium 开源浏览器项目**,开发者无需关注浏览器兼容问题。 **核心特性**: - **标准化集成**:不影响 JavaScript 语言特性,网页运行效果与浏览器中完全一致 - **低学习成本**:接口设计符合 JavaScript 语法规范,API 风格与 VBA 一致 - **TypeScript 支持**:`wps-jsapi` 依赖包提供全部接口的 TypeScript 描述,支持 VSCode 代码联想 - **跨平台**:支持 Windows / Linux 操作系统 - **灵活部署**:支持 Publish 模式、jsplugins.xml 模式和动态传递模式 **运行环境要求**: | 项目 | 要求 | |------|------| | WPS Office | 企业版推荐;个人版需 >= 12.1.0.16910 | | Node.js | 支持至 V20.15 | | 编辑器 | VSCode(推荐) | ```bash # 安装开发工具包 npm install -g wpsjs # 安装 TypeScript 类型定义(可选,用于代码提示) npm install --save-dev wps-jsapi ``` ## 架构组成 WPS 加载项由**自定义功能区(Ribbon)**和**网页部分**两大组成部分构成,通过 JSAPI 实现网页与 WPS 应用程序之间的交互。 ``` WPS 加载项架构 ├── 自定义功能区 (Ribbon) │ ├── ribbon.xml # 基于 Office 2007 UI 标准定义 │ ├── 按钮 (Button) # onAction / getEnabled / getVisible │ ├── 右键菜单 (ContextMenu) # 扩展右键菜单项 │ └── 命令覆盖 (Commands) # 覆盖内置命令行为 │ ├── 网页部分 │ ├── TaskPane (任务窗格) # 停靠在主窗口一侧的面板 │ ├── Dialog (对话框) # 弹出式交互界面 │ └── index.html (入口文件) # 加载项主入口 │ └── JSAPI 交互层 ├── wps.WpsApplication() # WPS 文字 Application ├── wps.EtApplication() # WPS 表格 Application ├── wps.Application # 通用 Application(演示等) ├── wps.PluginStorage # 跨页面数据共享 ├── wps.Enum # 枚举常量 └── wps_sdk.js # Web 集成 SDK 封装 ``` ### 三种交互方式 | 方式 | 说明 | 适用场景 | |------|------|---------| | **自定义功能区** | 通过 `ribbon.xml` 配置 CustomUI 标准控件 | 添加工具栏按钮、菜单项 | | **任务窗格** (TaskPane) | 停靠在 WPS 主窗口侧边的网页面板 | 展示丰富的交互内容,与文档实时交互 | | **对话框** (Dialog) | 弹出式网页对话框 | 结合事件监听,实现表单交互 | --- ## 项目目录结构 ### 原生 JS 模式 ``` HelloWps/ ├── images/ # 资源图片,ribbon 菜单图标等 ├── js/ │ ├── ribbon.js # ribbon 入口功能 JS(事件分发) │ ├── taskpane.js # 任务窗格交互逻辑 │ ├── dialog.js # 对话框功能 JS │ ├── systemdemo.js # 外部系统调用接口 │ └── util.js # 工具类,枚举定义等 ├── ui/ │ ├── taskpane.html # 侧边栏内部页面 │ └── dialog.html # 对话框内部页面 ├── index.html # 入口文件(核心) ├── ribbon.xml # 定义 customUI(核心) ├── main.js # 代码加载管理入口 ├── manifest.xml # 自定义函数元数据(可选) ├── functions.json # 函数定义(可选,可自动生成) └── package.json # Node.js 项目配置 ``` ### Vue 模式 ``` HelloWps-Vue/ ├── public/ │ └── ribbon.xml # 自定义功能区配置 ├── src/ │ ├── components/ │ │ ├── js/ │ │ │ ├── ribbon.js # Ribbon 事件处理逻辑 │ │ │ ├── taskpane.js # 任务窗格交互逻辑 │ │ │ ├── dialog.js # 对话框交互逻辑 │ │ │ ├── systemdemo.js # 外部系统调用接口 │ │ │ └── util.js # 工具函数 / 枚举值 │ │ └── views/ # Vue 组件 │ └── App.vue ├── index.html # 入口文件 ├── main.js # Vue 应用入口 ├── manifest.xml # 自定义函数元数据 ├── vite.config.js # 构建配置 └── package.json ``` --- ## 1. 快速开始:创建首个加载项 ### 创建项目 ```bash # 创建新项目(交互式选择类型和风格) wpsjs create HelloWps ``` 执行后会出现交互式选项: 1. **选择加载项类型**:文字(Writer)/ 电子表格(Spreadsheet)/ 演示(Presentation) 2. **选择代码风格**:无(原生 JS + HTML)/ Vue(集成 Vue 脚手架,v1.5.4+ 支持 Vue3) ### 启动调试 ```bash cd HelloWps # 启动调试(自动启动 WPS 并加载加载项,支持热更新) wpsjs debug ``` `wpsjs debug` 会执行以下操作: - 自动启动 WPS 应用程序并加载加载项 - 启动本地 HTTP 服务,提供前端页面热更新功能 - 检测到文件变化时自动刷新加载项 ### 调试工具 - **快捷键** `ALT + F12`:打开加载项独立调试器 - 调试器基于 Chromium DevTools,支持断点、控制台、网络监控等 --- ## 2. 自定义功能区 (`ribbon.xml`) ### 基本结构 `ribbon.xml` 是加载项的界面定义文件,基于 **Office 2007 CustomUI 标准**。 ```xml
``` --- ## 6. 对话框 (Dialog) ### 创建对话框 ```javascript /** * 显示自定义对话框 * @param {string} url - 对话框页面路径 * @param {string} title - 对话框标题 * @param {number} width - 宽度(像素) * @param {number} height - 高度(像素) * @param {boolean} isModal - 是否为模态对话框 */ function showDialog() { wps.ShowDialog( "./ui/dialog.html", // 页面路径 "自定义对话框", // 标题 400, // 宽度 300, // 高度 false // 非模态 ); } ``` ### 对话框页面示例 ```html 对话框

输入要插入的文本

``` --- ## 7. PluginStorage 跨页面数据共享 ### 概述 `wps.PluginStorage` 是 WPS 加载项中实现跨页面(Ribbon、TaskPane、Dialog)数据共享的核心机制。它类似于浏览器的 `localStorage`,但作用域为当前加载项实例。 ### 基本用法 ```javascript // 存储数据 wps.PluginStorage.setItem("key", "value"); wps.PluginStorage.setItem("taskpane_id", tskpane.ID); wps.PluginStorage.setItem("EnableFlag", false); // 读取数据 let value = wps.PluginStorage.getItem("key"); let tsId = wps.PluginStorage.getItem("taskpane_id"); let flag = wps.PluginStorage.getItem("EnableFlag"); // 删除数据 wps.PluginStorage.removeItem("key"); ``` ### 典型应用场景 ```javascript // 场景 1:TaskPane ID 持久化,避免重复创建 function getOrCreateTaskPane(url) { let tsId = wps.PluginStorage.getItem("taskpane_id"); if (tsId) { return wps.GetTaskPane(tsId); } let tskpane = wps.CreateTaskPane(url); wps.PluginStorage.setItem("taskpane_id", tskpane.ID); return tskpane; } // 场景 2:在 Ribbon 和 TaskPane 之间传递参数 // ribbon.js 中设置 wps.PluginStorage.setItem("selectedAction", "format"); // taskpane.html 中读取 let action = wps.PluginStorage.getItem("selectedAction"); if (action === "format") { // 执行格式化逻辑 } // 场景 3:存储用户配置 wps.PluginStorage.setItem("config", JSON.stringify({ autoSave: true, theme: "dark", language: "zh-CN" })); let config = JSON.parse(wps.PluginStorage.getItem("config")); ``` --- ## 8. 事件监听 (ApiEvent) ### 事件注册与移除 ```javascript /** * 注册文档事件监听 * 通过 ApiEvent.AddApiEventListener 注册 */ function bindEvents() { // 监听文档打开事件 wps.ApiEvent.AddApiEventListener("DocumentOpen", function(doc) { console.log("文档已打开: " + doc.Name); }); // 监听文档保存前事件 wps.ApiEvent.AddApiEventListener("DocumentBeforeSave", function(doc, saveAsUI, cancel) { console.log("文档即将保存: " + doc.Name); // 返回 true 取消保存操作 return false; }); // 监听文档关闭前事件 wps.ApiEvent.AddApiEventListener("DocumentBeforeClose", function(doc, cancel) { console.log("文档即将关闭: " + doc.Name); }); // 监听窗口切换事件 wps.ApiEvent.AddApiEventListener("WindowActivate", function(doc, wn) { console.log("窗口激活: " + doc.Name); }); } /** * 移除事件监听 */ function unbindEvents() { wps.ApiEvent.RemoveApiEventListener("DocumentOpen"); wps.ApiEvent.RemoveApiEventListener("DocumentBeforeSave"); wps.ApiEvent.RemoveApiEventListener("DocumentBeforeClose"); wps.ApiEvent.RemoveApiEventListener("WindowActivate"); } ``` ### 事件类型 #### 通知型事件 接收已发生的变化,不可取消操作: | 事件名 | 触发时机 | 回调参数 | |--------|---------|---------| | `DocumentOpen` | 文档打开后 | `(doc)` | | `DocumentAfterClose` | 文档关闭后 | `(doc)` | | `WindowActivate` | 窗口获得焦点 | `(doc, wn)` | | `WindowDeactivate` | 窗口失去焦点 | `(doc, wn)` | #### 询问型事件 可通过返回值控制是否继续执行操作: | 事件名 | 触发时机 | 回调参数 | 取消方式 | |--------|---------|---------|---------| | `DocumentBeforeSave` | 文档保存前 | `(doc, saveAsUI, cancel)` | 返回 `true` | | `DocumentBeforeClose` | 文档关闭前 | `(doc, cancel)` | 返回 `true` | | `DocumentBeforePrint` | 文档打印前 | `(doc, cancel)` | 返回 `true` | --- ## 9. 配置文件详解 ### ribbon.xml — 界面定义 详见第 2 节。 ### manifest.xml — 自定义函数元数据 用于定义 `functions.json` 的路径和命名空间(仅自定义函数场景需要): ```xml 1.0.0 HelloEt 加载项描述 /functions.json HelloEt ``` | 元素 | 说明 | |------|------| | `ApiVersion` | API 版本号 | | `Name` | 加载项名称 | | `Description` | 加载项描述 | | `Functions/Uri` | functions.json 的相对路径 | | `Functions/Namespace` | 自定义函数的命名空间 | > 如果使用 `wps.AddCustomFunction` 直接注册函数,则不需要 manifest.xml。 ### jsplugins.xml — 加载项管理 控制加载项的加载方式,相当于加载项列表文件。 **在线模式**: ```xml ``` **离线模式**: ```xml ``` | 属性 | 说明 | |------|------| | `name` | 插件标识符 | | `type` | 应用类型:`wps`(文字)、`et`(表格)、`wpp`(演示) | | `url` | 插件地址(在线 URL 或本地 7z 包路径) | | `version` | 版本号(离线模式) | | `enable` | 是否启用(在线模式) | > 在线模式和离线模式不可同时使用。 --- ## 10. 部署与发布 ### 模式一:Publish 模式(推荐) 适用于功能扩展类插件和单业务系统集成: ```bash # 打包发布(需填写服务器地址) wpsjs publish ``` 打包后生成 `wps-addon-publish` 目录,包含: - `publish.html` — 安装管理页面 - 加载项资源文件 **部署步骤**: 1. 将生成的文件部署到 Web 服务器 2. 将 `publish.html` 部署到服务器(一般与加载项资源分开部署) 3. 用户在浏览器访问 `publish.html`,点击"安装"按钮即可 **原理**:发布后会在客户端本地的 `jsaddons` 目录生成 `publish.xml` 文件,WPS 启动时自动加载。 ### 模式二:jsplugins.xml 模式 适用于企业级离线部署和二次打包分发: **配置步骤**: 1. 配置 `oem.ini` 中的 `JSPluginsServer` 值 ```ini # oem.ini 配置路径 # Windows: 安装路径\WPS Office\版本号\office6\cfgs\oem.ini # Linux: /opt/kingsoft/wps-office/office6/cfgs/oem.ini # UOS: /opt/apps/cn.wps.wps-office-pro/files/kingsoft/wps-office/office6/cfgs/oem.ini [Support] JSPluginsServer=http://your-server.com/jsplugins.xml ``` 2. 准备 `jsplugins.xml` 文件(见第 9 节) 3. 部署到指定 Web 服务器 4. WPS 启动时自动从该地址拉取配置并加载加载项 > **注意**:个人版 WPS(>= 12.1.0.16910)已限制 oem.ini 方式加载加载项,建议使用 Publish 模式。 ### 模式三:动态传递模式 适用于多业务系统集成场景,业务系统调用时才加载加载项: ```javascript // Web 页面中通过 wps_sdk.js 调用 // 单进程模式 WpsInvoke.InvokeAsHttp( "wps", // 应用类型 "WpsOAAssist", // 加载项名称 "openDoc", // 调用的方法名 JSON.stringify({ // 传递的参数 url: "https://example.com/doc.docx" }), function(result) { console.log("调用结果: " + result); } ); // 多进程模式 let wpsClient = new WpsClient("wps"); wpsClient.jsPluginsXml = "http://your-server.com/jsplugins.xml"; wpsClient.InvokeAsHttp( "WpsOAAssist", "openDoc", JSON.stringify({ url: "https://example.com/doc.docx" }), function(result) { console.log("调用结果: " + result); } ); ``` --- ## 11. 外部系统集成 (Web SDK) ### wps_sdk.js 核心接口 WPS 提供 `wps_sdk.js` 用于外部网页启动和调用 WPS 客户端。 #### 单进程模式 (WpsInvoke) | 方法 | 说明 | |------|------| | `WpsInvoke.InvokeAsHttp(type, name, func, params, callback)` | 以 HTTP 方式调用 WPS | | `WpsInvoke.InvokeAsHttps(type, name, func, params, callback)` | 以 HTTPS 方式调用 WPS | | `WpsInvoke.RegWebNotify(type, name, callback)` | 注册前端接收 WPS 消息的回调 | | `WpsInvoke.IsClientRunning(type, callback)` | 检查 WPS 客户端运行状态 | | `WpsInvoke.ClientType` | 客户端类型常量(`wps` / `et` / `wpp`) | #### 多进程模式 (WpsClient) ```javascript // 创建 WPS 客户端实例 let wpsClient = new WpsClient("wps"); // 设置消息回调 wpsClient.onMessage = function(msg) { console.log("收到 WPS 消息: " + msg); }; // 设置加载项配置 wpsClient.jsPluginsXml = "http://your-server.com/jsplugins.xml"; // 以静默模式启动 wpsClient.StartWpsInSilentMode("WpsOAAssist", function() { console.log("WPS 已在后台启动"); }); // 将窗口显示到最前 wpsClient.ShowToFront("WpsOAAssist"); // 调用加载项方法 wpsClient.InvokeAsHttp( "WpsOAAssist", // 加载项名称 "handleCommand", // 调用的方法名 JSON.stringify(params), // 参数 function(result) { } // 回调 ); ``` ### 加载项管理 (WpsAddonMgr) | 方法 | 说明 | |------|------| | `WpsAddonMgr.enable(name, callback)` | 启用指定加载项 | | `WpsAddonMgr.disable(name, callback)` | 禁用指定加载项 | | `WpsAddonMgr.getAllConfig(callback)` | 获取 publish.xml 配置内容 | | `WpsAddonMgr.verifyStatus(name, callback)` | 检查 ribbon.xml 文件有效性 | ### 暴露接口给外部系统 加载项需要通过 `window` 全局函数暴露接口,以便外部系统调用: ```javascript // systemdemo.js — 暴露给外部系统的接口 /** * 从外部系统打开文件 * @param {Object} params - 参数对象 * @param {string} params.filepath - 文件路径或 URL */ function openOfficeFileFromSystemDemo(params) { let jsonObj = typeof params === "string" ? JSON.parse(params) : params; let filePath = jsonObj.filepath; wps.EtApplication().Workbooks.OpenFromUrl(filePath); return JSON.stringify({ result: "success" }); } /** * 通用外部系统调用入口 * @param {string} funName - 方法名 * @param {string} params - JSON 参数字符串 */ function InvokeFromSystemDemo(funName, params) { let jsonObj = JSON.parse(params); switch (funName) { case "wireFrame": return handleWireFrame(jsonObj); case "getDocumentName": return getDocumentName(); default: return JSON.stringify({ error: "未知方法: " + funName }); } } // 挂载到 window 全局对象 window.openOfficeFileFromSystemDemo = openOfficeFileFromSystemDemo; window.InvokeFromSystemDemo = InvokeFromSystemDemo; ``` --- ## 12. JSAPI 核心接口参考 ### 公共 API | 对象 | 说明 | |------|------| | `wps.PluginStorage` | 跨页面数据共享 | | `wps.CreateTaskPane(url)` | 创建任务窗格 | | `wps.GetTaskPane(id)` | 获取已创建的任务窗格 | | `wps.ShowDialog(url, title, w, h, modal)` | 显示对话框 | | `wps.Enum` | 枚举常量集合 | | `wps.ApiEvent` | 事件注册对象 | ### WPS 文字 (Writer) API 核心对象 | 对象 | 说明 | |------|------| | `Application` | 应用入口,通过 `wps.WpsApplication()` 获取 | | `Document` | 文档对象,管理内容、保存、属性等 | | `Selection` | 当前选区,操作光标位置和选中内容 | | `Range` | 文档范围,精确定位和操作文本 | | `Paragraph` | 段落对象 | | `Table` / `Row` / `Column` / `Cell` | 表格相关对象 | | `Font` / `ParagraphFormat` / `Style` | 格式设置 | | `Find` / `Replace` | 查找替换 | | `Bookmark` | 书签操作 | | `Field` | 域操作 | | `ContentControl` | 内容控件 | | `Comment` / `Revision` | 批注与修订 | | `Hyperlink` | 超链接 | ### WPS 表格 (Spreadsheet) API 核心对象 | 对象 | 说明 | |------|------| | `Application` | 应用入口,通过 `wps.EtApplication()` 获取 | | `Workbook` | 工作簿对象 | | `Worksheet` | 工作表对象 | | `Range` | 单元格区域(最常用对象) | | `AutoFilter` / `Sort` | 筛选与排序 | | `Font` / `Border` / `Interior` | 样式设置 | | `Shape` / `Chart` | 图形与图表 | | `FormatConditions` | 条件格式 | | `Validation` | 数据有效性 | | `WorksheetFunction` | 内置工作表函数 | #### Range 对象常用操作 | 操作分类 | 属性/方法 | |---------|----------| | **数据访问** | `Value` / `Value2`(读写值)、`Text`(只读文本)、`Formula`(公式) | | **格式设置** | `NumberFormat`、`Interior.Color`、`HorizontalAlignment`、`WrapText` | | **结构属性** | `Count`、`Rows`、`Columns`、`Cells`、`EntireRow` / `EntireColumn` | | **单元格操作** | `Clear()`、`ClearContents()`、`ClearFormats()`、`Copy()`、`Cut()` | | **合并拆分** | `Merge()`、`UnMerge()` | | **填充** | `FillDown()`、`FillRight()`、`AutoFill()` | | **遍历** | `Each()`、`Item()`、`Offset()` | ### WPS 演示 (Presentation) API 核心对象 | 对象 | 说明 | |------|------| | `Application` | 应用入口,通过 `wps.Application` 获取 | | `Presentation` | 演示文稿对象 | | `Slide` | 幻灯片对象 | | `Shape` / `TextFrame` / `TextRange` | 形状与文本 | | `PageSetup` | 页面设置 | | `SlideShowSettings` / `SlideShowView` | 放映控制 | ### 加载项专用 API | 对象 | 说明 | |------|------| | `OAAssist` | OA 助手对象,提供文档保护、套红、痕迹管理等 OA 场景功能 | | `ApiEvent` | 事件注册/移除对象 | | `Env` | 环境信息对象,获取 WPS 运行环境相关信息(版本、路径等) | --- ## 13. 自定义函数(仅表格加载项) ### 概述 自定义函数允许开发者在 WPS 表格中注册可在单元格公式中使用的自定义函数(如 `=HelloEt.ADD(1, 2)`)。 ### 方式一:通过 manifest.xml 注册 **manifest.xml**: ```xml 1.0.0 HelloEt 自定义函数示例 /functions.json HelloEt ``` **functions.json**(一般由 vite 插件自动生成): ```json { "functions": [ { "description": "两数相加", "name": "ADD", "params": [ { "description": "第一个数", "name": "a", "type": "number" }, { "description": "第二个数", "name": "b", "type": "number" } ], "result": { "type": "number" } } ] } ``` **functions.js**: ```javascript /** * 自定义函数:两数相加 * 在单元格中使用 =HelloEt.ADD(1, 2) */ function ADD(a, b) { return a + b; } ``` ### 方式二:通过 API 直接注册 ```javascript wps.AddCustomFunction({ name: "MYSUM", description: "自定义求和函数", params: [ { name: "range", description: "求和区域", type: "range" } ], handler: function(range) { let sum = 0; for (let i = 1; i <= range.Count; i++) { sum += range.Item(i).Value2 || 0; } return sum; } }); ``` > **版本要求**:自定义函数需要 WPS 版本 >= 12.1.0.20540 --- ## 14. VBA 迁移注意事项 ### 从 VBA 到 JavaScript 的语法差异 | VBA 写法 | JavaScript 写法 | 说明 | |---------|----------------|------| | `doc.Save` | `doc.Save()` | 方法调用必须加括号 | | `Cells(1, 1)` | `Cells.Item(1, 1)` | 使用 `.Item()` 访问集合元素 | | `Range("A1").Value` | `Range("A1").Value2` | 推荐使用 `Value2`(性能更好) | | `MsgBox "hello"` | `alert("hello")` | 消息框 | | `Dim x As Integer` | `let x = 0` | 变量声明 | | `Nothing` | `null` / `undefined` | 空值 | | `Set obj = ...` | `let obj = ...` | 对象赋值无需 `Set` | | 缺省参数留空 | 使用 `undefined` 占位 | 缺省参数处理 | ### 示例对比 **VBA**: ```vba Sub InsertText() Dim doc As Document Set doc = ActiveDocument doc.Range(0, 0).Text = "Hello" doc.Save End Sub ``` **JavaScript**: ```javascript function InsertText() { let doc = wps.WpsApplication().ActiveDocument; if (doc) { doc.Range(0, 0).Text = "Hello"; doc.Save(); } } ``` --- ## 15. 最佳实践 ### 开发规范 1. **始终做空值检查**:操作文档前必须检查 `ActiveDocument` / `ActiveWorkbook` 是否存在 ```javascript // 正确写法 let doc = wps.WpsApplication().ActiveDocument; if (!doc) { alert("当前没有打开任何文档"); return; } // 安全地操作文档 doc.Range(0, 0).Text = "Hello"; // 错误写法(可能抛出异常) wps.WpsApplication().ActiveDocument.Range(0, 0).Text = "Hello"; ``` 2. **使用 PluginStorage 管理状态**:跨页面共享数据的唯一可靠方式 3. **枚举值做兼容处理**:同时支持 `wps.Enum` 和自定义回退枚举 ```javascript let value; if (wps.Enum) { value = wps.Enum.msoCTPDockPositionLeft; } else { value = WPS_Enum.msoCTPDockPositionLeft; } ``` 4. **TaskPane ID 持久化**:创建后立即存入 PluginStorage,避免重复创建 5. **通过 window 暴露接口**:外部 OA 系统只能通过全局函数与加载项通信 6. **ribbon.xml 严格遵循格式**:XML 声明顶格,控件尺寸属性限制 ### 调试技巧 1. 使用 `ALT + F12` 打开调试器,支持断点调试和控制台输出 2. 使用 `console.log()` 输出调试信息 3. 修改代码后,`wpsjs debug` 支持热更新自动刷新 4. 通过 `http://127.0.0.1:端口/manifest.xml` 验证配置文件是否正确加载 5. 自定义函数修改后需重启 WPS(不支持热重载) ### 性能优化 1. **批量操作单元格**:使用 Range 批量设值,避免逐个单元格操作 ```javascript // 推荐:批量设值 let range = sheet.Range("A1:C3"); range.Value2 = [ ["姓名", "年龄", "部门"], ["张三", 28, "技术部"], ["李四", 32, "产品部"] ]; // 不推荐:逐个设值 for (let i = 1; i <= 3; i++) { for (let j = 1; j <= 3; j++) { sheet.Cells.Item(i, j).Value2 = data[i-1][j-1]; } } ``` 2. **关闭屏幕刷新**:大量操作前关闭屏幕更新 ```javascript let app = wps.EtApplication(); app.ScreenUpdating = false; // 关闭屏幕刷新 // 执行大量数据操作 ... app.ScreenUpdating = true; // 恢复屏幕刷新 ``` --- ## 16. 完整示例:OA 文档助手 以下是一个完整的 WPS 文字加载项示例,实现基本的 OA 文档管理功能。 ### ribbon.xml ```xml