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