最近开始了WPS加载项的研究了,发现WPS加载项是越研究越香,尤其是和“加载项”相比,“WPS加载项”功能更加完善,交互方式也更齐全,比如:自定义功能区、通过功能区跳转出对应页面/窗口等等。两者之间具体的区别可以自行搜索一下。
(一)时间助手
接下来分享我的第一个使用WPS加载项实现的项目——时间助手(在表格窗口加一个显示时间的侧边栏),具体看下方演示效果:
主要的实现步骤如下:
(二)在功能区自定义一个按钮,用于时间助手这个页面的开启和关闭
代码实现:在ribbon.xml中添加一个button,button名称是“时间助手”,绑定了点击事件用于执行OnAction,并通过getImage获取该按钮的图标
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnAddinLoad">
<!-- 自定义功能区 -->
<ribbon startFromScratch="false">
<tabs>
<!-- 标签选项卡 -->
<tab id="wpsAddinTab" label="库存核对">
<group>
<button id="Other" label="其他" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" getVisible="OnGetVisible" size="large"/>
<box id="Other" boxStyle="vertical">
<!-- 在功能区添加一个按钮,名称是“时间助手” -->
<button id="time" label="时间助手" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" visible="true" size="large"/>
<button id="home" label="工具箱" onAction="OnAction" getEnabled="OnGetEnabled" getImage="GetImage" visible="true" size="large"/>
</box>
</group>
</tab>
</tabs>
</ribbon>
</contextMenus> -->
</customUI>
(三)在ribbon.js中配置相关对应的功能,ribbon.js在WPS加载项创建时会自动生成。
①在OnAction函数中添加侧边栏相关代码,配置路径打开对应的html页面。
function OnAction(control) {
const eleId = control.Id
switch (eleId) {
case "time":
{
let tsId1 = wps.PluginStorage.getItem("taskpane_id1")
if (!tsId1) {
let tskpane1 = wps.CreateTaskPane(GetUrlPath() + "/ui/time.html")//这里通过路径打开对应的html页面
let id1 = tskpane1.ID
wps.PluginStorage.setItem("taskpane_id1", id1)
tskpane1.Visible = true
} else {
let tskpane1 = wps.GetTaskPane(tsId1)
tskpane1.Visible = !tskpane1.Visible
}
}
break
default:
break
}
return true
}
② 在GetImage函数中配置button图标的路径。
function GetImage(control) {
const eleId = control.Id
switch (eleId) {
// 其他
case "Other":
return "images/QT_logo.png"
case "time":
return "images/time.png"
case "home":
return "images/home.png"
default:
;
}
return "images/newFromTemp.svg"
}
(四)使用html画出侧边栏页面,比较简单所以直接用了html+css+js实现。
HTML:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WPS助手</title>
<link rel="stylesheet" href="../css/time.css">
</head>
<body>
<div class="container">
<div class="info"> <span id="current-time" class="time"></span></div>
<div class="info"> <span id="current-date" class="highlight"></span></div>
<div class="info">距离下班: <span id="time-to-off" class="highlight"></span></div>
<div class="info">下一个周末: <span id="days-to-saturday" class="highlight"></span> 天</div>
<div class="info">下一个节假日: <span id="days-to-holiday" class="highlight"></span> 天</div>
</div>
<div class = 'weather'>
<div class="tq-1">天气预报</div>
<div class="tq">今天天气: <span id="today-weather" class="highlight"></span></div>
<div class="tq">今天温度: <span id="today-temp" class="highlight"></span> °C</div>
<div class="tq">明天天气: <span id="tomorrow-weather" class="highlight"></span></div>
<div class="tq">明天温度: <span id="tomorrow-temp" class="highlight"></span> °C</div>
</div>
<div class="divItem">
<div class="div2"><button class="btn" onclick="onbuttonclick('dockLeft')">停靠左边</button></div>
<div class="div2"><button class="btn" onclick="onbuttonclick('dockRight')">停靠右边</button></div>
<div class="div2"><button class="btn" onclick="onbuttonclick('hideTaskPane')">隐藏</button></div>
</div>
<script src="../js/time.js"></script>
</body>
</html>
CSS:
body, html {
height: 100%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: #fffdfd;
font-family: 'Arial', sans-serif;
}
.container {
background-color: #fff;
border: 2px solid #ccc;
border-radius: 20px;
padding: 15px;
width: 180px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
animation: fadeIn 1s ease-in-out;
position: absolute;
top: 10%;
transform: translateY(-20%);
}
.info {
margin: 20px 0;
font-size: 20px;
color: #333;
}
.time {
font-size: 30px;
color: #ff4081;
font-weight: bold;
animation: pulse 1.5s infinite;
}
.highlight {
font-size: 15px;
color: #ff4081;
font-weight: bold;
animation: pulse 1.5s infinite;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.weather {
background-color: #fff;
border: 2px solid #ccc;
border-radius: 20px;
padding: 15px;
width: 180px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
animation: fadeIn 1s ease-in-out;
position: absolute;
top: 46%;
transform: translateY(-20%);
}
.tq-1 {
margin: 20px 0;
font-size: 20px;
color: #333;
}
.tq {
margin: 15px 0;
font-size: 15px;
color: #333;
}
.divItem {
background-color: #fff;
border: 2px solid #ccc;
border-radius: 20px;
padding: 15px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
width: 180px;
animation: fadeIn 1s ease-in-out;
position: absolute;
top: 77%;
transform: translateY(-20%);
}
.div2 {
margin: 10px 0;
font-size: 15px;
color: #333;
}
.btn {
height: 50px;
width: 100px;
font-size: 15px;
color: #080808;
}
.btn:hover {
background-color: #ff4081;
font-size: 15px;
color: #080808;
animation: pulse 0.5s infinite;
}
JS:
function updateTime() {
const now = new Date();
const currentTime = now.toLocaleTimeString();
const currentDate = now.toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' });
document.getElementById('current-time').textContent = currentTime;
document.getElementById('current-date').textContent = currentDate;
const offTime = new Date();
const zhouji = offTime.getDay();
switch (zhouji) {
case 3: offTime.setHours(20, 0, 0); // 周三下班时间是20:00
break
case 4: offTime.setHours(20, 0, 0); // 周四下班时间是20:00
break
default : offTime.setHours(18, 0, 0); // 其他下班时间是18:00
break
}
const timeDifference = offTime - now;
const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
const timeToOff = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
document.getElementById('time-to-off').textContent = timeDifference > 0 ? timeToOff : "00:00:00";
const nextSaturday = new Date(now);
nextSaturday.setDate(now.getDate() + (6 - now.getDay()));
const daysToSaturday = Math.ceil((nextSaturday - now) / (1000 * 60 * 60 * 24));
document.getElementById('days-to-saturday').textContent = daysToSaturday;
const holidays = [
new Date(now.getFullYear(), 8, 15), // 例:中秋节9月15日
new Date(now.getFullYear(), 9, 1), // 例:国庆节10月1日
new Date(now.getFullYear(), 0, 1), // 例:元旦1月1日
];
let daysToHoliday = Infinity;
holidays.forEach(holiday => {
const diff = Math.ceil((holiday - now) / (1000 * 60 * 60 * 24));
if (diff >= 0 && diff < daysToHoliday) {
daysToHoliday = diff;
}
});
document.getElementById('days-to-holiday').textContent = daysToHoliday;
setTimeout(updateTime, 1000);
}
function onbuttonclick(idStr) {
if (typeof (wps.Enum) != "object") { // 如果没有内置枚举值
wps.Enum = WPS_Enum
}
switch(idStr) {
case "dockLeft": {
let tsId = wps.PluginStorage.getItem("taskpane_id1")
if (tsId){
let tskpane = wps.GetTaskPane(tsId)
tskpane.DockPosition = wps.Enum.msoCTPDockPositionLeft
}
break
}
case "dockRight": {
let tsId = wps.PluginStorage.getItem("taskpane_id1")
if (tsId){
let tskpane = wps.GetTaskPane(tsId)
tskpane.DockPosition = wps.Enum.msoCTPDockPositionRight
}
break
}
case "hideTaskPane": {
let tsId = wps.PluginStorage.getItem("taskpane_id1")
if (tsId){
let tskpane = wps.GetTaskPane(tsId)
tskpane.Visible = false
}
break
}
case "addString": {
let curSheet = wps.EtApplication().ActiveSheet;
if (curSheet){
curSheet.Cells.Item(1, 1).Formula="Hello, wps加载项!" + curSheet.Cells.Item(1, 1).Formula
}
break;
}
case "getDocName": {
let doc = wps.EtApplication().ActiveWorkbook
let textValue = "";
if (!doc){
textValue = textValue + "当前没有打开任何文档"
return
}
textValue = textValue + doc.Name
document.getElementById("text_p").innerHTML = textValue
break
}
}
}
// JavaScript 获取并展示天气信息的部分
const apiKey = '5e19938dc766447fb6552948242106'; // 在这里替换为你自己的 API Key
const city = 'Shanghai'; // 在这里替换为你想要查询天气的城市
function updateWeather() {
fetch(`https://api.weatherapi.com/v1/forecast.json?key=${apiKey}&q=${city}&days=2&lang=zh`)
.then(response => response.json())
.then(data => {
const todayWeather = data.forecast.forecastday[0].day.condition.text;
const todayMaxTemp = data.forecast.forecastday[0].day.maxtemp_c;
const todayMinTemp = data.forecast.forecastday[0].day.mintemp_c;
const tomorrowWeather = data.forecast.forecastday[1].day.condition.text;
const tomorrowMaxTemp = data.forecast.forecastday[1].day.maxtemp_c;
const tomorrowMinTemp = data.forecast.forecastday[1].day.mintemp_c;
document.getElementById('today-weather').textContent = todayWeather;
document.getElementById('today-temp').textContent = todayMinTemp + "~" + todayMaxTemp;
document.getElementById('tomorrow-weather').textContent = tomorrowWeather;
document.getElementById('tomorrow-temp').textContent = tomorrowMinTemp + "~" + tomorrowMaxTemp;
})
.catch(error => console.error('Error fetching weather data:', error));
}
updateTime();
updateWeather();
(五)总结
截至目前,也做了不少工作相关的WPS加载项(大部分是从之前的加载项代码转置过来的,主要是用于个人工作,定向性比较重所以就不展示分享了)。
关于WPS加载项欢迎留言讨论,或者你想要通过WPS加载项实现某些功能,也欢迎留言or私信,看到了会尽力帮你实现,
Tip:完全免费(主要想练习)