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

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

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