[TOC]
> Tue May 11 2021 04:44:50 GMT+0800 (GMT+08:00)
WPS JS 插件功能区菜单貌似是基于 Office 2007 UI 标准的,你可以用它的 Ribbon UI 标准库来给自己的插件设计菜单。差别在于:
1. 面向 Office 设计的功能区菜单一般需要 VBA 宏作为菜单元素的事件响应,比如按钮、输入框等等。而 WPS 插件则需要用 JavaScript 的函数来响应。
> 注意:单文件`"注入"式(嵌入)`的 CustomUI 在 WPS 中打开虽然能显示自定义界面,但不会响应——除非你的 WPS 安装了 VBA 环境且切换到 VBA 环境。
>
> WPS 支持的 CustomUI 是针对 JS 加载项(JS插件)的(通常它是全局的),而 Micro Office 的 CustomUI 是可以镶嵌到单个文件中(非全局,和文件一起保存,仅对这个文件生效),`WPS 没有提供这个功能支持——将CustomUI注入单个文件中配合JS宏一起使用`。
2. RibbonUI 的配置文件 XML 中各组件的配置大同小异,主要差异是在组件回调上。了解Custom UI 的话很容易就做好。
我们来看看官方文档中的示例:
1. Ribbon xml
```XML
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnAddinLoad">
<ribbon startFromScratch="false">
<tabs>
<tab id="wpsAddinTab" label="wps加载项示例">
<group id="btnDemoGroup" label="group1">
<button id="btnShowMsg" label="弹出消息框" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" visible="true" size="large"/>
<button id="btnIsEnbable" getLabel="OnGetLabel" onAction="OnAction" enabled="true" getImage="GetImage" visible="true" size="large"/>
<button id="btnShowDialog" label="弹对话框网页" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" getVisible="OnGetVisible" size="large"/>
<button id="btnShowTaskPane" label="弹任务窗格网页" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" getVisible="OnGetVisible" size="large"/>
<button id="btnApiEvent" getLabel="OnGetLabel" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" getVisible="OnGetVisible" size="large"/>
<button id="btnWebNotify" label="给业务系统发通知" onAction="OnAction" enabled="true" getImage="GetImage" getVisible="OnGetVisible" size="large"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
```
UI 组件回调响应 JS:
```js
//这个函数在整个wps加载项中是第一个执行的
function OnAddinLoad(ribbonUI){
if (typeof (wps.ribbonUI) != "object"){
wps.ribbonUI = ribbonUI
}
if (typeof (wps.Enum) != "object") { // 如果没有内置枚举值
wps.Enum = WPS_Enum
}
wps.PluginStorage.setItem("EnableFlag", false) //往PluginStorage中设置一个标记,用于控制两个按钮的置灰
wps.PluginStorage.setItem("ApiEventFlag", false) //往PluginStorage中设置一个标记,用于控制ApiEvent的按钮label
return true
}
var WebNotifycount = 0;
function OnAction(control) {
const eleId = control.Id
switch (eleId) {
case "btnShowMsg":
{
const doc = wps.WpsApplication().ActiveDocument
if (!doc) {
alert("当前没有打开任何文档")
return
}
alert(doc.Name)
}
break;
case "btnIsEnbable":
{
let bFlag = wps.PluginStorage.getItem("EnableFlag")
wps.PluginStorage.setItem("EnableFlag", !bFlag)
//通知wps刷新以下几个按饰的状态
wps.ribbonUI.InvalidateControl("btnIsEnbable")
wps.ribbonUI.InvalidateControl("btnShowDialog")
wps.ribbonUI.InvalidateControl("btnShowTaskPane")
//wps.ribbonUI.Invalidate(); 这行代码打开则是刷新所有的按钮状态
break
}
case "btnShowDialog":
wps.ShowDialog(GetUrlPath() + "/ui/dialog.html", "这是一个对话框网页", 400 * window.devicePixelRatio, 400 * window.devicePixelRatio, false)
break
case "btnShowTaskPane":
{
let tsId = wps.PluginStorage.getItem("taskpane_id")
if (!tsId) {
let tskpane = wps.CreateTaskPane(GetUrlPath() + "/ui/taskpane.html")
let id = tskpane.ID
wps.PluginStorage.setItem("taskpane_id", id)
tskpane.Visible = true
} else {
let tskpane = wps.GetTaskPane(tsId)
tskpane.Visible = !tskpane.Visible
}
}
break
case "btnApiEvent":
{
let bFlag = wps.PluginStorage.getItem("ApiEventFlag")
let bRegister = bFlag ? false : true
wps.PluginStorage.setItem("ApiEventFlag", bRegister)
if (bRegister){
wps.ApiEvent.AddApiEventListener('DocumentNew', OnNewDocumentApiEvent)
}
else{
wps.ApiEvent.RemoveApiEventListener('DocumentNew', OnNewDocumentApiEvent)
}
wps.ribbonUI.InvalidateControl("btnApiEvent")
}
break
case "btnWebNotify":
{
let currentTime = new Date()
let timeStr = currentTime.toLocaleDateString() + ", " + currentTime.toLocaleTimeString()
wps.OAAssist.WebNotify("这行内容由wps加载项主动送达给业务系统,可以任意自定义, 比如时间值:" + timeStr + ",次数:" + (++WebNotifycount), true)
}
break
default:
break
}
return true
}
function GetImage(control) {
const eleId = control.Id
switch (eleId) {
case "btnShowMsg":
return "images/1.svg"
case "btnShowDialog":
return "images/2.svg"
case "btnShowTaskPane":
return "images/3.svg"
default:
;
}
return "images/newFromTemp.svg"
}
function OnGetEnabled(control) {
const eleId = control.Id
switch (eleId) {
case "btnShowMsg":
return true
break
case "btnShowDialog":
{
let bFlag = wps.PluginStorage.getItem("EnableFlag")
return bFlag
break
}
case "btnShowTaskPane":
{
let bFlag = wps.PluginStorage.getItem("EnableFlag")
return bFlag
break
}
default:
break
}
return true
}
function OnGetVisible(control){
return true
}
function OnGetLabel(control){
const eleId = control.Id
switch (eleId) {
case "btnIsEnbable":
{
let bFlag = wps.PluginStorage.getItem("EnableFlag")
return bFlag ? "按钮Disable" : "按钮Enable"
break
}
case "btnApiEvent":
{
let bFlag = wps.PluginStorage.getItem("ApiEventFlag")
return bFlag ? "清除新建文件事件" : "注册新建文件事件"
break
}
}
return ""
}
function OnNewDocumentApiEvent(doc){
alert("新建文件事件响应,取文件名: " + doc.Name)
}
```
> 能在 JS 中找到组件中的一些属性(配置项)吧,函数名对应组件的ID或者其他属性值。
## 工具集
1. Office Ribbon Editor [GitHub](https://github.com/fernandreu/office-ribbonx-editor)
2. visualribboneditor (据说不支持中文 label)加载项 https://andypope.info
上面两个都是针对 Microsoft Office 的工具,可定制 Office Menu UI(加载项的菜单\功能区自定义界面),因为 WPS 没有类似的工具,如果你不熟悉 Custom UI 标准,可以先在 Micro Office 上设计好,然后拿到 WPS 上使用。
custom UI 文档 https://docs.microsoft.com/en-us/openspecs/office_standards/ms-customui/574eeee8-7a03-406a-b95f-f9e51e53dd9d 貌似都用 Ribbon 这个组件。没太深入了解,大家自行研究。
自定义的菜单、功能区是由 xml 配置的,你还得为为这些自定义的菜单、按钮等写代码以响应其动作以及实现其功能。上面两个工具生成的组件回调函数都是 VBA 的 Sub 过程,你要自己手动改成 JS 的 function。并进一步完善功能逻辑。
如果你很熟悉这一套流程,可以:
1. Custom UI XML 转为 JSON。
2. JSON 中提取组件 ID、方法、事件名称,然后用字符模板生成组件的事件、功能对应的 JS 函数。
> 目的是不必在 XML 中一个一个组件地去找,然后一个一个组件地去写功能实现。
当然了,一个一个组件地去实现,靠谱些。
- 1-概述
- 2-升级吧
- 3-阅读指南
- 4-答萌新问
- 5-iTool 加载项
- 6-iTool下载安装
- 7-Gitee token 生成
- 8-喜欢用表格当下载器的小伙伴看过来
- 9-喜欢扒数据的小伙伴跟上
- 10-了解 WPS JS宏编辑器
- 11-快捷键
- 12-立即窗口
- 13-断点调试
- 14-如何在 VB、JS 环境之间切换
- 15-如何快速进入使用、编辑JS宏
- 16-使用宏编辑器需要注意的点
- 17-保存&导出&使用宏
- 18-全局隐藏函数名(仅WPS)
- 19-如何命名自己的全局函数的名称(宏名)
- 20-如何使用别人写的宏
- 21-如何修改录制好的宏
- 22-WPS 宏中可用的 JS 库
- 23-将 Lodash 装进 WPS JS宏
- 24-使用 Moment.js
- 25-用 Papa Parse 进行JSON、CSV 互转
- 26-Lodash.js源码
- 27-Moment.js源码
- 28-papaparse.js源码
- 29-WPS JSAPI
- 30-对象名中英文对照表
- 31-事件汇总
- 32-对象属性的使用
- 33-从 Excel VBA 宏到 WPS JS宏
- 34-从 Visual Basic Script 转到 JavaScript
- 35-fileSystem文件系统
- 36-Open 与 Write
- 37-将内容写入 TXT 文件
- 38-Dir 函数读文件夹
- 39-关于颜色
- 40-表单、ActiveX控件
- 41-控件样式
- 42-数据绑定
- 43-解决“我是谁,我在哪”的问题
- 44-绕坑:那些奇奇怪怪的问题
- 45-警惕:宏操作对象限制
- 46-别造轮子了,用现成的吧
- 47-Item 属性快把你逼疯了吧
- 48-空单元格的值是什么
- 49-单元格区域(多个单元格)的值
- 50-编译参数禁止项开关导致的问题
- 51-代码上下文带来的问题
- 52-API 中方法函数参数的问题
- 53-结束进程吧
- 54-福利:WPS 表格 Modules 开箱即用
- 55-最后一格可用空单元格总在变?帮你追上它
- 56-判断一个单元格是不是在指定区域里
- 57-工作表类
- 58-单元格读取封装
- 59-WPS JS 加载项
- 60-加载项开发绕坑
- 61-开发
- 62-部署、分发
- 63-安装、卸载加载项
- 64-更智能的处理加载项的安装
- 65-WPS 控件点击之后报错:Error: arguments error
- 66-对 wpsjs npm 包的一些改进
- 67-加载项中 TaskPane 和 Dialog之间的差别
- 68-怎么解决 ShowDialog 窗口无限弹出?
- 69-Custom UI
- 70-控制控件在不同应用上的显示和隐藏
- 71-本地化处理
- 72-加载项示例和自建工具
- 73-如何保证在无 WEB 环境下加载项依旧正常工作
- 74-WPS 对 customUI 组件的支持程度
- 75-如何让加载项的 input 类元素准确关联 SheetSelectionChange 事件
- 76-从零开始开发一个简易的 JS 加载项
- 77-搭建项目
- 78-插件规划
- 79-第一个 ribbonUI 控件
- 80-第一个弹窗 confirm
- 81-第一个 Web 弹窗: ShowDialog
- 82-第一个侧栏 TaskPane
- 83-打包第一个版本
- 84-RibbonUI 控件不完全示例
- 85-基本结构
- 86-WPS JS加载项 RibbonUI 控件 API 与 VBE API 的差别
- 87-控件属性设计"误区"
- 88-主控:有条件加载 customUI.onLoad
- 89-布局盒子(父元素)
- 90-控件属性·事件·动作
- 91-简单示例
- 92-WPS 表格
- 93-单元格
- 94-如何描述单元格对象
- 95-单元格读写
- 96-单元格信息
- 97-关于合并单元格
- 98-合并单元格,I hate it!
- 99-单元格复制粘贴
- 100-选择性粘贴
- 101-自定义序列
- 102-单元格的删除/插入
- 103-单元格填充
- 104-单元格去重和高亮突出显示
- 105-单元格条件格式
- 106-数据透视表
- 107-自动筛选
- 108-通过 JS 宏处理 DISPIMG 函数
- 109-计时器:OnTime 和 Wait
- 110-探索: Range.Offset 如何安全“漂移”
- 111-表格自动化事件
- 112-创建一个事件记录日志工作表
- 113-实现类 Vlookup 单条件提取内容
- 114-代码和宏说明
- 115-创建工作表目录
- 116-将所有批注汇总成列表
- 117-JS宏代码
- 118-剥离 UDF 公式(数值化处理)
- 119-单元格区域转 JSON 并写入文件
- 120-JS宏代码
- 121-设置工作表标签(Tab)颜色
- 122-插入日历 calendar
- 123-再偷懒些
- 124-示例代码
- 125-WPS 表格函数表
- 126-JS 宏中可用的表格函数
- 127-WPS 表格-工作表-工作簿
- 128-通用简易工作表目录
- 129-工作表排序
- 130-巧用 Parent 进行对象锁定
- 131-我想要的 WPS 表格函数
- 132-单元格字符匹配统计
- 133-基于正则表达式的 Replace
- 134-身份证
- 135-控件 API 精讲(以表格为例)
- 136-窗体控件
- 137-ActiveX 控件
- 138-WPS 文档
- 139-"定位"专题
- 140-视图切换
- 141-别被 paragraph 骗了
- 142-无效脚注、尾注检测
- 143-获取所有标题
- 144-答网友问
- 145-轮序键入——键入完成自动切换单元格
- 146-又是一个很会偷懒的 Bigger Cousin
- 147-给你演示什么叫"画地为牢"
- 148-示例代码
- 149-如何批量超链接到同名的sheet?
- 150-如何将一个表中一个列的值合并到另一个表中的一个单元格中?
- 151-多个字符都往一个单元格里塞
- 152-wps如何将A列里包含B列的单元格高亮显示?
- 153-excel中随着打印份数渐序更新日期的函数?
- 154-就是玩儿
- 155-挖宝:寻找API文档中没有的接口
- 156-通过 Application 对象的属性获取信息
- 157-呵呵,失败的抽奖!
- 158-哪里不会点哪里
- 159-芝麻开门
- 160-乖,别闹,Stay here
- 161-JS宏 实现【WPS 表格 VIP 专享】功能挑战
- 162-工作表批量重命名
- 163-工作表标签批量着色
- 164-工作表拆分为工作簿
- 165-工作簿合并为工作表
- 166-WPS 表格 API 参考
- 167-Application.InpuBox
- 168-Worksheet(s)、Sheets
- 169-Range.End 精讲
- 170-Cells 精讲
- 171-WPS 文字 API 参考
- 172-怎么快速掌握 文字 宏API
- 173-WPS 宏编辑器 API 参考
- 174-InputBox
- 175-MsgBox
- 176-alert
- 177-WPS 扩展 API 参考
- 178-WebShape
- 179-通用 API 参考
- 180-WPS Office文件上传&下载 JavaScript 宏 API
- 181-JS宏API:Office(全网独一份)
- 182-公共部分
- 183-文字独有
- 184-JavaScript 语言基础笔记
- 185-国际化API(格式化处理)
- 186-数组
- 187-日期时间
- 188-数学计算
- 189-外接输入设备(读卡器、扫码枪)数据写进 WPS 可行性
- 190-鸣谢