Compare commits
6 Commits
b74978d2dc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a2ea27a25 | |||
| 9fc1e8633d | |||
| b9f41191ab | |||
| c35b60b496 | |||
| 2d455ddb20 | |||
| 99d3ecad6d |
@@ -5419,8 +5419,8 @@
|
|||||||
"holiday": 44,
|
"holiday": 44,
|
||||||
"holiday_or": 45,
|
"holiday_or": 45,
|
||||||
"holiday_overtime": 10,
|
"holiday_overtime": 10,
|
||||||
"holiday_today": 2,
|
"holiday_today": 1,
|
||||||
"holiday_legal": 2,
|
"holiday_legal": 1,
|
||||||
"holiday_recess": 1
|
"holiday_recess": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -5439,8 +5439,8 @@
|
|||||||
"holiday": 44,
|
"holiday": 44,
|
||||||
"holiday_or": 44,
|
"holiday_or": 44,
|
||||||
"holiday_overtime": 10,
|
"holiday_overtime": 10,
|
||||||
"holiday_today": 1,
|
"holiday_today": 2,
|
||||||
"holiday_legal": 1,
|
"holiday_legal": 2,
|
||||||
"holiday_recess": 1
|
"holiday_recess": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
267
main.js
267
main.js
@@ -1,10 +1,12 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 复旦微加班时间统计
|
// @name 复旦微加班时间统计
|
||||||
// @namespace http://start.fengbohan.com/
|
// @namespace http://start.fengbohan.com/
|
||||||
// @version 2026-01-28
|
// @version 2026-05-22
|
||||||
// @description 更新2026年的节假日数据
|
// @description 更新2026年的节假日数据,新增查当周、MyTools联动
|
||||||
// @author bhfeng
|
// @author bhfeng
|
||||||
// @match http://192.168.36.67:7888/shr/dynamic.do?uipk=com.kingdee.eas.hr.ats.app.WorkCalendarItem*
|
// @match http://192.168.36.67:7888/shr/dynamic.do?uipk=com.kingdee.eas.hr.ats.app.WorkCalendarItem*
|
||||||
|
// @updateURL https://gitea.fengbohan.com/fengbh/FM-OHS/raw/branch/main/main.js
|
||||||
|
// @downloadURL https://gitea.fengbohan.com/fengbh/FM-OHS/raw/branch/main/main.js
|
||||||
// @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==
|
// @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
|
// @grant none
|
||||||
// @run-at document-idle
|
// @run-at document-idle
|
||||||
@@ -21984,15 +21986,15 @@ const workDayData = {
|
|||||||
// 计算最大值和最小值
|
// 计算最大值和最小值
|
||||||
const maxTime = Math.max(...validTimes);
|
const maxTime = Math.max(...validTimes);
|
||||||
const minTime = Math.min(...validTimes);
|
const minTime = Math.min(...validTimes);
|
||||||
// 工作日从下午5点20分(17:20:00)开始计算加班
|
// 工作日加班起算时间(从配置获取)
|
||||||
const fiveTwentyPM = (17 * 3600 + 20 * 60) * 1000;
|
const overtimeStartMs = parseOvertimeStart(overtimeConfig.overtime_start);
|
||||||
let timeDifferenceInHours;
|
let timeDifferenceInHours;
|
||||||
|
|
||||||
// 计算加班时间
|
// 计算加班时间
|
||||||
if (workDay) {
|
if (workDay) {
|
||||||
// 工作日
|
// 工作日
|
||||||
if (maxTime >= fiveTwentyPM) {
|
if (maxTime >= overtimeStartMs) {
|
||||||
timeDifferenceInHours = (maxTime - fiveTwentyPM) / (1000 * 60 * 60);
|
timeDifferenceInHours = (maxTime - overtimeStartMs) / (1000 * 60 * 60);
|
||||||
} else {
|
} else {
|
||||||
timeDifferenceInHours = 0;
|
timeDifferenceInHours = 0;
|
||||||
}
|
}
|
||||||
@@ -22113,6 +22115,130 @@ const workDayData = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QUERY_DELAY_MS = 500;
|
const QUERY_DELAY_MS = 500;
|
||||||
|
const MYTOOLS_BASE = 'https://mytools.fengbohan.com';
|
||||||
|
|
||||||
|
// 当前显示月份的偏移量(0=当月周期,正数=往前翻,负数=往后翻)
|
||||||
|
let currentMonthOffset = 0;
|
||||||
|
|
||||||
|
// 加班配置(默认值,启动时尝试从 MyTools 拉取)
|
||||||
|
let overtimeConfig = { overtime_start: '17:20', weekly_target: 18 };
|
||||||
|
|
||||||
|
// 上次计算的结果缓存(用于保存到 MyTools)
|
||||||
|
let lastOvertimeRecords = [];
|
||||||
|
|
||||||
|
async function initConfig() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`${MYTOOLS_BASE}/api/overtime-config`, {
|
||||||
|
signal: AbortSignal.timeout(2000)
|
||||||
|
});
|
||||||
|
if (resp.ok) {
|
||||||
|
const data = await resp.json();
|
||||||
|
overtimeConfig = data;
|
||||||
|
console.log('FM-OHS: 已从 MyTools 拉取配置', overtimeConfig);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('FM-OHS: MyTools 不可用,使用默认配置');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析加班起算时间为秒数
|
||||||
|
function parseOvertimeStart(timeStr) {
|
||||||
|
const [h, m] = timeStr.split(':').map(Number);
|
||||||
|
return (h * 3600 + m * 60) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集表格数据(不修改 DOM,返回记录数组)
|
||||||
|
function collectOvertimeRecords() {
|
||||||
|
const table = document.getElementById('grid');
|
||||||
|
if (!table) return [];
|
||||||
|
const rows = table.getElementsByTagName('tr');
|
||||||
|
const records = [];
|
||||||
|
for (let i = 1; i < rows.length; i++) {
|
||||||
|
const cells = rows[i].getElementsByTagName('td');
|
||||||
|
if (cells.length > 1) {
|
||||||
|
const timeStr = cells[3].textContent;
|
||||||
|
const weekday = cells[2].textContent;
|
||||||
|
const date = cells[1].textContent;
|
||||||
|
const workDay = isWorkDay(weekday, date);
|
||||||
|
const hours = calculateTimeDifference(timeStr, workDay);
|
||||||
|
records.push({ date, weekday, hours: parseFloat(hours.toFixed(2)), workday: workDay });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toast 提示
|
||||||
|
function showToast(message, type) {
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.textContent = message;
|
||||||
|
Object.assign(toast.style, {
|
||||||
|
position: 'fixed', top: '20px', right: '20px', zIndex: '99999',
|
||||||
|
padding: '12px 20px', borderRadius: '6px', fontSize: '14px',
|
||||||
|
fontFamily: '"Microsoft Yahei", sans-serif',
|
||||||
|
color: '#fff', backgroundColor: type === 'success' ? '#28a745' : '#dc3545',
|
||||||
|
boxShadow: '0 4px 12px rgba(0,0,0,0.2)', transition: 'opacity 0.3s',
|
||||||
|
});
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
setTimeout(() => { toast.style.opacity = '0'; }, 2500);
|
||||||
|
setTimeout(() => { toast.remove(); }, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存加班记录到 MyTools
|
||||||
|
async function saveToMyTools() {
|
||||||
|
if (lastOvertimeRecords.length === 0) {
|
||||||
|
showToast('没有可保存的加班记录', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const total = lastOvertimeRecords.reduce((s, r) => s + r.hours, 0);
|
||||||
|
const weekLabel = (() => {
|
||||||
|
const d = new Date();
|
||||||
|
const iso = (d2 => {
|
||||||
|
const start = new Date(d2.getFullYear(), 0, 1);
|
||||||
|
const days = Math.floor((d2 - start) / 86400000);
|
||||||
|
return `${d2.getFullYear()}-W${String(Math.ceil((days + start.getDay() + 1) / 7)).padStart(2, '0')}`;
|
||||||
|
})(d);
|
||||||
|
return iso;
|
||||||
|
})();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`${MYTOOLS_BASE}/api/overtime-records`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ week: weekLabel, records: lastOvertimeRecords, total: parseFloat(total.toFixed(2)) }),
|
||||||
|
signal: AbortSignal.timeout(3000),
|
||||||
|
});
|
||||||
|
if (resp.ok) {
|
||||||
|
showToast(`已保存 ${lastOvertimeRecords.length} 条记录到 MyTools`, 'success');
|
||||||
|
} else {
|
||||||
|
showToast('保存失败: ' + resp.status, 'error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showToast('MyTools 不可用,请确认服务已启动', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建保存按钮(浮动在表格上方)
|
||||||
|
function showSaveButton() {
|
||||||
|
// 移除旧按钮
|
||||||
|
const old = document.getElementById('fmohs-save-btn');
|
||||||
|
if (old) old.remove();
|
||||||
|
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.id = 'fmohs-save-btn';
|
||||||
|
btn.textContent = '保存到 MyTools';
|
||||||
|
Object.assign(btn.style, {
|
||||||
|
margin: '8px 4px', padding: '6px 16px', fontSize: '13px',
|
||||||
|
backgroundColor: '#1f6feb', color: '#fff', border: 'none',
|
||||||
|
borderRadius: '4px', cursor: 'pointer', fontFamily: '"Microsoft Yahei", sans-serif',
|
||||||
|
});
|
||||||
|
btn.addEventListener('click', saveToMyTools);
|
||||||
|
|
||||||
|
// 插入到 grid 表格上方
|
||||||
|
const gridView = document.getElementById('gview_grid');
|
||||||
|
if (gridView) {
|
||||||
|
gridView.parentNode.insertBefore(btn, gridView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建菜单按钮
|
// 创建菜单按钮
|
||||||
function createMenuButton(text, options = {}) {
|
function createMenuButton(text, options = {}) {
|
||||||
@@ -22130,13 +22256,26 @@ const workDayData = {
|
|||||||
display: "block",
|
display: "block",
|
||||||
lineHeight: "60px",
|
lineHeight: "60px",
|
||||||
height: "60px",
|
height: "60px",
|
||||||
padding: "0px 10px",
|
padding: "0px 14px",
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
color: "#e5e5e5",
|
color: "#e5e5e5",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
|
border: "none",
|
||||||
|
borderBottom: "2px solid transparent",
|
||||||
|
transition: "color 0.15s, border-bottom-color 0.15s",
|
||||||
|
outline: "none",
|
||||||
};
|
};
|
||||||
Object.assign(button.style, copiedStyles);
|
Object.assign(button.style, copiedStyles);
|
||||||
|
|
||||||
|
button.addEventListener('mouseenter', () => {
|
||||||
|
button.style.color = "#fff";
|
||||||
|
button.style.borderBottomColor = "rgba(255,255,255,0.4)";
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseleave', () => {
|
||||||
|
button.style.color = "#e5e5e5";
|
||||||
|
button.style.borderBottomColor = "transparent";
|
||||||
|
});
|
||||||
|
|
||||||
// 处理点击事件回调
|
// 处理点击事件回调
|
||||||
if (typeof options.onClick === 'function') {
|
if (typeof options.onClick === 'function') {
|
||||||
button.addEventListener('click', options.onClick);
|
button.addEventListener('click', options.onClick);
|
||||||
@@ -22183,6 +22322,52 @@ const workDayData = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 基于当前偏移量动态导航月份(反复点击可逐月翻看历史)
|
||||||
|
function navigateMonth(delta) {
|
||||||
|
currentMonthOffset += delta;
|
||||||
|
const range = getCustomMonthRange(currentMonthOffset);
|
||||||
|
console.log(`navigateMonth offset=${currentMonthOffset}: ${range.firstDay} ~ ${range.lastDay}`);
|
||||||
|
simulateDateInput('beginDate', range.firstDay);
|
||||||
|
simulateDateInput('endDate', range.lastDay);
|
||||||
|
queryButton.click();
|
||||||
|
setTimeout(() => get_work_time(), QUERY_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据指定年月查询(考勤周期:上月21 ~ 本月20)
|
||||||
|
function queryByYearMonth(year, month) {
|
||||||
|
const formatLocal = (date) => {
|
||||||
|
return date.toLocaleDateString('en-CA', {
|
||||||
|
year: 'numeric', month: '2-digit', day: '2-digit'
|
||||||
|
}).replace(/\//g, '-');
|
||||||
|
};
|
||||||
|
// 考勤周期:当月21日 至 次月20日
|
||||||
|
let startMonth = month - 1; // JS month: 0-indexed
|
||||||
|
let startYear = year;
|
||||||
|
const firstDay = new Date(startYear, startMonth, 21);
|
||||||
|
let endYear = year, endMonth = month;
|
||||||
|
if (endMonth > 11) { endMonth -= 12; endYear++; }
|
||||||
|
const lastDay = new Date(endYear, endMonth, 20);
|
||||||
|
|
||||||
|
// 推算偏移量,让后续 navigateMonth 能正确工作
|
||||||
|
const now = new Date();
|
||||||
|
const currentCycleStart = getCustomMonthRange(0).firstDay;
|
||||||
|
const targetFirst = formatLocal(firstDay);
|
||||||
|
// 近似:计算目标月与当前月的月份差
|
||||||
|
const monthDiff = (now.getFullYear() - year) * 12 + (now.getMonth() + 1) - month;
|
||||||
|
const day = now.getDate();
|
||||||
|
if (day >= 21) {
|
||||||
|
currentMonthOffset = monthDiff;
|
||||||
|
} else {
|
||||||
|
currentMonthOffset = monthDiff - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`queryByYearMonth ${year}-${month}: ${targetFirst} ~ ${formatLocal(lastDay)}`);
|
||||||
|
simulateDateInput('beginDate', targetFirst);
|
||||||
|
simulateDateInput('endDate', formatLocal(lastDay));
|
||||||
|
queryButton.click();
|
||||||
|
setTimeout(() => get_work_time(), QUERY_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
// 查询工作时间
|
// 查询工作时间
|
||||||
function get_work_time() {
|
function get_work_time() {
|
||||||
const totalSum = sumOfSecondColumn('grid');
|
const totalSum = sumOfSecondColumn('grid');
|
||||||
@@ -22198,6 +22383,10 @@ const workDayData = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
titleCell.textContent = `加班时间(${totalSum.toFixed(2)}小时)`;
|
titleCell.textContent = `加班时间(${totalSum.toFixed(2)}小时)`;
|
||||||
|
|
||||||
|
// 收集记录并显示保存按钮
|
||||||
|
lastOvertimeRecords = collectOvertimeRecords();
|
||||||
|
showSaveButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryButton = document.getElementById("query");
|
const queryButton = document.getElementById("query");
|
||||||
@@ -22207,11 +22396,15 @@ const workDayData = {
|
|||||||
get_work_time();
|
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);
|
const button4 = createQueryButton('查当周', getCurrentWeekRange);
|
||||||
|
const button0 = createQueryButton('查当月', () => {
|
||||||
|
currentMonthOffset = 0;
|
||||||
|
navigateMonth(0);
|
||||||
|
});
|
||||||
|
const buttonNext = createQueryButton('查下月', () => navigateMonth(-1));
|
||||||
|
const buttonPrev = createQueryButton('查上月', () => navigateMonth(1));
|
||||||
|
const button2 = createQueryButton('查上上月', () => navigateMonth(2));
|
||||||
|
const button3 = createQueryButton('查上上上月', () => navigateMonth(3));
|
||||||
|
|
||||||
// 获取要将按钮添加到其中的容器
|
// 获取要将按钮添加到其中的容器
|
||||||
let container = document.getElementById('seclevelmenu');
|
let container = document.getElementById('seclevelmenu');
|
||||||
@@ -22219,11 +22412,59 @@ const workDayData = {
|
|||||||
container = document.body;
|
container = document.body;
|
||||||
}
|
}
|
||||||
container.appendChild(button);
|
container.appendChild(button);
|
||||||
|
container.appendChild(button4);
|
||||||
container.appendChild(button0);
|
container.appendChild(button0);
|
||||||
container.appendChild(button1);
|
container.appendChild(buttonNext);
|
||||||
|
container.appendChild(buttonPrev);
|
||||||
container.appendChild(button2);
|
container.appendChild(button2);
|
||||||
container.appendChild(button3);
|
container.appendChild(button3);
|
||||||
container.appendChild(button4);
|
|
||||||
|
// 年月选择器
|
||||||
|
(function createYearMonthSelector() {
|
||||||
|
const wrapper = document.createElement('span');
|
||||||
|
wrapper.style.cssText = 'display:inline-flex;align-items:center;gap:4px;margin:0 8px;';
|
||||||
|
|
||||||
|
const yearSel = document.createElement('select');
|
||||||
|
yearSel.id = 'fmohs-year';
|
||||||
|
const thisYear = new Date().getFullYear();
|
||||||
|
for (let y = 2024; y <= thisYear + 1; y++) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = y; opt.textContent = y + '年';
|
||||||
|
if (y === thisYear) opt.selected = true;
|
||||||
|
yearSel.appendChild(opt);
|
||||||
|
}
|
||||||
|
yearSel.style.cssText = 'padding:2px 6px;font-size:12px;border:1px solid #666;border-radius:3px;background:#333;color:#e5e5e5;font-family:inherit;cursor:pointer;';
|
||||||
|
|
||||||
|
const monthSel = document.createElement('select');
|
||||||
|
monthSel.id = 'fmohs-month';
|
||||||
|
for (let m = 1; m <= 12; m++) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = m; opt.textContent = m + '月';
|
||||||
|
if (m === new Date().getMonth() + 1) opt.selected = true;
|
||||||
|
monthSel.appendChild(opt);
|
||||||
|
}
|
||||||
|
monthSel.style.cssText = yearSel.style.cssText;
|
||||||
|
|
||||||
|
const queryBtn = document.createElement('button');
|
||||||
|
queryBtn.textContent = '查询';
|
||||||
|
queryBtn.style.cssText = 'padding:2px 12px;font-size:12px;color:#e5e5e5;background:transparent;border:1px solid #666;border-radius:3px;cursor:pointer;font-family:inherit;transition:color 0.15s,border-color 0.15s;';
|
||||||
|
queryBtn.addEventListener('mouseenter', () => { queryBtn.style.color = '#fff'; queryBtn.style.borderColor = '#999'; });
|
||||||
|
queryBtn.addEventListener('mouseleave', () => { queryBtn.style.color = '#e5e5e5'; queryBtn.style.borderColor = '#666'; });
|
||||||
|
queryBtn.addEventListener('click', () => {
|
||||||
|
const y = parseInt(yearSel.value);
|
||||||
|
const m = parseInt(monthSel.value);
|
||||||
|
queryByYearMonth(y, m);
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.appendChild(yearSel);
|
||||||
|
wrapper.appendChild(monthSel);
|
||||||
|
wrapper.appendChild(queryBtn);
|
||||||
|
container.appendChild(wrapper);
|
||||||
|
})();
|
||||||
|
|
||||||
console.log("复旦微加班时间统计脚本已加载");
|
console.log("复旦微加班时间统计脚本已加载");
|
||||||
console.log("点击按钮后会统计加班时间");
|
console.log("点击按钮后会统计加班时间");
|
||||||
|
|
||||||
|
// 启动时拉取配置(非阻塞)
|
||||||
|
initConfig();
|
||||||
})()
|
})()
|
||||||
267
main.template.js
267
main.template.js
@@ -1,10 +1,12 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 复旦微加班时间统计
|
// @name 复旦微加班时间统计
|
||||||
// @namespace http://start.fengbohan.com/
|
// @namespace http://start.fengbohan.com/
|
||||||
// @version 2026-01-28
|
// @version 2026-05-22
|
||||||
// @description 更新2026年的节假日数据
|
// @description 更新2026年的节假日数据,新增查当周、MyTools联动
|
||||||
// @author bhfeng
|
// @author bhfeng
|
||||||
// @match http://192.168.36.67:7888/shr/dynamic.do?uipk=com.kingdee.eas.hr.ats.app.WorkCalendarItem*
|
// @match http://192.168.36.67:7888/shr/dynamic.do?uipk=com.kingdee.eas.hr.ats.app.WorkCalendarItem*
|
||||||
|
// @updateURL https://gitea.fengbohan.com/fengbh/FM-OHS/raw/branch/main/main.js
|
||||||
|
// @downloadURL https://gitea.fengbohan.com/fengbh/FM-OHS/raw/branch/main/main.js
|
||||||
// @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==
|
// @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
|
// @grant none
|
||||||
// @run-at document-idle
|
// @run-at document-idle
|
||||||
@@ -61,15 +63,15 @@
|
|||||||
// 计算最大值和最小值
|
// 计算最大值和最小值
|
||||||
const maxTime = Math.max(...validTimes);
|
const maxTime = Math.max(...validTimes);
|
||||||
const minTime = Math.min(...validTimes);
|
const minTime = Math.min(...validTimes);
|
||||||
// 工作日从下午5点20分(17:20:00)开始计算加班
|
// 工作日加班起算时间(从配置获取)
|
||||||
const fiveTwentyPM = (17 * 3600 + 20 * 60) * 1000;
|
const overtimeStartMs = parseOvertimeStart(overtimeConfig.overtime_start);
|
||||||
let timeDifferenceInHours;
|
let timeDifferenceInHours;
|
||||||
|
|
||||||
// 计算加班时间
|
// 计算加班时间
|
||||||
if (workDay) {
|
if (workDay) {
|
||||||
// 工作日
|
// 工作日
|
||||||
if (maxTime >= fiveTwentyPM) {
|
if (maxTime >= overtimeStartMs) {
|
||||||
timeDifferenceInHours = (maxTime - fiveTwentyPM) / (1000 * 60 * 60);
|
timeDifferenceInHours = (maxTime - overtimeStartMs) / (1000 * 60 * 60);
|
||||||
} else {
|
} else {
|
||||||
timeDifferenceInHours = 0;
|
timeDifferenceInHours = 0;
|
||||||
}
|
}
|
||||||
@@ -190,6 +192,130 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QUERY_DELAY_MS = 500;
|
const QUERY_DELAY_MS = 500;
|
||||||
|
const MYTOOLS_BASE = 'https://mytools.fengbohan.com';
|
||||||
|
|
||||||
|
// 当前显示月份的偏移量(0=当月周期,正数=往前翻,负数=往后翻)
|
||||||
|
let currentMonthOffset = 0;
|
||||||
|
|
||||||
|
// 加班配置(默认值,启动时尝试从 MyTools 拉取)
|
||||||
|
let overtimeConfig = { overtime_start: '17:20', weekly_target: 18 };
|
||||||
|
|
||||||
|
// 上次计算的结果缓存(用于保存到 MyTools)
|
||||||
|
let lastOvertimeRecords = [];
|
||||||
|
|
||||||
|
async function initConfig() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`${MYTOOLS_BASE}/api/overtime-config`, {
|
||||||
|
signal: AbortSignal.timeout(2000)
|
||||||
|
});
|
||||||
|
if (resp.ok) {
|
||||||
|
const data = await resp.json();
|
||||||
|
overtimeConfig = data;
|
||||||
|
console.log('FM-OHS: 已从 MyTools 拉取配置', overtimeConfig);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('FM-OHS: MyTools 不可用,使用默认配置');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析加班起算时间为秒数
|
||||||
|
function parseOvertimeStart(timeStr) {
|
||||||
|
const [h, m] = timeStr.split(':').map(Number);
|
||||||
|
return (h * 3600 + m * 60) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集表格数据(不修改 DOM,返回记录数组)
|
||||||
|
function collectOvertimeRecords() {
|
||||||
|
const table = document.getElementById('grid');
|
||||||
|
if (!table) return [];
|
||||||
|
const rows = table.getElementsByTagName('tr');
|
||||||
|
const records = [];
|
||||||
|
for (let i = 1; i < rows.length; i++) {
|
||||||
|
const cells = rows[i].getElementsByTagName('td');
|
||||||
|
if (cells.length > 1) {
|
||||||
|
const timeStr = cells[3].textContent;
|
||||||
|
const weekday = cells[2].textContent;
|
||||||
|
const date = cells[1].textContent;
|
||||||
|
const workDay = isWorkDay(weekday, date);
|
||||||
|
const hours = calculateTimeDifference(timeStr, workDay);
|
||||||
|
records.push({ date, weekday, hours: parseFloat(hours.toFixed(2)), workday: workDay });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toast 提示
|
||||||
|
function showToast(message, type) {
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.textContent = message;
|
||||||
|
Object.assign(toast.style, {
|
||||||
|
position: 'fixed', top: '20px', right: '20px', zIndex: '99999',
|
||||||
|
padding: '12px 20px', borderRadius: '6px', fontSize: '14px',
|
||||||
|
fontFamily: '"Microsoft Yahei", sans-serif',
|
||||||
|
color: '#fff', backgroundColor: type === 'success' ? '#28a745' : '#dc3545',
|
||||||
|
boxShadow: '0 4px 12px rgba(0,0,0,0.2)', transition: 'opacity 0.3s',
|
||||||
|
});
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
setTimeout(() => { toast.style.opacity = '0'; }, 2500);
|
||||||
|
setTimeout(() => { toast.remove(); }, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存加班记录到 MyTools
|
||||||
|
async function saveToMyTools() {
|
||||||
|
if (lastOvertimeRecords.length === 0) {
|
||||||
|
showToast('没有可保存的加班记录', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const total = lastOvertimeRecords.reduce((s, r) => s + r.hours, 0);
|
||||||
|
const weekLabel = (() => {
|
||||||
|
const d = new Date();
|
||||||
|
const iso = (d2 => {
|
||||||
|
const start = new Date(d2.getFullYear(), 0, 1);
|
||||||
|
const days = Math.floor((d2 - start) / 86400000);
|
||||||
|
return `${d2.getFullYear()}-W${String(Math.ceil((days + start.getDay() + 1) / 7)).padStart(2, '0')}`;
|
||||||
|
})(d);
|
||||||
|
return iso;
|
||||||
|
})();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`${MYTOOLS_BASE}/api/overtime-records`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ week: weekLabel, records: lastOvertimeRecords, total: parseFloat(total.toFixed(2)) }),
|
||||||
|
signal: AbortSignal.timeout(3000),
|
||||||
|
});
|
||||||
|
if (resp.ok) {
|
||||||
|
showToast(`已保存 ${lastOvertimeRecords.length} 条记录到 MyTools`, 'success');
|
||||||
|
} else {
|
||||||
|
showToast('保存失败: ' + resp.status, 'error');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showToast('MyTools 不可用,请确认服务已启动', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建保存按钮(浮动在表格上方)
|
||||||
|
function showSaveButton() {
|
||||||
|
// 移除旧按钮
|
||||||
|
const old = document.getElementById('fmohs-save-btn');
|
||||||
|
if (old) old.remove();
|
||||||
|
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.id = 'fmohs-save-btn';
|
||||||
|
btn.textContent = '保存到 MyTools';
|
||||||
|
Object.assign(btn.style, {
|
||||||
|
margin: '8px 4px', padding: '6px 16px', fontSize: '13px',
|
||||||
|
backgroundColor: '#1f6feb', color: '#fff', border: 'none',
|
||||||
|
borderRadius: '4px', cursor: 'pointer', fontFamily: '"Microsoft Yahei", sans-serif',
|
||||||
|
});
|
||||||
|
btn.addEventListener('click', saveToMyTools);
|
||||||
|
|
||||||
|
// 插入到 grid 表格上方
|
||||||
|
const gridView = document.getElementById('gview_grid');
|
||||||
|
if (gridView) {
|
||||||
|
gridView.parentNode.insertBefore(btn, gridView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建菜单按钮
|
// 创建菜单按钮
|
||||||
function createMenuButton(text, options = {}) {
|
function createMenuButton(text, options = {}) {
|
||||||
@@ -207,13 +333,26 @@
|
|||||||
display: "block",
|
display: "block",
|
||||||
lineHeight: "60px",
|
lineHeight: "60px",
|
||||||
height: "60px",
|
height: "60px",
|
||||||
padding: "0px 10px",
|
padding: "0px 14px",
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
color: "#e5e5e5",
|
color: "#e5e5e5",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
|
border: "none",
|
||||||
|
borderBottom: "2px solid transparent",
|
||||||
|
transition: "color 0.15s, border-bottom-color 0.15s",
|
||||||
|
outline: "none",
|
||||||
};
|
};
|
||||||
Object.assign(button.style, copiedStyles);
|
Object.assign(button.style, copiedStyles);
|
||||||
|
|
||||||
|
button.addEventListener('mouseenter', () => {
|
||||||
|
button.style.color = "#fff";
|
||||||
|
button.style.borderBottomColor = "rgba(255,255,255,0.4)";
|
||||||
|
});
|
||||||
|
button.addEventListener('mouseleave', () => {
|
||||||
|
button.style.color = "#e5e5e5";
|
||||||
|
button.style.borderBottomColor = "transparent";
|
||||||
|
});
|
||||||
|
|
||||||
// 处理点击事件回调
|
// 处理点击事件回调
|
||||||
if (typeof options.onClick === 'function') {
|
if (typeof options.onClick === 'function') {
|
||||||
button.addEventListener('click', options.onClick);
|
button.addEventListener('click', options.onClick);
|
||||||
@@ -260,6 +399,52 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 基于当前偏移量动态导航月份(反复点击可逐月翻看历史)
|
||||||
|
function navigateMonth(delta) {
|
||||||
|
currentMonthOffset += delta;
|
||||||
|
const range = getCustomMonthRange(currentMonthOffset);
|
||||||
|
console.log(`navigateMonth offset=${currentMonthOffset}: ${range.firstDay} ~ ${range.lastDay}`);
|
||||||
|
simulateDateInput('beginDate', range.firstDay);
|
||||||
|
simulateDateInput('endDate', range.lastDay);
|
||||||
|
queryButton.click();
|
||||||
|
setTimeout(() => get_work_time(), QUERY_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据指定年月查询(考勤周期:上月21 ~ 本月20)
|
||||||
|
function queryByYearMonth(year, month) {
|
||||||
|
const formatLocal = (date) => {
|
||||||
|
return date.toLocaleDateString('en-CA', {
|
||||||
|
year: 'numeric', month: '2-digit', day: '2-digit'
|
||||||
|
}).replace(/\//g, '-');
|
||||||
|
};
|
||||||
|
// 考勤周期:当月21日 至 次月20日
|
||||||
|
let startMonth = month - 1; // JS month: 0-indexed
|
||||||
|
let startYear = year;
|
||||||
|
const firstDay = new Date(startYear, startMonth, 21);
|
||||||
|
let endYear = year, endMonth = month;
|
||||||
|
if (endMonth > 11) { endMonth -= 12; endYear++; }
|
||||||
|
const lastDay = new Date(endYear, endMonth, 20);
|
||||||
|
|
||||||
|
// 推算偏移量,让后续 navigateMonth 能正确工作
|
||||||
|
const now = new Date();
|
||||||
|
const currentCycleStart = getCustomMonthRange(0).firstDay;
|
||||||
|
const targetFirst = formatLocal(firstDay);
|
||||||
|
// 近似:计算目标月与当前月的月份差
|
||||||
|
const monthDiff = (now.getFullYear() - year) * 12 + (now.getMonth() + 1) - month;
|
||||||
|
const day = now.getDate();
|
||||||
|
if (day >= 21) {
|
||||||
|
currentMonthOffset = monthDiff;
|
||||||
|
} else {
|
||||||
|
currentMonthOffset = monthDiff - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`queryByYearMonth ${year}-${month}: ${targetFirst} ~ ${formatLocal(lastDay)}`);
|
||||||
|
simulateDateInput('beginDate', targetFirst);
|
||||||
|
simulateDateInput('endDate', formatLocal(lastDay));
|
||||||
|
queryButton.click();
|
||||||
|
setTimeout(() => get_work_time(), QUERY_DELAY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
// 查询工作时间
|
// 查询工作时间
|
||||||
function get_work_time() {
|
function get_work_time() {
|
||||||
const totalSum = sumOfSecondColumn('grid');
|
const totalSum = sumOfSecondColumn('grid');
|
||||||
@@ -275,6 +460,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
titleCell.textContent = `加班时间(${totalSum.toFixed(2)}小时)`;
|
titleCell.textContent = `加班时间(${totalSum.toFixed(2)}小时)`;
|
||||||
|
|
||||||
|
// 收集记录并显示保存按钮
|
||||||
|
lastOvertimeRecords = collectOvertimeRecords();
|
||||||
|
showSaveButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryButton = document.getElementById("query");
|
const queryButton = document.getElementById("query");
|
||||||
@@ -284,11 +473,15 @@
|
|||||||
get_work_time();
|
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);
|
const button4 = createQueryButton('查当周', getCurrentWeekRange);
|
||||||
|
const button0 = createQueryButton('查当月', () => {
|
||||||
|
currentMonthOffset = 0;
|
||||||
|
navigateMonth(0);
|
||||||
|
});
|
||||||
|
const buttonNext = createQueryButton('查下月', () => navigateMonth(-1));
|
||||||
|
const buttonPrev = createQueryButton('查上月', () => navigateMonth(1));
|
||||||
|
const button2 = createQueryButton('查上上月', () => navigateMonth(2));
|
||||||
|
const button3 = createQueryButton('查上上上月', () => navigateMonth(3));
|
||||||
|
|
||||||
// 获取要将按钮添加到其中的容器
|
// 获取要将按钮添加到其中的容器
|
||||||
let container = document.getElementById('seclevelmenu');
|
let container = document.getElementById('seclevelmenu');
|
||||||
@@ -296,11 +489,59 @@
|
|||||||
container = document.body;
|
container = document.body;
|
||||||
}
|
}
|
||||||
container.appendChild(button);
|
container.appendChild(button);
|
||||||
|
container.appendChild(button4);
|
||||||
container.appendChild(button0);
|
container.appendChild(button0);
|
||||||
container.appendChild(button1);
|
container.appendChild(buttonNext);
|
||||||
|
container.appendChild(buttonPrev);
|
||||||
container.appendChild(button2);
|
container.appendChild(button2);
|
||||||
container.appendChild(button3);
|
container.appendChild(button3);
|
||||||
container.appendChild(button4);
|
|
||||||
|
// 年月选择器
|
||||||
|
(function createYearMonthSelector() {
|
||||||
|
const wrapper = document.createElement('span');
|
||||||
|
wrapper.style.cssText = 'display:inline-flex;align-items:center;gap:4px;margin:0 8px;';
|
||||||
|
|
||||||
|
const yearSel = document.createElement('select');
|
||||||
|
yearSel.id = 'fmohs-year';
|
||||||
|
const thisYear = new Date().getFullYear();
|
||||||
|
for (let y = 2024; y <= thisYear + 1; y++) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = y; opt.textContent = y + '年';
|
||||||
|
if (y === thisYear) opt.selected = true;
|
||||||
|
yearSel.appendChild(opt);
|
||||||
|
}
|
||||||
|
yearSel.style.cssText = 'padding:2px 6px;font-size:12px;border:1px solid #666;border-radius:3px;background:#333;color:#e5e5e5;font-family:inherit;cursor:pointer;';
|
||||||
|
|
||||||
|
const monthSel = document.createElement('select');
|
||||||
|
monthSel.id = 'fmohs-month';
|
||||||
|
for (let m = 1; m <= 12; m++) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = m; opt.textContent = m + '月';
|
||||||
|
if (m === new Date().getMonth() + 1) opt.selected = true;
|
||||||
|
monthSel.appendChild(opt);
|
||||||
|
}
|
||||||
|
monthSel.style.cssText = yearSel.style.cssText;
|
||||||
|
|
||||||
|
const queryBtn = document.createElement('button');
|
||||||
|
queryBtn.textContent = '查询';
|
||||||
|
queryBtn.style.cssText = 'padding:2px 12px;font-size:12px;color:#e5e5e5;background:transparent;border:1px solid #666;border-radius:3px;cursor:pointer;font-family:inherit;transition:color 0.15s,border-color 0.15s;';
|
||||||
|
queryBtn.addEventListener('mouseenter', () => { queryBtn.style.color = '#fff'; queryBtn.style.borderColor = '#999'; });
|
||||||
|
queryBtn.addEventListener('mouseleave', () => { queryBtn.style.color = '#e5e5e5'; queryBtn.style.borderColor = '#666'; });
|
||||||
|
queryBtn.addEventListener('click', () => {
|
||||||
|
const y = parseInt(yearSel.value);
|
||||||
|
const m = parseInt(monthSel.value);
|
||||||
|
queryByYearMonth(y, m);
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.appendChild(yearSel);
|
||||||
|
wrapper.appendChild(monthSel);
|
||||||
|
wrapper.appendChild(queryBtn);
|
||||||
|
container.appendChild(wrapper);
|
||||||
|
})();
|
||||||
|
|
||||||
console.log("复旦微加班时间统计脚本已加载");
|
console.log("复旦微加班时间统计脚本已加载");
|
||||||
console.log("点击按钮后会统计加班时间");
|
console.log("点击按钮后会统计加班时间");
|
||||||
|
|
||||||
|
// 启动时拉取配置(非阻塞)
|
||||||
|
initConfig();
|
||||||
})()
|
})()
|
||||||
Reference in New Issue
Block a user