Files
FM-OHS/main.template.js
fengbh b74978d2dc 新增查当周按钮,代码重构与数据分离
- 新增"查当周"按钮,计算当周周一至周日的加班总工时
- 新增 createQueryButton 工厂函数,消除月按钮重复代码
- 修复 calculateTimeDifference 返回字符串导致 NaN 的 bug
- 修复 sumOfSecondColumn/get_work_time 缺少空值保护的 bug
- 修复 isWorkDay 中 == 改为 ===
- var 统一为 const/let,提取 QUERY_DELAY_MS 常量
- 提取 getCustomMonthRange 中重复的 formatLocal
- 按钮增加 cursor:pointer 样式
- 数据与代码分离:main.template.js(~300行)+ build.py 自动组装 main.js
- update.py/comp_json.py 支持命令行年份参数,默认当年
- comp_json.py 增加去重逻辑
- 更新 README 为 GitHub 开源项目风格,含快速上手和每年更新章节

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:11:13 +08:00

306 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ==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("点击按钮后会统计加班时间");
})()