// ==UserScript== // @name 汉江师范学院官网助手 // @name:en HJNU Portal Helper // @namespace https://www.hjnu.edu.cn/ // @version 1.0.0 // @description 汉江师范学院官网增强:通知公告新公告监控(桌面提醒)、通知/要闻聚合速览、全校快捷导航、夜间模式、文章阅读增强、链接新标签打开。 // @description:en Enhancements for hjnu.edu.cn: new-notice monitor with desktop notification, news aggregator, quick navigation, dark mode, reading enhancements. // @author WorkBuddy // @match *://www.hjnu.edu.cn/* // @match *://jwc.hjnu.edu.cn/* // @match *://lib.hjnu.edu.cn/* // @match *://xxgk.hjnu.edu.cn/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // @grant GM_setClipboard // @grant GM_registerMenuCommand // @connect www.hjnu.edu.cn // @connect jwc.hjnu.edu.cn // @connect lib.hjnu.edu.cn // @connect xxgk.hjnu.edu.cn // @run-at document-end // @noframes // @license MIT // ==/UserScript== (function () { 'use strict'; /* ================= 常量 ================= */ var BASE = 'https://www.hjnu.edu.cn/'; var IS_WWW = location.hostname === 'www.hjnu.edu.cn' || location.hostname === 'hjnu.edu.cn'; var IS_ARTICLE = /\/info\/\d+\/\d+\.htm$/.test(location.pathname); var URLS = { tzgg: BASE + 'index/tzgg.htm', // 通知公告 (col 1045) hsyw: BASE + 'index/hsyw.htm', // 汉师要闻 (col 1046) zhxw: BASE + 'index/zhxw.htm', // 综合新闻 (col 1108) xzxs: BASE + 'index/xzxs.htm', // 学者学术 (col 1043) xsxy: BASE + 'index/xsxy.htm' // 学生学涯 (col 1044) }; // 已在浏览器中逐一验证过的真实入口 var NAV_LINKS = [ { g: '资讯栏目', items: [ { t: '学校首页', u: BASE }, { t: '通知公告', u: URLS.tzgg }, { t: '汉师要闻', u: URLS.hsyw }, { t: '综合新闻', u: URLS.zhxw }, { t: '学者学术', u: URLS.xzxs }, { t: '学生学涯', u: URLS.xsxy }, { t: '媒体汉师', u: BASE + 'index/mtgz.htm' }, { t: '理论聚焦', u: BASE + 'index/lljj.htm' } ]}, { g: '常用服务', items: [ { t: '教务处', u: 'https://jwc.hjnu.edu.cn/' }, { t: '教学服务(教务系统)', u: 'http://jwsys.hjnu.edu.cn/' }, { t: '办事大厅(校内)', u: 'https://ehall.hjnu.edu.cn/default/index.html#/' }, { t: '办事大厅(校外/WebVPN)', u: 'https://webvpn.hjnu.edu.cn/login' }, { t: '图书馆', u: 'https://lib.hjnu.edu.cn/' }, { t: '信息公开', u: 'https://xxgk.hjnu.edu.cn/' }, { t: '邮箱入口', u: 'https://mail.hjnu.edu.cn/' }, { t: '校长信箱', u: BASE + 'info/1045/11019.htm' } ]} ]; /* ================= 设置 ================= */ var SKEY = 'hjnu_settings_v1'; var DEFAULTS = { dark: false, // 夜间模式 newTab: true, // 列表文章链接新标签打开 relTime: false, // 显示相对时间(X天前) monitor: true, // 通知公告监控 interval: 30, // 监控间隔(分钟) zoom: 1, // 文章正文字号缩放 ballPos: null // 悬浮球位置 }; var settings = loadSettings(); function loadSettings() { var s = {}; try { s = JSON.parse(GM_getValue(SKEY, '{}')) || {}; } catch (e) { s = {}; } var out = {}; Object.keys(DEFAULTS).forEach(function (k) { out[k] = (s[k] === undefined) ? DEFAULTS[k] : s[k]; }); return out; } function saveSettings() { GM_setValue(SKEY, JSON.stringify(settings)); } /* ================= 工具函数 ================= */ function fetchDoc(url) { return new Promise(function (resolve, reject) { GM_xmlhttpRequest({ method: 'GET', url: url, timeout: 20000, onload: function (r) { if (r.status >= 200 && r.status < 400) { resolve(new DOMParser().parseFromString(r.responseText, 'text/html')); } else { reject(new Error('HTTP ' + r.status)); } }, onerror: function () { reject(new Error('network error')); }, ontimeout: function () { reject(new Error('timeout')); } }); }); } // 解析 .n_list 列表页:li > a > time(em 年月 + span 日) + div.txt > h2 标题 function parseColumn(doc, colId) { var seen = {}, out = []; var anchors = doc.querySelectorAll('.n_list li a[href], li a[href*="info/' + colId + '/"]'); Array.prototype.forEach.call(anchors, function (a) { var raw = a.getAttribute('href') || ''; var abs; try { abs = new URL(raw, BASE).href; } catch (e) { abs = raw; } if (seen[abs]) return; var m = raw.match(/info\/(\d+)\/(\d+)\.htm/); var id = m ? parseInt(m[2], 10) : null; var time = a.querySelector('time'); var date = ''; if (time) { var em = time.querySelector('em'), sp = time.querySelector('span'); if (em && sp) date = em.textContent.trim() + '-' + sp.textContent.trim(); } var h2 = a.querySelector('h2, h1'); var title = (h2 ? h2.textContent : a.textContent).trim().replace(/\s+/g, ' '); if (!title) return; // 去掉摘要干扰:h2 存在时取 h2 seen[abs] = 1; out.push({ id: id, title: title, url: abs, date: date }); }); return out; } function relTime(dateStr) { var d = new Date(String(dateStr).replace(/-/g, '/')); if (isNaN(d.getTime())) return null; var diff = Date.now() - d.getTime(); if (diff < 0) return null; var days = Math.floor(diff / 86400000); if (days <= 0) return '今天'; if (days === 1) return '昨天'; if (days < 30) return days + ' 天前'; var mo = Math.floor(days / 30); if (mo < 12) return mo + ' 个月前'; return Math.floor(mo / 12) + ' 年前'; } function toast(msg, ms) { var t = document.createElement('div'); t.className = 'hjnu-toast'; t.textContent = msg; document.body.appendChild(t); setTimeout(function () { t.classList.add('show'); }, 10); setTimeout(function () { t.classList.remove('show'); setTimeout(function () { t.remove(); }, 300); }, ms || 2600); } /* ================= 样式 ================= */ var css = [ // ---- 悬浮球 ---- '#hjnu-ball{position:fixed;z-index:2147483600;right:24px;bottom:96px;width:48px;height:48px;border-radius:50%;', 'background:linear-gradient(135deg,#1e5799,#2989d8);color:#fff;font:600 13px/48px "Microsoft YaHei",sans-serif;', 'text-align:center;cursor:pointer;box-shadow:0 4px 14px rgba(30,87,153,.45);user-select:none;transition:transform .15s;}', '#hjnu-ball:hover{transform:scale(1.08);}', '#hjnu-ball .hjnu-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;border-radius:9px;', 'background:#e53935;color:#fff;font:600 11px/18px Arial,sans-serif;padding:0 4px;box-sizing:border-box;display:none;}', // ---- 面板 ---- '#hjnu-panel{position:fixed;z-index:2147483601;right:24px;bottom:156px;width:370px;max-height:min(560px,72vh);', 'background:#fff;border-radius:14px;box-shadow:0 12px 40px rgba(0,0,0,.22);display:none;flex-direction:column;', 'overflow:hidden;font:14px/1.6 "Microsoft YaHei",sans-serif;color:#333;}', '#hjnu-panel.open{display:flex;}', '#hjnu-panel .hp-head{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;', 'background:linear-gradient(135deg,#1e5799,#2989d8);color:#fff;}', '#hjnu-panel .hp-head b{font-size:15px;}', '#hjnu-panel .hp-head .hp-close{cursor:pointer;font-size:18px;opacity:.85;padding:0 4px;}', '#hjnu-panel .hp-head .hp-close:hover{opacity:1;}', '#hjnu-panel .hp-tabs{display:flex;border-bottom:1px solid #ececec;background:#fafafa;}', '#hjnu-panel .hp-tabs div{flex:1;text-align:center;padding:9px 0;cursor:pointer;font-size:13px;color:#666;}', '#hjnu-panel .hp-tabs div.on{color:#1e5799;font-weight:600;border-bottom:2px solid #1e5799;background:#fff;}', '#hjnu-panel .hp-body{overflow-y:auto;padding:10px 14px 14px;}', // ---- 导航 ---- '#hjnu-panel .hp-nav h4{margin:10px 0 6px;font-size:12px;color:#999;font-weight:600;letter-spacing:1px;}', '#hjnu-panel .hp-nav .hp-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;}', '#hjnu-panel .hp-nav a{display:block;padding:8px 10px;border-radius:8px;background:#f2f6fa;color:#2b4f77;', 'font-size:13px;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}', '#hjnu-panel .hp-nav a:hover{background:#e1ecf7;}', // ---- 资讯列表 ---- '#hjnu-panel .hp-news .hp-sub{display:flex;justify-content:space-between;align-items:center;margin:8px 0 4px;}', '#hjnu-panel .hp-news .hp-sub b{font-size:13px;color:#1e5799;}', '#hjnu-panel .hp-news .hp-sub .hp-refresh{font-size:12px;color:#999;cursor:pointer;}', '#hjnu-panel .hp-news .hp-sub .hp-refresh:hover{color:#1e5799;}', '#hjnu-panel .hp-news ul{list-style:none;margin:0;padding:0;}', '#hjnu-panel .hp-news li{border-bottom:1px dashed #eee;}', '#hjnu-panel .hp-news li a{display:flex;gap:8px;padding:8px 2px;text-decoration:none;color:#333;font-size:13px;align-items:baseline;}', '#hjnu-panel .hp-news li a:hover{color:#1e5799;}', '#hjnu-panel .hp-news li .d{flex:none;color:#999;font-size:11px;font-family:Consolas,monospace;min-width:72px;}', '#hjnu-panel .hp-news li .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}', '#hjnu-panel .hp-news li .t.new{color:#c62828;}', '#hjnu-panel .hp-news .hp-tip{color:#999;font-size:12px;text-align:center;padding:14px 0;}', // ---- 设置 ---- '#hjnu-panel .hp-set .row{display:flex;justify-content:space-between;align-items:center;padding:10px 2px;border-bottom:1px dashed #eee;font-size:13px;}', '#hjnu-panel .hp-set .row input[type=checkbox]{width:16px;height:16px;accent-color:#1e5799;}', '#hjnu-panel .hp-set .row select{padding:2px 6px;border:1px solid #ddd;border-radius:6px;font-size:12px;}', '#hjnu-panel .hp-set .foot{margin-top:10px;font-size:11px;color:#bbb;text-align:center;}', // ---- 文章阅读工具条 ---- '#hjnu-readbar{position:fixed;z-index:2147483600;left:50%;transform:translateX(-50%);bottom:18px;display:none;', 'gap:6px;background:rgba(20,40,60,.88);backdrop-filter:blur(6px);border-radius:24px;padding:7px 10px;}', '#hjnu-readbar span{color:#fff;font:12px/1 "Microsoft YaHei",sans-serif;padding:7px 10px;border-radius:16px;', 'cursor:pointer;user-select:none;white-space:nowrap;}', '#hjnu-readbar span:hover{background:rgba(255,255,255,.18);}', // ---- 返回顶部 ---- '#hjnu-top{position:fixed;z-index:2147483599;right:24px;bottom:40px;width:40px;height:40px;border-radius:50%;', 'background:rgba(30,87,153,.85);color:#fff;font:600 12px/40px "Microsoft YaHei",sans-serif;text-align:center;', 'cursor:pointer;display:none;box-shadow:0 3px 10px rgba(0,0,0,.25);}', '#hjnu-top:hover{background:rgba(30,87,153,1);}', // ---- Toast ---- '.hjnu-toast{position:fixed;z-index:2147483647;left:50%;top:8%;transform:translateX(-50%) translateY(-8px);', 'background:rgba(20,40,60,.92);color:#fff;padding:9px 18px;border-radius:20px;font:13px/1.4 "Microsoft YaHei",sans-serif;', 'opacity:0;transition:all .25s;pointer-events:none;max-width:80vw;}', '.hjnu-toast.show{opacity:1;transform:translateX(-50%) translateY(0);}', // ---- 相对时间 ---- '.hjnu-rel{color:#c62828;font-size:.85em;font-style:normal;margin-left:4px;}', // ---- 夜间模式 ---- 'html.hjnu-dark{filter:invert(1) hue-rotate(180deg);background:#fff;}', 'html.hjnu-dark img,html.hjnu-dark video,html.hjnu-dark iframe,html.hjnu-dark svg,html.hjnu-dark canvas,', 'html.hjnu-dark [style*="background-image"]{filter:invert(1) hue-rotate(180deg);}', 'html.hjnu-dark #hjnu-ball,html.hjnu-dark #hjnu-panel,html.hjnu-dark #hjnu-readbar,html.hjnu-dark #hjnu-top,', 'html.hjnu-dark .hjnu-toast{filter:invert(1) hue-rotate(180deg);}' ].join(''); var styleEl = document.createElement('style'); styleEl.id = 'hjnu-style'; styleEl.textContent = css; document.head.appendChild(styleEl); /* ================= 夜间模式 ================= */ function applyDark() { document.documentElement.classList.toggle('hjnu-dark', !!settings.dark); } function toggleDark() { settings.dark = !settings.dark; saveSettings(); applyDark(); syncSetUI(); toast(settings.dark ? '夜间模式已开启' : '夜间模式已关闭'); } /* ================= 新标签打开 ================= */ function applyNewTab() { if (!settings.newTab) return; var links = document.querySelectorAll('a[href]'); Array.prototype.forEach.call(links, function (a) { if (/\/info\/\d+\/\d+\.htm/.test(a.getAttribute('href') || '')) { a.target = '_blank'; a.rel = 'noopener'; } }); } /* ================= 相对时间 ================= */ function applyRelTime() { if (!settings.relTime) return; // 1) 列表页 time 结构(em 年月 + span 日) document.querySelectorAll('.n_list li a').forEach(function (a) { if (a.querySelector('.hjnu-rel')) return; var t = a.querySelector('time'); if (!t) return; var em = t.querySelector('em'), sp = t.querySelector('span'); if (!em || !sp) return; var d = em.textContent.trim() + '-' + sp.textContent.trim(); var r = relTime(d); if (r) t.insertAdjacentHTML('beforeend', '' + r + ''); }); // 2) 普通日期文本(如首页 “2026-08-24”) var els = document.querySelectorAll('span,em,td,p'); var n = 0; for (var i = 0; i < els.length && n < 200; i++) { var el = els[i]; if (el.querySelector('.hjnu-rel') || (el.parentElement && el.parentElement.querySelector('.hjnu-rel'))) continue; var txt = (el.textContent || '').trim(); if (/^\d{4}-\d{2}-\d{2}$/.test(txt)) { var rr = relTime(txt); if (rr) { el.insertAdjacentHTML('beforeend', '' + rr + ''); n++; } } } } /* ================= UI:悬浮球 + 面板 ================= */ var ball, panel, badge = 0; function buildUI() { ball = document.createElement('div'); ball.id = 'hjnu-ball'; ball.title = '汉师助手'; ball.innerHTML = '汉师'; document.body.appendChild(ball); panel = document.createElement('div'); panel.id = 'hjnu-panel'; panel.innerHTML = buildPanelHTML(); document.body.appendChild(panel); // 恢复悬浮球位置 if (settings.ballPos) { ball.style.left = settings.ballPos.x + 'px'; ball.style.top = settings.ballPos.y + 'px'; ball.style.right = 'auto'; ball.style.bottom = 'auto'; } bindBall(); bindPanel(); } function buildPanelHTML() { var navHtml = NAV_LINKS.map(function (g) { var items = g.items.map(function (it) { return '' + esc(it.t) + ''; }).join(''); return '