[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-鳴謝