Greasy Fork

东北大学绩点小助手

计算加权绩点并显示在页面上的油猴脚本

当前为 2023-08-15 提交的版本,查看 最新版本

// ==UserScript==
// @name         东北大学绩点小助手
// @license MIT
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  计算加权绩点并显示在页面上的油猴脚本
// @author       FFSNOW
// @match        http://219.216.96.4/*
// @match        https://webvpn.neu.edu.cn/*
// @match        https://portal.neu.edu.cn/*
// @match        https://eone.neu.edu.cn/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_listValues
// @grant        GM_deleteValue
// ==/UserScript==

(function () {
    'use strict';
    /*悬浮窗功能*/
    // 创建按钮
    let button = document.createElement('button');
    button.innerHTML = '绩点助手';
    // 设置按钮样式
    button.style.position = 'fixed';
    button.style.bottom = '20px';
    button.style.right = '20px';
    button.style.zIndex = '9999';
    button.style.backgroundColor = 'rgba(16, 55, 112, 0.7)';
    button.style.color = 'white';
    button.style.border = 'none';
    button.style.borderRadius = '50%';
    button.style.width = '55px'; // 按钮宽度与高度相等
    button.style.height = '55px'; // 按钮宽度与高度相等
    button.style.padding = '12px';
    button.style.fontSize = '10px';
    button.style.cursor = 'move';
    button.style.boxShadow = '0 0 5px rgba(0,0,0,0.3)';
    // 按钮点击事件处理函数
    function buttonClickHandler() {
        let domain = window.location.hostname;
        if (domain === 'webvpn.neu.edu.cn') {
            window.open('https://webvpn.neu.edu.cn/http/62304135386136393339346365373340e2b0fd71d8941093ab4e2527/eams/teach/grade/course/person!historyCourseGrade.action?projectType=MAJOR');
        } else {
            window.open('http://219.216.96.4/eams/teach/grade/course/person!historyCourseGrade.action?projectType=MAJOR');
        }
    }
    // 右键菜单事件处理函数
    function buttonContextMenuHandler(event) {
        event.preventDefault();
        button.remove();
        floatingWindow.style.display = 'none';
    }
    // 创建悬浮窗
    let floatingWindow = document.createElement('div');
    floatingWindow.innerHTML = '点击进入,右键关闭,本插件作用为绩点计算,你可以输入新的一门课的学分和绩点预测绩点变化,你也可以进入绩点模拟功能随意修改绩点,进一步分析';
    floatingWindow.style.position = 'fixed';
    floatingWindow.style.backgroundColor = 'white';
    floatingWindow.style.color = 'black';
    floatingWindow.style.padding = '10px';
    floatingWindow.style.border = '1px solid black';
    floatingWindow.style.borderRadius = '5px';
    floatingWindow.style.bottom = '20px';
    floatingWindow.style.right = '80px';
    floatingWindow.style.display = 'none';
    // 将悬浮窗添加到页面上
    document.body.appendChild(floatingWindow);
    // 在按钮上绑定鼠标移入事件,显示悬浮窗口
    button.addEventListener('mouseover', function () {
        floatingWindow.style.display = 'block';
    });
    // 在按钮上绑定鼠标移出事件,隐藏悬浮窗口
    button.addEventListener('mouseout', function () {
        floatingWindow.style.display = 'none';
    });
    var flag = true;
    //绩点小助手触发器
    if (window.location.href.indexOf("person!historyCourseGrade.action") !== -1) {
        // 当前域名中包含"person!historyCourseGrade.action",运行绩点小助手
        GPAHELPER();
        flag = false;
    }
    //学业预警助手触发器
    if (window.location.href.indexOf("myPlanCompl.action") !== -1) {
        // 当前域名中包含"myPlanCompl.action",运行学业预警小助手
        GPAPlANHELPER();
    }
    if (flag) {
        // 将按钮添加到页面上
        document.body.appendChild(button);
    }


    // 绑定按钮点击事件
    button.addEventListener('click', buttonClickHandler);
    // 绑定右键菜单事件
    button.addEventListener('contextmenu', buttonContextMenuHandler);
    function GPAHELPER() {
        /*学期学年筛选功能*/
        //初始化筛选器
        function addFilter() {
            // 获取表格
            var table = document.querySelector('.gridtable');
            if (!table) {
                return;
            }

            // 创建筛选器输入框
            var input = document.createElement('input');
            input.type = 'text';
            input.placeholder = '筛选学年学期';
            input.style.backgroundColor = 'white'; // 背景颜色
            input.style.border = '1px solid lightgray'; // 边框样式
            input.style.padding = '10px'; // 内边距
            input.addEventListener('input', function () {
                var filterValue = input.value.toLowerCase();
                var rows = table.querySelectorAll('tbody tr');

                // 遍历表格中的每一行,根据输入框的值进行筛选
                rows.forEach(function (row) {
                    var cell = row.querySelector('td:first-child');
                    var value = cell.textContent.toLowerCase();

                    // 根据筛选值隐藏或显示行
                    if (value.indexOf(filterValue) === -1) {
                        row.style.display = 'none';
                    } else {
                        row.style.display = '';
                    }
                });
            });
            return input;
        }
        // 插入筛选器输入框
        var tableHead = document.querySelector('.gridhead');
        var newRow = tableHead.insertRow(0);
        newRow.insertCell().appendChild(addFilter());
        //新科目绩点影响
        var creditInput = document.createElement('input');
        creditInput.type = 'text';
        creditInput.placeholder = '新课学分';
        creditInput.style.backgroundColor = 'white'; // 背景颜色
        creditInput.style.border = '1px solid lightgray'; // 边框样式
        creditInput.style.padding = '10px'; // 内边距
        newRow.insertCell().appendChild(creditInput);
        //学分输入框
        var gradeInput = document.createElement('input');
        gradeInput.type = 'text';
        gradeInput.placeholder = '预估绩点';
        gradeInput.style.backgroundColor = 'white';
        gradeInput.style.border = '1px solid lightgray';
        gradeInput.style.padding = '10px';
        newRow.insertCell().appendChild(gradeInput);
        //预测绩点输入框
        var calculateButton = document.createElement('button');
        calculateButton.textContent = '筛选/预测计算';
        calculateButton.style.backgroundColor = 'white'; // 背景颜色
        calculateButton.style.color = 'black'; // 文字颜色
        calculateButton.style.width = '100%'; // 宽度设置为全宽
        calculateButton.style.padding = '10px'; // 内边距
        calculateButton.style.border = 'none'; // 去除边框
        calculateButton.style.cursor = 'pointer'; // 鼠标指针样式
        // 添加悬停样式
        calculateButton.addEventListener('mouseenter', function () {
            calculateButton.style.backgroundColor = 'lightgray';
        });

        calculateButton.addEventListener('mouseleave', function () {
            calculateButton.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(calculateButton);

        var resultCell = newRow.insertCell();
        resultCell.colSpan = 7;

        //绩点计划按钮
        var planbutten = document.createElement('button');
        planbutten.textContent = '学业预警';
        planbutten.style.backgroundColor = 'white'; // 背景颜色
        planbutten.style.color = 'black'; // 文字颜色
        planbutten.style.width = '100%'; // 宽度设置为全宽
        planbutten.style.padding = '10px'; // 内边距
        planbutten.style.border = 'none'; // 去除边框
        planbutten.style.cursor = 'pointer'; // 鼠标指针样式
        // 添加悬停样式
        planbutten.addEventListener('mouseenter', function () {
            planbutten.style.backgroundColor = 'lightgray';
        });

        planbutten.addEventListener('mouseleave', function () {
            planbutten.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(planbutten);

        //绩点模拟按钮
        var editButton = document.createElement('button');
        editButton.textContent = '绩点模拟';
        editButton.style.backgroundColor = 'white'; // 背景颜色
        editButton.style.color = 'black'; // 文字颜色
        editButton.style.width = '100%'; // 宽度设置为全宽
        editButton.style.padding = '10px'; // 内边距
        editButton.style.border = 'none'; // 去除边框
        editButton.style.cursor = 'pointer'; // 鼠标指针样式
        // 添加悬停样式
        editButton.addEventListener('mouseenter', function () {
            editButton.style.backgroundColor = 'lightgray';
        });

        editButton.addEventListener('mouseleave', function () {
            editButton.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(editButton);
        
        //绩点模拟计算按钮
        var huanxiangbutten = document.createElement('button');
        huanxiangbutten.textContent = '绩点模拟计算';
        huanxiangbutten.style.backgroundColor = 'white'; // 背景颜色
        huanxiangbutten.style.color = 'black'; // 文字颜色
        huanxiangbutten.style.width = '100%'; // 宽度设置为全宽
        huanxiangbutten.style.padding = '10px'; // 内边距
        huanxiangbutten.style.border = 'none'; // 去除边框
        huanxiangbutten.style.cursor = 'pointer'; // 鼠标指针样式
        // 添加悬停样式
        huanxiangbutten.addEventListener('mouseenter', function () {
            huanxiangbutten.style.backgroundColor = 'lightgray';
        });

        huanxiangbutten.addEventListener('mouseleave', function () {
            huanxiangbutten.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(huanxiangbutten);

        // 显示绩点
        var oldGPA = GM_getValue('GPA');
        //alert(oldGPA);
        var trueGPA = calculateWeightedGPA().toFixed(4);
        // 判断绩点是否发生变化
        if (oldGPA !== trueGPA) {
            alert('你的绩点从' + oldGPA + '变为了' + trueGPA);
        }
        GM_setValue('GPA', trueGPA);

        // 计算加权绩点的函数
        function calculateWeightedGPA(credit_index = 6, grade_index = 12, category_index = 4) {
            var table = document.querySelector('.gridtable');
            var tableBody = table.querySelector('tbody');
            var rows = tableBody.getElementsByTagName('tr');
            var totalCredit = 0;
            var totalGrade = 0;
            var totalCreditwithoutchose = 0;
            var totalGradewithoutchose = 0;

            // 获取表头单元格
            var tableHead = document.querySelector('.gridhead tr').nextElementSibling;
            var cells = tableHead.querySelectorAll('th');

            // 遍历表头中的每个单元格,查找"绩点"和"学分"所在的列数
            cells.forEach(function (cell, index) {
                var cellText = cell.textContent.trim();

                if (cellText === "绩点") {
                    grade_index = index;
                }

                if (cellText === "学分") {
                    credit_index = index;
                }

                if (cellText === "课程类别") {
                    category_index = index;
                }
            });

            for (var i = 0; i < rows.length; i++) {
                var row = rows[i];
                var credit = parseFloat(row.cells[credit_index].textContent);
                var grade = parseFloat(row.cells[grade_index].textContent);
                var category = row.cells[category_index].textContent.trim();

                if (isNaN(credit) || isNaN(grade)) {
                    continue; // 跳过数据不是数字的行
                }

                if (row.style.display === 'none') {
                    continue; // 跳过被隐藏的行
                }

                if (category != "通识选修类") {
                    totalCreditwithoutchose += credit;
                    totalGradewithoutchose += credit * grade;
                }
                totalCredit += credit;
                totalGrade += credit * grade;
            }

            var inputCredit = parseFloat(creditInput.value);
            var inputGrade = parseFloat(gradeInput.value);
            var newTotalCredit = totalCredit + inputCredit;
            var newTotalGrade = totalGrade + inputCredit * inputGrade;
            var newGPA = newTotalGrade / newTotalCredit;
            var currentGPA = totalGrade / totalCredit;
            var currentGPAwithoutchose = totalGradewithoutchose / totalCreditwithoutchose;
            var gradeChange = newGPA - currentGPA;
            // 显示加权绩点
            resultCell.innerHTML = '当前加权绩点:' + currentGPA.toFixed(4) +
                ', 加上这门课绩点:' + newGPA.toFixed(4) +
                ', 绩点变化:' + gradeChange.toFixed(4);
            if (isNaN(newGPA) || isNaN(gradeChange)) {
                resultCell.innerHTML = '当前加权绩点:' + currentGPA.toFixed(4) +
                ', 当前总学分:' + totalCredit.toFixed(1) +
                ',去除通识选修类加权绩点:' + currentGPAwithoutchose.toFixed(4) + ',去除选修学分:' + totalCreditwithoutchose.toFixed(1)
            }
            return currentGPA;
        }

        // 监听计算按钮的点击事件
        calculateButton.addEventListener('click', function () {
            calculateWeightedGPA();
        });

        planbutten.addEventListener('click', function () {
            let domain = window.location.hostname;
        if (domain === 'webvpn.neu.edu.cn') {
            window.open('https://webvpn.neu.edu.cn/http/62304135386136393339346365373340e2b0fd71d8941093ab4e2527/eams/myPlanCompl.action');
        } else {
            window.open('http://219.216.96.4/eams/myPlanCompl.action');
        }
        });

        editButton.addEventListener('click', function () {
            if (document.designMode == 'off') {
                document.designMode = 'on'
                editButton.textContent = '结束模拟'
                // 显示绩点
                resultCell.innerHTML = '模拟模式可以随意改变网页内容,当前加权绩点:' + trueGPA;
                var table = document.querySelector('.gridtable');
                var tableBody = table.querySelector('tbody');

                // 获取表头单元格
                var tableHead = document.querySelector('.gridhead tr').nextElementSibling;
                var cells = tableHead.querySelectorAll('th');

                // 查找"考试情况"所在的列数
                var last_index = -1;
                cells.forEach(function (cell, index) {
                    var cellText = cell.textContent.trim();
                    if (cellText === "考试情况") {
                        last_index = index;
                    }
                });

                // 插入行
                for (var i = 0; i < 20; i++) {
                    var row = tableBody.insertRow();
                    row.classList.add('griddata-odd');
                    for (var j = 0; j < last_index + 1; j++) {
                        var celll = row.insertCell();
                        if (j === 3) {
                            celll.textContent = '模拟课程' + (i + 1);
                        } else {
                            celll.textContent = ' ';
                        }
                    }
                }
            }
            else {
                document.designMode = 'off'
                editButton.textContent = '模拟计算'
                location.reload();
            };
        });

        // 监听按钮的点击事件
        huanxiangbutten.addEventListener('click', function () {
            // 显示绩点
            var dreamGPA = calculateWeightedGPA();
            resultCell.innerHTML = '真实加权绩点:' + trueGPA + ',模拟绩点:' + dreamGPA.toFixed(4) + ',变化:' + (dreamGPA - trueGPA).toFixed(4);
        });


    }
    function GPAPlANHELPER() {
        var table = document.querySelector('.formTable');
        if (!table) {
            return;
        }
        //绩点显示列

        var rows = table.querySelectorAll('tr');
        // 遍历每一行
        for (var i = 1; i < rows.length; i++) {
            // 忽略class为darkColumn的行
            if (!rows[i].classList.contains('darkColumn')) {
                // 获取成绩元素
                var gradeCell = rows[i].querySelectorAll('td')[7];
                // 获取成绩值并转化
                var grade = gradeCell.innerText;

                var tip = rows[i].querySelectorAll('td')[9].innerText;
                var name = rows[i].querySelectorAll('td')[2].innerText;
                // 定义绩点转化函数
                function getGPA(score, tip) {
                    if (score === '优') {
                        return 4.5;
                    } else if (score === '良') {
                        return 3.5;
                    } else if (score === '中') {
                        return 2.5;
                    } else if (score === '及格') {
                        return 1.5;
                    } else if (score === '不及格') {
                        return 0;
                    } else if (score === '合格') {
                        return 3.0;
                    } else if (score === '不合格') {
                        return 0;
                    } else if (score === '--') {
                        console.log(tip.toString());
                        if (tip.toString().includes("替代课程")) {
                            return -1;
                        }
                        if (tip.toString().includes("在读")) {
                            return "在读";
                        }
                        return '';
                    } else {
                        return (score - 50) / 10;
                    }
                }
                // 创建绩点元素
                var gpaCell = document.createElement('td');
                // 获取成绩对应的绩点
                var gpa = getGPA(grade, tip);
                if (gpa == -1) {
                    if (GM_getValue(name)) {
                        gpa = GM_getValue(name);
                    } else {
                        var gpaInput = window.prompt("查询到替代课程,请输入[" + name + "]的绩点,输入后会保存,您可以通过清除记录按钮来重置输入:");
                        gpa = parseFloat(gpaInput);
                        GM_setValue(name, gpa)
                    }
                }
                // 设置绩点元素的内容
                gpaCell.innerText = gpa;
                gpaCell.style.textAlign = 'center';
                gpaCell.style.width = '100px';
                gpaCell.style.display = 'block';
                gpaCell.style.border = 'solid';
                gpaCell.style.borderWidth = '0px';
                gpaCell.style.borderRightWidth = '0px';
                gpaCell.style.borderBottomWidth = '0px';
                gpaCell.style.bordertopWidth = '1px';
                if (i % 2 == 0) {
                    gpaCell.style.backgroundColor = '#f7f7f7';
                }
                // 将绩点元素插入到行中(注意调整列数)
                rows[i].insertBefore(gpaCell, rows[i].querySelectorAll('td')[10]);
            }
        }

        var tableHead = document.querySelector('.formTable');
        var newRow = tableHead.insertRow(0);
        //清除替代课程记录按钮
        var delButton = document.createElement('button');
        delButton.textContent = '清除记录';
        delButton.style.backgroundColor = 'white'; // 背景颜色
        delButton.style.color = 'black'; // 文字颜色
        delButton.style.width = '100%'; // 宽度设置为全宽
        delButton.style.padding = '10px'; // 内边距
        delButton.style.border = 'none'; // 去除边框
        delButton.style.cursor = 'pointer'; // 鼠标指针样式
        // 添加悬停样式
        delButton.addEventListener('mouseenter', function () {
            delButton.style.backgroundColor = 'lightgray';
        });
        delButton.addEventListener('mouseleave', function () {
            delButton.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(delButton);

        //显示框

        var resultCell = newRow.insertCell();
        resultCell.colSpan = 8;
        resultCell.style.textAlign = 'center';

        //绩点模拟按钮
        var editButton = document.createElement('button');
        editButton.textContent = '编辑,可以修改最后一列绩点';
        editButton.style.backgroundColor = 'white'; // 背景颜色
        editButton.style.color = 'black'; // 文字颜色
        editButton.style.width = '100%'; // 宽度设置为全宽
        editButton.style.padding = '10px'; // 内边距
        editButton.style.border = 'none'; // 去除边框
        editButton.style.cursor = 'pointer'; // 鼠标指针样式

        // 添加悬停样式
        editButton.addEventListener('mouseenter', function () {
            editButton.style.backgroundColor = 'lightgray';
        });

        editButton.addEventListener('mouseleave', function () {
            editButton.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(editButton);
        //绩点模拟计算按钮
        var huanxiangbutten = document.createElement('button');
        huanxiangbutten.textContent = '模拟计算';
        huanxiangbutten.style.backgroundColor = 'white'; // 背景颜色
        huanxiangbutten.style.color = 'black'; // 文字颜色
        huanxiangbutten.style.width = '100%'; // 宽度设置为全宽
        huanxiangbutten.style.padding = '10px'; // 内边距
        huanxiangbutten.style.border = 'none'; // 去除边框
        huanxiangbutten.style.cursor = 'pointer'; // 鼠标指针样式
        // 添加悬停样式
        huanxiangbutten.addEventListener('mouseenter', function () {
            huanxiangbutten.style.backgroundColor = 'lightgray';
        });

        huanxiangbutten.addEventListener('mouseleave', function () {
            huanxiangbutten.style.backgroundColor = 'white';
        });
        newRow.insertCell().appendChild(huanxiangbutten);

        // 计算加权绩点的函数
        calculateWeightedGPA();
        function calculateWeightedGPA(credit_index = 4, grade_index = 10, category_index = 3) {
            var table = document.querySelector('.formTable');
            var rows = table.getElementsByTagName('tr');
            var totalCredit = 0;
            var totalGrade = 0;
            var totalCreditwithoutchose = 0;
            var totalGradewithoutchose = 0;
            for (var i = 2; i < rows.length; i++) {
                if (rows[i].classList.contains('darkColumn')) {
                    console.log(rows[i].classList); // 打印类名列表
                    continue; // 跳过不是成绩的行
                }
                var row = rows[i];
                var credit = parseFloat(row.cells[credit_index].textContent);
                var grade = parseFloat(row.cells[grade_index].textContent);
                var category = row.cells[category_index].textContent.trim();


                if (isNaN(credit) || isNaN(grade)) {
                    row.cells[0].textContent = "×";
                    continue; // 跳过数据不是数字的行
                }

                if (row.style.display === 'none') {
                    continue; // 跳过被隐藏的行
                }
                row.cells[0].textContent = "√";
                //console.log(row.cells[2].textContent + '\t' + row.cells[credit_index].textContent + '\t' + row.cells[grade_index].textContent);
                if (category != "否") {
                    row.cells[0].textContent = "√√";
                    totalCreditwithoutchose += credit;
                    totalGradewithoutchose += credit * grade;
                }
                totalCredit += credit;
                totalGrade += credit * grade;
            }
            var currentGPA = totalGrade / totalCredit;
            var currentGPAwithoutchose = totalGradewithoutchose / totalCreditwithoutchose;
            // 显示加权绩点

            resultCell.innerHTML = '当前加权绩点:' + currentGPA.toFixed(4) +
                ', 当前总学分:' + totalCredit.toFixed(1) +
                ',必修课加权绩点:' + currentGPAwithoutchose.toFixed(4) + ',必修课学分:' + totalCreditwithoutchose.toFixed(1) +',必修课成绩:' + (currentGPAwithoutchose.toFixed(4)*10+50) 

            return currentGPA;
        }
        editButton.addEventListener('click', function () {
            if (document.designMode == 'off') {
                document.designMode = 'on'
                editButton.textContent = '结束'
                // 显示绩点
                resultCell.innerHTML = '模拟模式可以随意改变网页内容,当前加权绩点:' + GM_getValue('GPA');
            }
            else {
                document.designMode = 'off'
                editButton.textContent = '编辑'
                location.reload();
            };
        });

        // 监听按钮的点击事件
        huanxiangbutten.addEventListener('click', function () {
            // 显示绩点
            var dreamGPA = calculateWeightedGPA();
            resultCell.innerHTML = '模拟绩点:' + dreamGPA.toFixed(4)
        });
        // 监听按钮的点击事件
        delButton.addEventListener('click', function () {
            var t = GM_getValue("GPA");
            var allValues = GM_listValues();
            allValues.forEach(function (key) {
                GM_deleteValue(key);
            });
            GM_setValue("GPA", t);
            alert("清除成功" + t)
        });
    }

})();