Failed to execute the [velocity] macro. Cause: [null]. Click on this message for details.

由用户 Yang Sheng 在 2025-01-31,10:39 保存的版本 1.5

显示最后作者
1 = 效果展示: =
2
3
4 [[image:1737957143812-601.png||height="701" width="1082"]]
5
6
7 = 部署方法: =
8
9
10 2025/02/01更新:
11
12 目前微软应用商店已过审,插件设置为未公开,您无法从应用商店直接搜索到,但可以通过以下网址直接访问
13
14
15 === 1.在浏览器安装油猴插件 ===
16
17
18
19 网址如下:
20 [[https:~~/~~/microsoftedge.microsoft.com/addons/detail/%E7%AF%A1%E6%94%B9%E7%8C%B4/iikmkjmpaadaobahmlepeloendndfphd?refid=bingshortanswersdownload>>url:https://microsoftedge.microsoft.com/addons/detail/%E7%AF%A1%E6%94%B9%E7%8C%B4/iikmkjmpaadaobahmlepeloendndfphd?refid=bingshortanswersdownload||rel="noopener noreferrer" target="_blank"]]
21
22
23 因为我已经安装了,下图是删除,没安装过的是安装。
24
25 [[image:1737957191147-772.png||height="752" width="1078"]]
26
27
28 == 2.配置浏览器开发者属性。 ==
29
30
31 复制下面网址至浏览器:
32
33 edge:~/~/extensions/
34
35 如下图:红框全部勾选
36
37 [[image:1737957191170-336.png||height="573" width="1083"]]
38
39
40 == 3.添加脚本 ==
41
42
43 访问如下网址,直接添加脚本:
44
45 [[https:~~/~~/update.greasyfork.org/scripts/525013/Remaining_Work_Hours_For_AIO.user.js>>url:https://update.greasyfork.org/scripts/525013/Remaining_Work_Hours_For_AIO.user.js||rel="noopener noreferrer" target="_blank"]]
46
47 核对名称,版本,直接安装即可
48
49 [[image:1737957191174-404.png||height="673" width="1084"]]
50
51
52 == 4.功能验证 ==
53
54 (% style="color:#c0392b" %)**此插件只有在考勤日历界面才会启用**
55
56 当停留在考勤日历界面时,检查脚本是否正常启用:
57
58
59 [[image:1737957191180-764.png]]
60
61
62 打开浏览器地址栏后插件的图标,让篡改猴可视化。
63
64 确认脚本已经选中:
65
66
67 [[image:1737957191180-705.png]]
68
69
70 且篡改猴图标有红色数字1,代表脚本正常运行。
71
72 至此便可正常显示剩余工时。
73
74
75 {{velocity}}
76 #if($xcontext.user != "XWiki.XWikiGuest")
77
78
79
80 = 代码如下: =
81
82
83 {{code language="java" layout="LINENUMBERS"}}
84 // ==UserScript==
85 // @name Remaining_Work_Hours_For_AIO
86 // @namespace http://nicebro.fun/
87 // @version V1.1
88 // @description Remaining_Work_Hours_for_AIO
89 // @author flower
90 // @match https://eip.h3c.com/myCenter/kaoqin
91 // @grant none
92 // @license MIT
93 // @downloadURL https://update.greasyfork.org/scripts/525013/Remaining_Work_Hours_For_AIO.user.js
94 // @updateURL https://update.greasyfork.org/scripts/525013/Remaining_Work_Hours_For_AIO.meta.js
95 // ==/UserScript==
96
97 //20250127 V1.1版本
98 //更新说明:
99 //对请假时长小于8小时场景,进行更新。
100
101 //20250126 V1.0版本
102 //计算逻辑:查看.absolute中的打卡时间,计算当日工作时间,减去午休时间12:00-1:00,及当日正常8小时工作时间,计算当日剩余工时
103 //当有外出公干逻辑时,取.ant-fullcalendar-content中的外出公干时间,与当日出勤时间合计后,减去午休及当日8小时工时,计算剩余工时
104 //考虑请假情况,请假情况不做处理,直接计算为0剩余工时。
105 //忽略上、下班未打卡提示
106 //考虑外出公干中0:00-0:00情况,直接计算为0剩余工时。
107
108 (function() {
109 'use strict';
110 let isInsertDiv = false;
111 let totalMinutesWorkOvertime = 0;
112
113 const timeToMinutes = (timeStr) => {
114 const [hours, minutes] = timeStr.split(':').map(Number);
115 return isNaN(hours) || isNaN(minutes) ? 0 : hours * 60 + minutes;
116 };
117
118 const delayInterval = 5000;
119 const scanInterval = 1000;
120 const standardWorkMinutes = 8 * 60; // 标准工作时间为8小时,单位为分钟
121 const siestaMinutes = timeToMinutes('13:00') - timeToMinutes('12:00'); // 午休时间,单位为分钟
122
123 const createDisplayElement = () => {
124 const fullscreenElement = document.querySelector('.ant-fullcalendar-fullscreen');
125 if (fullscreenElement) {
126 const displayDiv = document.createElement('div');
127 displayDiv.id = 'total-time-display';
128 displayDiv.style.backgroundColor = '#f0f0f0';
129 displayDiv.style.padding = '16px';
130 displayDiv.style.textAlign = 'left';
131 displayDiv.style.zIndex = '1000';
132 displayDiv.style.fontSize = '16px';
133 displayDiv.style.fontWeight = 'bold';
134 displayDiv.style.color = '#333';
135 displayDiv.textContent = "剩余工时-增值AIO试用: ";
136 fullscreenElement.parentNode.insertBefore(displayDiv, fullscreenElement);
137 }
138 };
139
140 const updateDisplay = (totalMinutesWorkOvertime, totalHours) => {
141 const displayDiv = document.getElementById('total-time-display');
142 if (displayDiv) {
143 displayDiv.textContent = `剩余工时-AIO试用: ${totalHours} 小时( ${totalMinutesWorkOvertime} 分钟 )`;
144 } else {
145 console.log("无法找到指定displayDiv");
146 }
147 };
148
149 const scanElements = () => {
150 try {
151 console.clear();
152 totalMinutesWorkOvertime = 0;
153
154 const element1 = document.querySelector('.ant-calendar-picker');
155 if (element1) {
156 const textContent = element1.textContent.trim();
157 const elements2 = document.querySelectorAll('.ant-fullcalendar-cell');
158 elements2.forEach((element2) => {
159 if (element2.title.includes(textContent)) {
160 let currentDate = 0;
161 const currentDateElements = element2.querySelector('.ant-fullcalendar-value');
162 if (currentDateElements) {
163 const tmpDate = currentDateElements.textContent.trim();
164 if (!isNaN(tmpDate)) {
165 currentDate = tmpDate;
166 }
167 }
168
169 console.log(`正在处理本月 ${currentDate} 日的数据...`);
170
171 let totalDailyMinutes = 0;
172 let hasLeave = false; // 用于判断是否有全日假期
173
174 // 检查是否为休息日
175 const absoluteElement = element2.querySelector('.absolute');
176 const statusTextElement = element2.querySelector('.ant-fullcalendar-content');
177 const hasTimePattern = /\d{1,2}:\d{2}/; // 时间格式匹配
178
179 // 如果没有时间信息,视为休息日
180 if (!absoluteElement && (!statusTextElement || !hasTimePattern.test(statusTextElement.textContent))) {
181 console.log(`本月 ${currentDate} 日为休息日,不计算剩余工时`);
182 return; // 当前日期为休息日,跳过计算
183 }
184
185 // 处理外出公干和假期时间段
186 if (statusTextElement) {
187 const statusText = statusTextElement.textContent.trim();
188 const publicBusinessPattern = /外出公干((\d{1,2}:\d{2})-(\d{1,2}:\d{2}))/g;
189 const leavePattern = /[\u4e00-\u9fa5]*假((\d{1,2}:\d{2})-(\d{1,2}:\d{2}))/g;
190 let match;
191
192 // 处理外出公干
193 while ((match = publicBusinessPattern.exec(statusText)) !== null) {
194 const startTime = timeToMinutes(match[1]);
195 const endTime = timeToMinutes(match[2]);
196 if (startTime && endTime && endTime > startTime) {
197 const duration = endTime - startTime;
198 totalDailyMinutes += duration;
199 console.log(`外出公干时间段:${match[1]} - ${match[2]},时长:${duration} 分钟`);
200 }
201 }
202
203 // 处理假期
204 while ((match = leavePattern.exec(statusText)) !== null) {
205 const startTime = timeToMinutes(match[1]);
206 const endTime = timeToMinutes(match[2]);
207 if (startTime === 0 && endTime === 0) {
208 console.log(`全日假期:${match[1]} - ${match[2]}`);
209 hasLeave = true;
210 } else if (startTime && endTime && endTime > startTime) {
211 const duration = endTime - startTime;
212 totalDailyMinutes += duration;
213 console.log(`假期时间段:${match[1]} - ${match[2]},时长:${duration} 分钟`);
214 }
215 }
216 }
217
218 // 处理打卡时间段
219 if (absoluteElement) {
220 const timeRange = absoluteElement.textContent.trim();
221 const timePattern = /(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})/g;
222 let match;
223 while ((match = timePattern.exec(timeRange)) !== null) {
224 const startTime = timeToMinutes(match[1]);
225 const endTime = timeToMinutes(match[2]);
226 if (startTime && endTime && endTime > startTime) {
227 const duration = endTime - startTime;
228 totalDailyMinutes += duration;
229 console.log(`打卡时间段:${match[1]} - ${match[2]},时长:${duration} 分钟`);
230 }
231 }
232 }
233
234 console.log(`本月 ${currentDate} 日总工作时间(含午休):${totalDailyMinutes} 分钟`);
235
236 // 计算当天剩余工时
237 if (totalDailyMinutes > 0) {
238 let dailyOvertimeMinutes = totalDailyMinutes - siestaMinutes - standardWorkMinutes;
239 console.log(`扣除标准工时和午休后,本月 ${currentDate} 日剩余工时:${dailyOvertimeMinutes} 分钟`);
240
241 if (dailyOvertimeMinutes > 0) {
242 totalMinutesWorkOvertime += dailyOvertimeMinutes;
243 }
244 } else if (hasLeave) {
245 console.log(`本月 ${currentDate} 日为全日假期,不计算剩余工时。`);
246 }
247
248 console.log(`本月 ${currentDate} 日处理完成。`);
249 }
250 });
251
252 // 输出累计结果
253 console.log(`总剩余分钟数: ${totalMinutesWorkOvertime}`);
254 const totalHours = (totalMinutesWorkOvertime / 60).toFixed(2);
255 console.log(`总剩余小时数: ${totalHours}`);
256
257 if (!isInsertDiv) {
258 createDisplayElement();
259 isInsertDiv = true;
260 }
261
262 updateDisplay(totalMinutesWorkOvertime, totalHours);
263 }
264 } catch (error) {
265 console.error("An error occurred during the scan:", error);
266 }
267 };
268
269 setTimeout(() => {
270 scanElements();
271 setInterval(scanElements, scanInterval);
272 }, delayInterval);
273 })();
274 {{/code}}
275
276
277 #end
278 {{/velocity}}
279
280
281