您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
电子科技大学教务助手帮你更便捷地使用教务系统
当前为
// ==UserScript== // @name 电子科技大学教务助手 // @namespace http://shawroger.gitee.io/ // @version 0.0.5 // @description 电子科技大学教务助手帮你更便捷地使用教务系统 // @author shawroger // @match *://eams.uestc.edu.cn/eams/* // @require https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js // @require https://cdn.bootcdn.net/ajax/libs/highcharts/9.1.2/highcharts.min.js // @icon https://www.uestc.edu.cn/favicon.ico // ==/UserScript== /** * 获取用户数据 * 只用于数据处理 * 不会上传数据,可以审查代码证明 */ const user = { id: "", // 学号 name: "", // 姓名 }; /** * 配置全局定时器时间间隔 */ const interval = { renderScore: 600, crashListen: 1000, globalListener: 200, }; /** * 配置 window.localStorage 参数 */ const storage = [ /** * 配置 是否显示顶部栏目 */ { key: "UESTC_STUDYSYS_HELPER_SHOWBAR_ALL", val: 1, }, /** * 配置 是否显示成绩图表 */ { key: "UESTC_STUDYSYS_HELPER_SHOW_SCORE_CHART", val: 1, }, ]; /** * 配置 是否继续循环 */ const loop = { /** * 循环监听渲染 计划页面 */ renderPlan: true, /** * 循环监听渲染 成绩页面 */ renderScore: true, }; /** * 配置首页导航 */ const navs = [ { text: "查询平时成绩", img: "https://pic.imgdb.cn/item/60ead7fd5132923bf8f5b136.png", href: "http://eams.uestc.edu.cn/eams/teach/grade/usual/usual-grade-std.action", }, { text: "查询我的成绩", img: "https://pic.imgdb.cn/item/60ead77f5132923bf8f3f56b.png", href: "http://eams.uestc.edu.cn/eams/teach/grade/course/person.action", }, { text: "查询我的计划", img: "https://pic.imgdb.cn/item/60eadacf5132923bf8ffcca2.png", href: "http://eams.uestc.edu.cn/eams/myPlanCompl.action", }, ]; /** * 配置成绩表格背景颜色 * 以分数段染色 */ const scoreColors = [ { range: [85, 101], background: "LightSeaGreen", color: "", level: 1, }, { range: [70, 85], background: "LawnGreen", color: "", level: 2, }, { range: [60, 70], background: "NavajoWhite", color: "", level: 3, }, { range: [-1, 60], background: "IndianRed", color: "white", level: 4, }, ]; /** * 配置成绩表格背景颜色 * 以关键词染色 */ const keywColors = [ { words: ["P", "A", "通过"], background: "LightSeaGreen", color: "", index: -3, level: 1, }, { words: ["否"], background: "IndianRed", color: "white", index: -2, level: 4, }, ]; /** * 成绩记录类 * 由于生成图表 */ class Recorder { v1 = 0; // 第一等级 v2 = 0; // 第二等级 v3 = 0; // 第三等级 bad = 0; // 未合格 /** * 对成绩等级进行计数 * @param {number} 配置的 level */ add(level) { if (level === 1) { this.v1++; } else if (level === 2) { this.v2++; } else if (level === 3) { this.v3++; } else if (level === 4) { this.bad++; } if (this.onChange) { this.onChange(); } } /** * 生成 highchart 图表配置数据 * @returns */ chart() { const config = { title: { text: `${user.name.length > 0 ? user.name + "的" : ""}成绩分布图`, }, chart: { plotBackgroundColor: null, plotBorderWidth: null, plotShadow: false, backgroundColor: "#f2f2f2", }, tooltip: { pointFormat: "共 {point.y} 门课程 {point.percentage:.1f}%", }, }; const plotOptions = { pie: { allowPointSelect: true, cursor: "pointer", dataLabels: { enabled: true, format: "{point.name} {point.percentage:.1f} %", style: { color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || "black", }, }, }, }; const series = [ { type: "pie", data: [ ["优秀", this.v1], ["较好", this.v2], ["一般", this.v3], ["未完成", this.bad], ], }, ]; config.series = series; config.plotOptions = plotOptions; return config; } /** * 读取成绩所在行的信息 * @param {any} tr 成绩所在行 * @param {string} text 成绩数据文字 */ info(_tr, _text) { // todo!() } } /** * 创建一个新的 Recorder 类 * @returns */ function createRecorder() { return new Recorder(); } /** * 初始化全局数据 */ function initState() { const userdom = $("a[title=查看登录记录]").text(); const index = userdom.indexOf("("); user.name = userdom.slice(0, index); user.id = userdom.slice(index + 1, userdom.length - 1); } /** * * @returns 返回localStorage配置列表 */ function initStorage() { function safeParseInt(val) { if (typeof val === "number") { // Maybe NaN itself return val; } else if (typeof val === "string") { return Number(val); } else { return NaN; } } return storage.map(({ key, val }) => { const v = safeParseInt(window.localStorage.getItem(key)); if (isNaN(v)) { window.localStorage.setItem(key, String(val)); return val; } else { return v; } }); } /** * * @param {string | string[]} url 地址栏关键词 * @returns 当前地址是否包含指定字符串 */ function includeUrl(url) { if (Array.isArray(url)) { for (const u of url) { if (window.location.href.includes(u)) { return true; } } } else { return window.location.href.includes(url); } return false; } /** * 直接跳转首页 */ function toHome() { window.location.href = `http://eams.uestc.edu.cn/eams`; } /** * 注入全局 CSS 样式 */ function injectCSS() { const head = $("head"); const css = ` <style type="text/css"> .uestc-helper-box { padding: 5px 10px; background: whiteSmoke; display: flex; justify-content: space-between; border-bottom: solid grey thin; } .btn-group button { margin-right: 15px; } #helper-text { font-size: 16px; padding-left: 10px; } .list_box_1 ul .li_1 { margin-bottom: 20px; } .btn-group { display: flex; justify-content: center; align-items: center; padding: 5px; } #ush_chart_box { width: 100%; height: auto; background: #f2f2f2; } </style>`; head.append(css); } /** * 绑定全局按钮事件 */ function injectEvent() { $("#reset_btn").click(() => { toHome(); }); $("#close_btn").click(() => { $(".uestc-helper-box").hide(); window.localStorage.setItem(storage[0].key, "0"); toHome(); }); $("#chart_btn").click(() => { const val = initStorage()[1]; const setVal = val > 0 ? "0" : "1"; window.localStorage.setItem(storage[1].key, setVal); toHome(); }); $("#show_btn").click(() => { $(".uestc-helper-box").show(); window.localStorage.setItem("UESTC_STUDYSYS_HELPER_SHOWBAR_ALL", "1"); toHome(); }); $("#position_bar span.secondMenu a").click(() => { $("#ush_chart_box").hide(); loop.renderScore = true; }); $(".toolbar-item-ge0 toolbar-item").click(() => { renderScore(); loop.renderScore = true; }); $("input[value='切换学期']").click(() => { renderScore(); loop.renderScore = true; }); } /** * 注入 HTML 代码 */ function injectHTML() { if (initStorage()[0] > 0) { const div = $("body"); const html = ` <div class="uestc-helper-box"> <p id="helper-text">欢迎使用<a target="_blank" href="https://greasyfork.org/zh-CN/scripts/429207-%E7%94%B5%E5%AD%90%E7%A7%91%E6%8A%80%E5%A4%A7%E5%AD%A6%E6%95%99%E5%8A%A1%E5%8A%A9%E6%89%8B"> 电子科技大学教务助手 </a></p> <div class="btn-group"> <button id="chart_btn">图表功能:${initStorage()[1] > 0 ? "开" : "关"}</button> <button id="reset_btn">重置教务系统</button> <button id="close_btn">隐藏面板</button> </div> </div>`; div.prepend(html); } else { const div = $("form[action='/eams/home.action']"); const html = `<input type="button" id="show_btn" value="显示助手">`; div.append(html); } } /** * 监听教务系统 * 出现异常直接跳转首页 */ function crashListen() { if ( includeUrl([ "stdElectCourse", "security/my.action", "teach/grade/usual/usual-grade-std!usualInfo.action", ]) ) { return; } if ($("#position_bar").length === 0) { $("#helper-text").css("color", "red"); $("#helper-text").text("教务系统页面显示异常,即将自动重置"); // 等待跳回 setTimeout(toHome, interval.crashListen); } } /** * 调整首页布局 */ function resetLayout() { $("img[src = '/eams/avatar/my.action']").hide(); if (includeUrl("home!submenus.action?")) { const node = $("div.list_box_1 li.li_1").last().clone(true); navs.forEach(({ text, img, href }) => { node.find("h3").text(text); node.find("a").attr("href", href); node.find("div").css("background", `url("${img}") no-repeat 50% 50%`); $("div.list_box_1 li.li_1") .last() .parent() .append(`<li class="li_1">` + node.html() + `</li>`); }); } } /** * 加入 footer 内容 */ function injectFooter() { const footer = $("#BottomBg"); footer.html( footer.text() + `<br/><br/> 页面启用了 <span style="color: orange"> <a target="_blank" style="color: orange" href="https://greasyfork.org/zh-CN/scripts/429207-%E7%94%B5%E5%AD%90%E7%A7%91%E6%8A%80%E5%A4%A7%E5%AD%A6%E6%95%99%E5%8A%A1%E5%8A%A9%E6%89%8B"> 电子科技大学教务助手 </a></span> made by shawroger` ); } /** * 通过分数区间来染色 * @param {Recorder} recorder * @param {any} tr * @param {string} text * @returns void */ function paintScore(recorder, tr, text) { const score = parseInt(text); for (const { color, range, background, level } of scoreColors) { if (range[0] <= score && score < range[1]) { recorder.add(level); recorder.info(tr, text); tr.css("background", background); if (color.length > 1) { tr.find("td").each((_, e) => { $(e).css("color", color); if ($(e).children("a").length > 0) { $(e).children("a").css("color", color); } }); } return true; } } return false; } /** * 通过关键词来染色 * @param {Recorder} recorder * @param {any} tr * @param {string} text * @returns void */ function paintKeywd(recorder, tr, text) { function t_(words, text) { for (const word of words) { if (text === word) { return true; } } return false; } for (const { color, words, background, index, level } of keywColors) { if (t_(words, text || tr.find("td").eq(index).text().trim())) { recorder.add(level); recorder.info(tr, text); tr.css("background", background); if (color.length > 1) { tr.find("td").each((_, e) => { $(e).css("color", color); if ($(e).children("font").length > 0) { $(e).children("font").attr("color", color); } if ($(e).children("a").length > 0) { $(e).children("a").css("color", color); } }); } return true; } } return false; } /** * 渲染 计划完成情况 页面 * @returns */ function renderPlan() { if (!includeUrl("myPlanCompl.action")) { return; } $("#ush_chart_box").show(); if ($("#ush_chart_box").length === 0) { $("#main").before(`<div id="ush_chart_box"></div>`); } loop.renderPlan = false; const table = $("table.formTable"); const recordBook = createRecorder(); table.find("tr").each((_, e) => { const tr = $(e); const text = tr.find("td").eq(-3).text().trim(); if (!paintScore(recordBook, tr, text)) { paintKeywd(recordBook, tr); } }); if (initStorage()[1] > 0) { $("#ush_chart_box").highcharts(recordBook.chart()); removeHightchartCredit(); } } /** * 渲染 我的成绩 页面 * @returns */ function renderScore() { if (!includeUrl(["teach/grade/course/person", "teach/grade/usual"])) { return; } $("#ush_chart_box").show(); if ($("#ush_chart_box").length === 0) { $("#main").before(`<div id="ush_chart_box"></div>`); } $("div[title='所有学期成绩']").click(() => { loop.renderScore = true; console.log("loopVars.renderScore = true"); }); const recordBook = createRecorder(); const table = $("table.gridtable").last(); table.find("tr").each((_, e) => { const tr = $(e); const text = includeUrl("historyCourseGrade") ? tr.find("td").last().text().trim() : tr.find("td").eq(-2).text().trim(); if (!paintScore(recordBook, tr, text)) { paintKeywd(recordBook, tr, text); } }); // 停止监听 if (initStorage()[1] > 0) { setTimeout(() => { loop.renderScore = false; if ($("#ush_chart_box").length > 0) { $("#ush_chart_box").highcharts(recordBook.chart()); removeHightchartCredit(); } }, interval.renderScore); } } /** * 主任务 */ function mainAction() { // 初始化数据 initState(); // 初始 DOM 部分 injectCSS(); injectHTML(); initStorage(); resetLayout(); injectFooter(); // 初始 事件部分 crashListen(); renderPlan(); renderScore(); /** * 配置 Highcharts 的主颜色 */ Highcharts.setOptions({ colors: scoreColors.map(({ background }) => background), }); } /** * 删除 highchart 的 credit */ function removeHightchartCredit() { $(".highcharts-credits").hide(); } /** * 循环监听任务 */ function loopAction() { injectEvent(); crashListen(); if (loop.renderPlan) { renderPlan(); } if (loop.renderScore) { renderScore(); } } (function () { "use strict"; mainAction(); setInterval(() => { loopAction(); }, interval.globalListener); })();