你是否曾在使用WPS时,反复执行某些枯燥的操作,并渴望有一个小工具能一键完成?或者,你是否发现WPS的某些功能未能完全契合你的特定工作流,希望对其进行增强?如果你有这样的想法,那么学习WPS插件开发将是解锁WPS Office全部潜力的关键一步。与内置的JS宏录制不同,插件开发允许你创建拥有独立界面、更深层系统集成和更稳定功能的自定义工具。
本文旨在为初学者提供一条清晰的路径,手把手引导你完成第一个WPS插件的开发、调试与测试。我们将从一个极具实用价值的例子出发——开发一个“文档信息侧边栏插件”,它可以在WPS文字的侧边面板中显示当前文档的页数、字数、最后修改时间,并允许用户快速插入自定义文档状态标记。通过这个项目,你将系统掌握WPS插件开发的核心概念与流程。
一、 为何选择开发WPS插件?明确你的开发动机 #
在投入时间学习之前,明确插件开发的价值有助于保持学习动力。WPS插件相较于简单的宏脚本,具有以下显著优势:
- 深度集成与专业外观:插件可以创建独立的Ribbon选项卡、功能按钮、任务窗格(侧边栏),甚至自定义对话框,提供与原生功能无异的专业用户体验。
- 功能更强大稳定:通过插件API,你可以访问更底层的文档对象和应用程序事件,实现更复杂、性能更好的自动化操作,避免宏录制的一些限制。
- 便于分发与复用:开发完成的插件可以打包成一个独立的安装文件(如
.wpsaddin或.exe),轻松分享给团队成员或其他用户,无需他们接触复杂的脚本代码。 - 提升个人与团队效率:将重复性工作固化为一键操作,定制符合特定业务场景的功能(如自动生成特定格式的报告、连接公司内部数据库等),是提升生产力的终极利器。
- 技术拓展与职业增值:掌握WPS插件开发,意味着你拥有了为国内最主流的办公软件生态贡献价值的能力,这是一项极具市场竞争力的技能。
如果你对更基础的自动化感兴趣,可以先行了解《 WPS宏与自动化办公入门到精通》,为插件开发打下基础。而对于更深入的API操作,我们的《 WPS二次开发进阶:使用JS宏API操作文档对象模型(DOM)》将是你的下一站。
二、 开发前准备:搭建你的WPS插件开发环境 #
工欲善其事,必先利其器。开始编写代码前,你需要配置好开发环境。
2.1 核心工具与软件安装 #
-
WPS Office 专业版或开发版:
- 建议安装最新版本的WPS Office。个人免费版虽然支持运行插件,但为了获得完整的开发支持和稳定性,推荐使用专业版或申请开发者版本。你可以访问官网或通过《 WPS Office 2024最新官方正版下载与安装激活全攻略》获取可靠安装源。
- 关键步骤:安装时,请务必在“自定义安装”中勾选“VBA宏支持”和“开发工具”相关组件。这是插件运行和调试的基础。
-
代码编辑器:
- Visual Studio Code (VS Code):首选推荐。它轻量、免费,且拥有强大的JavaScript/TypeScript支持和丰富的扩展插件。
- 安装VS Code扩展:在VS Code中安装以下扩展以提升效率:
ESLint:代码语法和风格检查。JavaScript (ES6) code snippets:快速输入代码片段。- (可选)
Live Server:用于本地调试HTML界面。
-
Node.js与npm:
- 前往Node.js官网下载并安装LTS(长期支持)版本。安装完成后,在命令行终端输入
node -v和npm -v验证是否安装成功。 - npm是随Node.js附带的包管理器,后续我们可能会用它来管理项目依赖。
- 前往Node.js官网下载并安装LTS(长期支持)版本。安装完成后,在命令行终端输入
2.2 理解WPS插件技术栈:JS-API与CEF #
WPS插件主要基于以下两项技术:
- WPS JS-API:这是WPS开放给JavaScript调用的应用程序编程接口。它允许你的JavaScript代码控制WPS应用程序(文字、表格、演示),操作文档内容,响应应用程序事件(如文档打开、保存)。其设计理念与微软的Office JS API相似,但针对WPS进行了优化。
- CEF (Chromium Embedded Framework):WPS使用CEF来渲染插件的用户界面(UI)。这意味着你的插件界面本质上是一个本地运行的“小型网页”,可以使用HTML、CSS和JavaScript来构建。这极大降低了UI开发的门槛,任何有前端开发经验的人都能快速上手。
简单来说:你的插件逻辑(操作WPS)使用WPS JS-API,你的插件界面(用户看到的)使用网页技术,两者通过WPS提供的桥梁进行通信。
三、 从零开始:创建你的第一个WPS插件项目 #
让我们开始实践。我们将创建一个名为 DocInfoPane(文档信息面板)的插件。
3.1 项目结构与文件创建 #
在你的工作目录(例如 D:\WPS_Projects)下,创建一个新的文件夹 DocInfoPane,并在其中创建以下文件和子文件夹:
DocInfoPane/
├── manifest.xml # 插件清单文件,核心配置文件
├── index.html # 插件主界面HTML文件
├── script.js # 插件主逻辑JavaScript文件
├── style.css # 插件界面样式文件
└── images/ # 存放图标等资源
└── icon-32.png # 插件图标(32x32像素)
3.2 编写插件清单文件 (manifest.xml) #
manifest.xml 是插件的“身份证”,它告诉WPS这个插件叫什么、作者是谁、入口在哪里、有哪些功能。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"
xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
xsi:type="TaskPaneApp">
<!-- 插件唯一ID,建议使用反向域名格式 -->
<Id>com.wpswy.document.info.pane</Id>
<Version>1.0.0.0</Version>
<ProviderName>WPS技术站</ProviderName>
<DefaultLocale>zh-CN</DefaultLocale>
<DisplayName DefaultValue="文档信息助手" />
<Description DefaultValue="在侧边栏显示文档页数、字数等信息,并快速插入状态标记。"/>
<IconUrl DefaultValue="https://wpswy.com/favicon.ico"/> <!-- 可选在线图标 -->
<SupportUrl DefaultValue="https://wpswy.com"/>
<AppDomains>
<AppDomain>https://wpswy.com</AppDomain>
</AppDomains>
<Hosts>
<Host Name="Document" /> <!-- 声明此插件支持WPS文字 -->
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="index.html"/> <!-- 指定主界面入口 -->
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
<Hosts>
<Host xsi:type="Document">
<DesktopFormFactor>
<GetStarted>
<Title resid="GetStarted.Title"/>
<Description resid="GetStarted.Description"/>
<LearnMoreUrl resid="GetStarted.LearnMoreUrl"/>
</GetStarted>
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<!-- 在“开始”选项卡后添加一个自定义选项卡 -->
<CustomTab id="Tab_wpswy">
<Group id="Group_DocInfo">
<Label resid="Group_DocInfo.Label"/>
<Icon>
<bt:Image size="16" resid="Icon_16"/>
<bt:Image size="32" resid="Icon_32"/>
<bt:Image size="80" resid="Icon_80"/>
</Icon>
<Control xsi:type="Button" id="Button_ShowPane">
<Label resid="Button_ShowPane.Label"/>
<Supertip>
<Title resid="Button_ShowPane.Title"/>
<Description resid="Button_ShowPane.Description"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon_16"/>
<bt:Image size="32" resid="Icon_32"/>
<bt:Image size="80" resid="Icon_80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>TaskpaneId_DocInfo</TaskpaneId>
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
</Group>
</CustomTab>
</ExtensionPoint>
<ExtensionPoint xsi:type="Taskpane" resid="Taskpane.Url" />
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon_16" DefaultValue="images/icon-16.png"/>
<bt:Image id="Icon_32" DefaultValue="images/icon-32.png"/>
<bt:Image id="Icon_80" DefaultValue="images/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://wpswy.com/news/46/"/>
<bt:Url id="Taskpane.Url" DefaultValue="index.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GetStarted.Title" DefaultValue="开始使用文档信息助手"/>
<bt:String id="Group_DocInfo.Label" DefaultValue="文档信息"/>
<bt:String id="Button_ShowPane.Label" DefaultValue="显示信息面板"/>
<bt:String id="Button_ShowPane.Title" DefaultValue="打开文档信息侧边栏"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="GetStarted.Description" DefaultValue="此插件将帮助您实时监控文档状态并快速标记。"/>
<bt:String id="Button_ShowPane.Description" DefaultValue="点击在侧边打开文档信息面板,查看页数、字数等信息。"/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</OfficeApp>
关键点说明:
Id:必须是全球唯一的标识符。SourceLocation:指向你的index.html文件。CustomTab:在WPS的功能区创建了一个名为“文档信息”的新选项卡。ShowTaskpane:定义了点击按钮后显示侧边栏的动作。Resources:集中定义了所有文本和图片资源,便于本地化。
3.3 构建插件用户界面 (index.html, style.css) #
index.html:创建侧边栏的基本骨架。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文档信息助手</title>
<link rel="stylesheet" href="style.css">
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
<!-- 引入WPS JS-API,注意:实际开发中需使用WPS提供的本地或特定版本 -->
<!-- <script src="wps-js-api.js"></script> -->
</head>
<body>
<div class="container">
<header>
<h1>📄 文档信息</h1>
<button id="btnRefresh" class="btn-icon" title="刷新信息">🔄</button>
</header>
<main>
<section class="info-section">
<h3>基础统计</h3>
<div class="info-item">
<label>页数:</label>
<span id="pageCount">--</span>
</div>
<div class="info-item">
<label>字数:</label>
<span id="wordCount">--</span>
</div>
<div class="info-item">
<label>最后保存:</label>
<span id="lastModified">--</span>
</div>
</section>
<section class="action-section">
<h3>快速标记</h3>
<p>在光标处插入文档状态标记:</p>
<div class="button-group">
<button class="btn-action" data-tag="[审阅中]">审阅中</button>
<button class="btn-action" data-tag="[已批准]">已批准</button>
<button class="btn-action" data-tag="[待发布]">待发布</button>
<button class="btn-action" data-tag="[机密]">机密</button>
</div>
<div class="custom-tag">
<input type="text" id="customTagInput" placeholder="输入自定义标记...">
<button id="btnInsertCustom" class="btn-small">插入</button>
</div>
</section>
</main>
<footer>
<p class="hint">*信息随文档活动自动更新</p>
</footer>
</div>
<script src="script.js"></script>
</body>
</html>
style.css:为侧边栏添加基本样式,确保其在WPS中美观协调。
body {
font-family: "Microsoft YaHei", "Segoe UI", sans-serif;
margin: 0;
padding: 12px;
background-color: #f8f9fa;
color: #333;
font-size: 14px;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #dee2e6;
padding-bottom: 10px;
margin-bottom: 15px;
}
header h1 {
margin: 0;
font-size: 18px;
color: #2c3e50;
}
.btn-icon {
background: none;
border: 1px solid #ced4da;
border-radius: 4px;
cursor: pointer;
padding: 5px 8px;
font-size: 16px;
}
.info-section, .action-section {
margin-bottom: 20px;
padding: 15px;
background: white;
border-radius: 6px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
h3 {
margin-top: 0;
color: #495057;
border-left: 4px solid #4a90e2;
padding-left: 8px;
}
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
padding-bottom: 8px;
border-bottom: 1px dashed #eee;
}
.info-item:last-child {
border-bottom: none;
margin-bottom: 0;
}
.info-item label {
font-weight: bold;
color: #6c757d;
}
.button-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin: 12px 0;
}
.btn-action {
padding: 8px 5px;
background-color: #e9ecef;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.btn-action:hover {
background-color: #d0d7e0;
}
.custom-tag {
display: flex;
gap: 8px;
margin-top: 10px;
}
#customTagInput {
flex-grow: 1;
padding: 8px;
border: 1px solid #ced4da;
border-radius: 4px;
}
.btn-small {
padding: 8px 12px;
background-color: #4a90e2;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
footer {
margin-top: auto;
padding-top: 10px;
text-align: center;
color: #868e96;
font-size: 12px;
}
.hint {
margin: 0;
}
3.4 编写插件核心逻辑 (script.js) #
这是插件的大脑,负责与WPS交互。
// 文档加载完成后初始化
Office.onReady((info) => {
if (info.host === Office.HostType.Word) { // 检查是否在WPS文字中运行
console.log('WPS文档信息插件已加载。');
initializeApp();
} else {
document.body.innerHTML = '<p>此插件仅支持在WPS文字中使用。</p>';
}
});
function initializeApp() {
// 绑定刷新按钮事件
document.getElementById('btnRefresh').addEventListener('click', updateDocumentInfo);
// 绑定所有预设标记按钮事件
document.querySelectorAll('.btn-action').forEach(button => {
button.addEventListener('click', (e) => {
insertTag(e.target.getAttribute('data-tag'));
});
});
// 绑定自定义标记插入按钮事件
document.getElementById('btnInsertCustom').addEventListener('click', () => {
const input = document.getElementById('customTagInput');
if (input.value.trim()) {
insertTag(`[${input.value.trim()}]`);
input.value = ''; // 清空输入框
}
});
// 初始加载时获取一次文档信息
updateDocumentInfo();
// 监听文档选择变化事件(模拟实现,实际API可能不同)
// Word.run(context => {
// context.document.body.onSelectionChanged.add(updateDocumentInfo);
// return context.sync();
// }).catch(console.error);
}
// 更新侧边栏中的文档信息
async function updateDocumentInfo() {
try {
// 注意:以下为示例代码,实际WPS JS-API函数名称和用法需参考官方文档
await Word.run(async (context) => {
const body = context.document.body;
context.load(body, 'text, paragraphCount'); // 加载正文内容和段落数
// 获取文档属性(页数、字数等可能需通过特定接口)
const properties = context.document.properties;
context.load(properties, 'lastModified');
await context.sync(); // 同步执行所有队列操作
// 更新UI (页数、字数等为示意,实际计算方式更复杂)
document.getElementById('pageCount').textContent = Math.ceil(body.paragraphCount / 50) || 1; // 模拟页数
document.getElementById('wordCount').textContent = body.text.trim().split(/\s+/).length || 0;
document.getElementById('lastModified').textContent = new Date(properties.lastModified).toLocaleString();
console.log('文档信息已更新。');
});
} catch (error) {
console.error('更新文档信息时出错:', error);
document.getElementById('pageCount').textContent = '错误';
document.getElementById('wordCount').textContent = '错误';
}
}
// 在光标处插入标记文本
async function insertTag(tagText) {
try {
await Word.run(async (context) => {
const range = context.document.getSelection(); // 获取当前选区
range.insertText(tagText + ' ', Word.InsertLocation.replace); // 插入文本并加一个空格
await context.sync();
console.log(`已插入标记: ${tagText}`);
});
} catch (error) {
console.error('插入标记时出错:', error);
alert('插入标记失败,请确保文档可编辑且光标位置正确。');
}
}
代码逻辑解析:
Office.onReady:确保WPS环境准备就绪后再执行插件代码。initializeApp:函数绑定所有按钮的点击事件。updateDocumentInfo:核心函数,通过Word.run异步上下文与WPS文档交互,获取文档属性并更新侧边栏显示。insertTag:获取当前光标位置(选区),并插入指定的标记文本。- 重要提示:上述代码中的
Word.run等API调用是基于微软Office JS API的示例语法。WPS JS-API的具体函数名和调用方式可能有所不同,开发时请务必以WPS官方提供的开发文档和API参考为准。
四、 调试、加载与测试你的插件 #
开发完成后,你需要让WPS加载并运行这个本地插件。
4.1 本地调试加载方式 #
-
配置信任的插件目录:
- 打开WPS文字,进入“文件”->“选项”->“信任中心”->“信任中心设置”->“受信任的加载项目录”。
- 添加你插件项目所在的根目录(如
D:\WPS_Projects\DocInfoPane)到信任列表中。这是最关键的一步。
-
使用开发工具加载:
- 在WPS文字中,切换到“开发工具”选项卡(如果没看到,需要在选项中启用)。
- 点击“加载项”或“插件”相关按钮,选择“从文件夹加载”或“部署清单”,然后选择你的
manifest.xml文件。
-
调试:
- 界面调试:由于界面是HTML,在侧边栏中右键点击,选择“检查元素”(如果WPS的CEF支持),即可打开开发者工具进行调试,如同在Chrome浏览器中一样。
- 逻辑调试:在VS Code中设置调试断点,或大量使用
console.log()将信息输出到开发者工具的Console面板。
4.2 常见问题与排查 #
- 插件未显示:检查
manifest.xml格式是否正确,信任目录是否设置无误,WPS是否重启。 - API调用失败:确认WPS版本支持JS-API,检查API函数名是否正确,使用
try...catch捕获错误并打印。 - 界面加载失败:检查HTML/CSS/JS文件路径是否正确,网络策略是否允许加载本地资源。
- 若遇到复杂的运行环境问题,可参考《 WPS常见安装失败、启动错误与网络问题的排查方法》进行系统性排查。
五、 进阶方向与插件发布 #
5.1 功能进阶思路 #
你的第一个插件运行成功后,可以考虑为其添加更多实用功能:
- 实时监听:利用WPS API的事件模型(如
onSelectionChanged,onContentChanged),实现信息的实时更新。 - 与云服务交互:在插件中调用网络API,实现文档内容翻译、云存储、数据查询等功能。
- 更复杂的UI:使用Vue.js或React等前端框架构建更动态、响应式的侧边栏界面。
- 支持多主机:修改
manifest.xml,让插件同时支持WPS文字、表格和演示。
5.2 打包与分发 #
- 打包:将整个插件项目文件夹(
manifest.xml,index.html,script.js,style.css,images/)压缩成一个ZIP文件,然后将其后缀改为.wpsaddin(或WPS指定的其他格式)。 - 数字签名:为了安全和信任,建议为你的插件进行数字签名。
- 分发方式:
- 内部共享:直接将
.wpsaddin文件发送给用户,他们双击即可安装。 - 网站下载:将插件包放在你的网站上(例如
https://wpswy.com/plugins/DocInfoPane.wpsaddin),提供下载链接和安装说明。 - 应用商店:考虑提交至WPS稻壳儿或未来的WPS插件应用市场,让更多用户发现和使用你的作品。关于稻壳儿生态,可以阅读《 WPS稻壳儿内容生态:如何成为模板/字体设计师并获取收益》获取启发。
- 内部共享:直接将
六、 常见问题解答 (FAQ) #
Q1: 开发WPS插件需要付费吗? A1: 开发本身完全免费。你只需要WPS Office(个人版或专业版)、一个代码编辑器和相关技术知识。插件打包和分发也无需向WPS支付费用。
Q2: WPS插件可以用哪些编程语言开发? A2: 目前,基于Web技术的插件(使用HTML/CSS/JS和WPS JS-API)是主流且官方推荐的方式。这与微软Office Add-in的技术路径一致。传统上也有使用C++、.NET (VSTO) 开发COM插件的方式,但这通常更复杂,且对新版WPS的兼容性需要单独验证。
Q3: 我写的插件能在别人的电脑上运行吗? A3: 可以,但前提是对方的WPS Office版本支持插件功能,并且正确安装了你的插件包。你需要将项目打包成分发格式(如.wpsaddin)。用户可能需要调整其WPS的安全设置以允许加载来自你的插件。
Q4: WPS插件的JS-API文档在哪里? A4: 这是初学者最常遇到的问题。WPS官方会为开发者提供专门的API参考文档和SDK(软件开发工具包)。建议访问WPS开放平台官网或开发者社区查找最新的开发文档、示例代码和教程。由于API可能更新,务必以官方最新文档为准。
Q5: 插件开发与之前提到的JS宏有什么区别? A5: JS宏主要是在WPS内置的宏编辑器环境中,针对单个文档进行自动化操作,功能相对受限,且与文档绑定。插件则是独立的应用程序扩展,可以创建全局性的用户界面,拥有更广泛的API访问权限和系统集成能力,可以安装到用户的WPS中,对所有文档生效。两者互为补充,插件开发的门槛和灵活性更高。
结语 #
恭喜你!通过跟随本文的步骤,你已经完成了从零到一创建WPS插件的完整旅程。你不仅学会了如何配置环境、编写清单文件、构建界面和实现核心逻辑,更掌握了调试和分发的基本方法。开发“文档信息侧边栏”只是一个起点,这个过程中学到的项目结构、API调用模式和调试技巧,是构建任何复杂插件的基石。
WPS插件开发的世界广阔而充满可能。你可以将创意转化为工具,解决实际办公痛点,甚至为WPS的生态贡献力量。下一步,建议你深入研究WPS JS-API的官方文档,探索更多事件和对象,尝试将插件与后端服务结合,或为你的团队定制一套专属的效率套件。记住,最好的学习永远是动手实践,去构想并实现你的下一个插件吧!