Yang Sheng 于 2025-02-42,17:46 最后修改

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