// ==UserScript== // @name 复旦微加班时间统计 // @namespace http://start.fengbohan.com/ // @version 2026-01-28 // @description 更新2026年的节假日数据 // @author bhfeng // @match http://192.168.36.67:7888/shr/dynamic.do?uipk=com.kingdee.eas.hr.ats.app.WorkCalendarItem* // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAdVBMVEX///////7+/v4kVZ729/lafbO2xd3x8/f5+vz8/Pzd4+4tW6GSp8xnh7nP2OgxXqLK1OZ1kb/s7/UgUZytvdhKcKw+aKgaTZlhgrbU3eqar9Dm6fFVd7CEnsZwjb4VSZfAy+BCbKq3xd58l8OXrM6nudaMo8kyjkGlAAABfklEQVQokT1Si5arIBCLDIqCiogP8FHtWvv/n3iH7vZyDjDMQAIJwLdlpVIlz0L8T0FAxDaTy5JBrWn52ZhxoO6tQ+ZyjT7fJSqRpYJAJte8qbDPmxKPY4vgXOqdhramwzp6B1GTKRJahjjVVdlQI3FauiTahkOmFfHyJ9qJduC2lHf4o69weiZYyLyAYvLWFe3O1apHa80AsZOpGfdhZrLHDfSDwpumFuVO5HqI8xot3xvrM5Qy+DGiqo3fTgbvteRRT36AHmlk2J/R26vlbHmuiMVrWtOlpoGPX2a2wV3BMjQiNSXakezVA50zfvbHyGTMQlcFnRNtRQXEl3u4lklWVRi6FNRuyTRD/5E7sXehvIlCBM6c3zBed3E/dNKqCdlg/bRIlENj/XHMxztphdUGxNxTKNhAPbz3/edjlEC0uRTLc6b81syu5K8fPOhxKtAvm5/NMw/vNRNfy1U9hUKpbnFNsyh8v0Pa0A/O1SyE+LrxV6mSdjH2gsPf/D//7BjTQny+YwAAAABJRU5ErkJggg== // @grant none // @run-at document-idle // @license MI // ==/UserScript== (function() { 'use strict'; // 硬编码工作日数据 /* WORKDAY_DATA_PLACEHOLDER */ const workDayList = workDayData.list; function isWorkDay(weekday, date) { // 将日期格式化为 YYYYMMDD date = date.replace(/-/g, ''); // 查找工作日列表中是否存在该日期 const foundItem = workDayList.find(item => String(item.date) === date); if (!foundItem) { if (weekday === "周六" || weekday === "周日") { return false; // 周六和周日默认不是工作日 } else{ return true; // 默认工作日是周一到周五 } } else if (foundItem.workday === 1) { return true; // 情况1:工作日 } else { return false; // 情况2:非工作日 } } function calculateTimeDifference(timeString, workDay) { // 判断字符串是否为 "--" if (timeString === '--') { return 0; } // 按逗号分割字符串 const times = timeString.split(','); // 解析时间并转换为毫秒 const parsedTimes = times.map(timeStr => { const [hours, minutes, seconds] = timeStr.trim().split(':').map(Number); return isNaN(hours) || isNaN(minutes) || isNaN(seconds) ? null : (hours * 3600 + minutes * 60 + seconds) * 1000; }); // 过滤掉无效的时间 const validTimes = parsedTimes.filter(time => time !== null); if (validTimes.length === 0) { console.warn(`calculateTimeDifference: 没有有效的时间数据, timeString="${timeString}"`); return 0; } // 计算最大值和最小值 const maxTime = Math.max(...validTimes); const minTime = Math.min(...validTimes); // 工作日从下午5点20分(17:20:00)开始计算加班 const fiveTwentyPM = (17 * 3600 + 20 * 60) * 1000; let timeDifferenceInHours; // 计算加班时间 if (workDay) { // 工作日 if (maxTime >= fiveTwentyPM) { timeDifferenceInHours = (maxTime - fiveTwentyPM) / (1000 * 60 * 60); } else { timeDifferenceInHours = 0; } } else { // 非工作日 timeDifferenceInHours = (maxTime - minTime) / (1000 * 60 * 60); } return timeDifferenceInHours; } function sumOfSecondColumn(tableId) { const table = document.getElementById(tableId); if (!table) { console.error(`sumOfSecondColumn: 未找到ID为 "${tableId}" 的表格`); return 0; } let totalSum = 0; const rows = table.getElementsByTagName('tr'); // 遍历每行 从1开始跳过表头 for (let i = 1; i < rows.length; i++) { const cells = rows[i].getElementsByTagName('td'); if (cells.length > 1) { // 确保该行至少有两列 const cellValue = cells[3].textContent; const weekday = cells[2].textContent; const date = cells[1].textContent; const workDay = isWorkDay(weekday, date); const difference = calculateTimeDifference(cellValue, workDay); totalSum += difference; // 修改单元格的值 cells[5].title = ""; cells[5].textContent = `${difference.toFixed(2)}H`; if (!workDay){ cells[5].style.backgroundColor = "#90EE90"; } } } return totalSum; } // 方便快速查询前一月、前二月、前三月 function simulateDateInput(elementId, dateValue) { const inputElement = document.getElementById(elementId); if (!inputElement) { console.error(`未找到ID为 ${elementId} 的输入框`); return; } inputElement.focus(); inputElement.value = dateValue; inputElement.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })); inputElement.dispatchEvent(new Event('change', { bubbles: true, cancelable: true })); inputElement.blur(); } function getCustomMonthRange(monthsAgo) { const formatLocal = (date) => { return date.toLocaleDateString('en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-'); }; const now = new Date(); let year = now.getFullYear(); let month = now.getMonth(); const day = now.getDate(); // 如果今天是21号及以后,当前周期是本月21到下月20 // 否则,周期是上月21到本月20 if (day >= 21) { month = month - monthsAgo; let startYear = year, startMonth = month; while (startMonth < 0) { startMonth += 12; startYear--; } let endYear = startYear, endMonth = startMonth + 1; if (endMonth > 11) { endMonth -= 12; endYear++; } const firstDay = new Date(startYear, startMonth, 21); const lastDay = new Date(endYear, endMonth, 20); return { firstDay: formatLocal(firstDay), lastDay: formatLocal(lastDay) }; } else { month = month - monthsAgo - 1; let startYear = year, startMonth = month; while (startMonth < 0) { startMonth += 12; startYear--; } let endYear = startYear, endMonth = startMonth + 1; if (endMonth > 11) { endMonth -= 12; endYear++; } const firstDay = new Date(startYear, startMonth, 21); const lastDay = new Date(endYear, endMonth, 20); return { firstDay: formatLocal(firstDay), lastDay: formatLocal(lastDay) }; } } const QUERY_DELAY_MS = 500; // 创建菜单按钮 function createMenuButton(text, options = {}) { const button = document.createElement('button'); button.textContent = text; const copiedStyles = { webkitTapHighlightColor: "rgba(0, 0, 0, 0)", fontFamily: '"Helvetica Neue", Helvetica, Arial, "Microsoft Yahei", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", sans-serif', fontSize: "14px", listStyle: "none", textAlign: "center", boxSizing: "border-box", webkitTextSizeAdjust: "100%", backgroundColor: "transparent", display: "block", lineHeight: "60px", height: "60px", padding: "0px 10px", textDecoration: "none", color: "#e5e5e5", cursor: "pointer", }; Object.assign(button.style, copiedStyles); // 处理点击事件回调 if (typeof options.onClick === 'function') { button.addEventListener('click', options.onClick); } return button; } // 创建查询按钮(设置日期范围并查询) function createQueryButton(text, rangeGetter) { return createMenuButton(text, { onClick: () => { const { firstDay, lastDay } = rangeGetter(); console.log(`firstDay: ${firstDay}, lastDay: ${lastDay}`); simulateDateInput('beginDate', firstDay); simulateDateInput('endDate', lastDay); queryButton.click(); setTimeout(() => { get_work_time(); }, QUERY_DELAY_MS); } }); } // 计算当周(周一至周日)的日期范围 function getCurrentWeekRange() { const now = new Date(); const dayOfWeek = now.getDay(); // 0=周日, 1=周一, ..., 6=周六 const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; const monday = new Date(now); monday.setDate(now.getDate() + mondayOffset); const sunday = new Date(monday); sunday.setDate(monday.getDate() + 6); const formatLocal = (date) => { return date.toLocaleDateString('en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-'); }; return { firstDay: formatLocal(monday), lastDay: formatLocal(sunday) }; } // 查询工作时间 function get_work_time() { const totalSum = sumOfSecondColumn('grid'); const headerTable = document.querySelector("#gview_grid > div.ui-state-default.ui-jqgrid-hdiv > div > table > thead > tr"); if (!headerTable) { console.error('get_work_time: 未找到表头元素'); return; } const rows = headerTable.getElementsByTagName('th'); const titleCell = rows[5].querySelector('#jqgh_grid_fillCardTimeStr'); if (!titleCell) { console.error('get_work_time: 未找到表头加班列元素'); return; } titleCell.textContent = `加班时间(${totalSum.toFixed(2)}小时)`; } const queryButton = document.getElementById("query"); const button = createMenuButton("统计加班时间", { onClick: () => { get_work_time(); } }); const button0 = createQueryButton('查当月', () => getCustomMonthRange(0)); const button1 = createQueryButton('查上月', () => getCustomMonthRange(1)); const button2 = createQueryButton('查上上月', () => getCustomMonthRange(2)); const button3 = createQueryButton('查上上上月', () => getCustomMonthRange(3)); const button4 = createQueryButton('查当周', getCurrentWeekRange); // 获取要将按钮添加到其中的容器 let container = document.getElementById('seclevelmenu'); if (!container) { container = document.body; } container.appendChild(button); container.appendChild(button0); container.appendChild(button1); container.appendChild(button2); container.appendChild(button3); container.appendChild(button4); console.log("复旦微加班时间统计脚本已加载"); console.log("点击按钮后会统计加班时间"); })()