// ==UserScript==
// @name 智学网排名显示
// @namespace http://www.gzsofteasy.com/
// @version 2.4
// @description 智学网排名优化显示
// @author Frank Chen
// @license MPL
// @homepage https://scriptcat.org/zh-CN/script-show-page/2821
// @source https://scriptcat.org/zh-CN/script-show-page/2821/code
// @supportURL https://github.com/cshuibo/ZhiXueWangRank/issues/new/choose
// @match https://*.zhixue.com/activitystudy/web-report/index.html?from=web-container_top*
// @match https://*.zhixue.com/activitystudy/web-report/index.html?examId=*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
const GLOBALDATA = {
classStatTotalNum:0,
gradeStatTotalNum:0,
unionStatTotalNum:0,
setClassStatTotalNum: (n) => {
GLOBALDATA.classStatTotalNum = n;
},
setGradeStatTotalNum: (n) => {
GLOBALDATA.gradeStatTotalNum = n;
},
setUnionStatTotalNum: (n) => {
GLOBALDATA.unionStatTotalNum = n;
},
data: {},
newCode: id => {
if (!!GLOBALDATA.data[id]) {
return;
}
GLOBALDATA.data[id] = {
number: 0,
subject: {},
};
},
addNumber: (id, n) => {
GLOBALDATA.newCode(id);
GLOBALDATA.data[id].number = n;
},
addRank: (id, n, o) => {
GLOBALDATA.newCode(id);
GLOBALDATA.data[id].subject[n] = {
rank: -1,
org: o,
};
},
calc: id => {
const e = GLOBALDATA.data[id];
const keys = Object.keys(e.subject);
if (e.number === 0 || keys.length === 0) {
return;
}
for (const ki in keys) {
const k = keys[ki];
if (e.subject[k].rank !== -1) {
return;
}
const percentage = e.subject[k].org;
const total = e.number;
const rank = Math.ceil(percentage / 100 * total);
GLOBALDATA.data[id].subject[k].rank = Math.min(Math.max(rank, 1), total);
}
display(id);
},
};
function display(id) {
let html = `
班级排行榜
考试人数 ${GLOBALDATA.data[id].number}
`;
const keys = Object.keys(GLOBALDATA.data[id].subject);
for (const ki in keys) {
const k = keys[ki];
html += `
${k}
第
${GLOBALDATA.data[id].subject[k].rank}
名
`;
}
html += `
`;
let rankDOM = document.createElement('div');
rankDOM.classList.add('hierarchy');
rankDOM.id = 'qianjuzhixuerank';
rankDOM.innerHTML = html;
document.querySelector('#report > div > div.report > div > div').insertBefore(rankDOM, document.querySelector('#report > div > div.report > div > div > div:nth-child(2)'));
}
function patchRequest(url, xhr) {
//alert(url);
if (url.indexOf('zhixuebao/report/exam/getSubjectDiagnosis') !== -1) {
xhr.addEventListener("load", patchGetSubjectDiagnosisOnReadyStateChange);
}
if (url.indexOf('zhixuebao/report/paper/getLevelTrend') !== -1) {
xhr.addEventListener("load", patchGetLevelTrendOnReadyStateChange);
}else if (url.indexOf('zhixuebao/report/exam/getLevelTrend') !== -1) {
xhr.addEventListener("load", patchGetLevelTrendOnReadyStateChange);
}
let element = document.querySelector("div.switch_tab_container>div.switch_tab");
if(element){
element.addEventListener('click', function(event) {
//console.log('元素被点击了!');
setTimeout(function() {
changeRankDisplay();
}, 100);
});
}
}
/**
* 解析 URL 参数(同时支持查询参数和哈希参数)
* @param {string} [url] 可选,默认为当前页面的URL
* @returns {object} 参数对象,同名参数自动转为数组
*/
function parseURLParams(url) {
const params = {};
const rawUrl = url || window.location.href;
// 同时处理 ? 和 # 分隔符
const queryIndex = rawUrl.indexOf('?');
const hashIndex = rawUrl.indexOf('#');
// 提取查询参数部分
let queryStr = '';
if (queryIndex > -1) {
const endIndex = hashIndex > -1 ? hashIndex : rawUrl.length;
queryStr = rawUrl.slice(queryIndex + 1, endIndex);
}
// 提取哈希参数部分
let hashStr = '';
if (hashIndex > -1) {
hashStr = rawUrl.slice(hashIndex + 1);
}
// 合并所有参数字符串
const paramStr = [queryStr, hashStr].filter(s => s).join('&');
// 核心解析逻辑
paramStr.split('&').forEach(pair => {
const [keyEncoded, valueEncoded] = pair.split('=');
if (!keyEncoded) return;
try {
const key = decodeURIComponent(keyEncoded);
const value = valueEncoded ? decodeURIComponent(valueEncoded) : '';
// 处理同名参数数组化
if (params.hasOwnProperty(key)) {
params[key] = Array.isArray(params[key])
? [...params[key], value]
: [params[key], value];
} else {
params[key] = value;
}
} catch (e) {
console.warn('参数解码失败:', e);
}
});
return params;
}
function patchGetSubjectDiagnosisOnReadyStateChange(proto) {
const xhr = proto.currentTarget;
if (xhr.readyState !== 4) {
return;
}
const responseURL = xhr.responseURL;
//alert(responseURL);
const params = parseURLParams(responseURL);
//alert(params.examId);
const code = params.examId;
//const code = responseURL.substring(72, 108);
//https://ali-bg.zhixue.com/zhixuebao/report/exam/getSubjectDiagnosis?examId=4ed7f790-90e1-4fa4-b43b-2d29f48d1d71
//https://ali-bg.zhixue.com/zhixuebao/report/exam/getLevelTrend?examId=4ed7f790-90e1-4fa4-b43b-2d29f48d1d71&pageIndex=1&pageSize=5
const data = JSON.parse(xhr.response);
data.result.list.forEach(element => {
GLOBALDATA.addRank(code, element.subjectName, element.myRank);
});
GLOBALDATA.calc(code);
}
function patchGetLevelTrendOnReadyStateChange(proto) {
console.log(">>>patchGetLevelTrendOnReadyStateChange");
const xhr = proto.currentTarget;
if (xhr.readyState !== 4) {
return;
}
const responseURL = xhr.responseURL;
//alert(responseURL);
const params = parseURLParams(responseURL);
//alert(params.examId);
const code = params.examId;
const data = JSON.parse(xhr.response);
console.log(data);
if(data.result.list.length > 0){
var classStatTotalNum=data.result.list[0].statTotalNum;
//alert(classStatTotalNum);
GLOBALDATA.setClassStatTotalNum(classStatTotalNum);
GLOBALDATA.addNumber(code, classStatTotalNum);
GLOBALDATA.calc(code);
}
if(data.result.list.length > 1){
var gradeStatTotalNum=data.result.list[1].statTotalNum;
//alert(gradeStatTotalNum);
GLOBALDATA.setGradeStatTotalNum(gradeStatTotalNum);
}
if(data.result.list.length > 2){
var unionStatTotalNum=data.result.list[2].totalNum;
//alert(unionStatTotalNum);
GLOBALDATA.setUnionStatTotalNum(unionStatTotalNum);
}
setTimeout(function() {
changeRankDisplay();
}, 1000);
}
function fetchComputedStyle(obj , property){
//能力检测
if(window.getComputedStyle){
//现在要把用户输入的property中检测一下是不是驼峰,转为连字符写法
//强制把用户输入的词儿里面的大写字母,变为小写字母加-
//paddingLeft → padding-left
property = property.replace(/([A-Z])/g , function(match,$1){
return "-" + $1.toLowerCase();
});
return window.getComputedStyle(obj)[property];
}else{
//IE只认识驼峰,我们要防止用户输入短横,要把短横改为大写字母
//padding-left → paddingLeft
property = property.replace(/\-([a-z])/g , function(match,$1){
return $1.toUpperCase();
});
return obj.currentStyle[property];
}
}
function getPercentage() {
var percent=0;
let runningElement = document.querySelector("div.class-running-container > div.class-running");
if(runningElement){
//alert(getElementLeftPercentage(runningElement));
var leftValue=fetchComputedStyle(runningElement,"left");
var widthValue=fetchComputedStyle(runningElement,"width");
leftValue = parseFloat(leftValue);
widthValue = parseFloat(widthValue);
//alert("leftValue:"+leftValue);
//alert("widthValue:"+widthValue);
percent=leftValue/widthValue;
}
return percent;
}
//根据百分比和人数计算排名
function getRank(startTotalNumber,percentage){
var rank=startTotalNumber*(1-percentage);
rank = Math.ceil(rank);
rank = Math.max(rank, 1)
return rank;
}
function changeRankDisplay(){
console.log(">>>changeRankDisplay");
let runningElement = document.querySelector("div.class-running-container > div.class-running");
if(runningElement){
var percentage=getPercentage();
var percentDisplay=(percentage)*100;
percentDisplay = parseFloat(percentDisplay.toFixed(2));
var subjectName='Test';
let tabItem=document.querySelector('div.zx-tab-list > div.current-tab > div.zx-tab-item-label > span > span');
if(tabItem){
//alert(tabItem.innerText);
subjectName=tabItem.innerText;
//alert(subjectName);
}
var startTotalNumber=1;
let element3 = document.querySelector("div.switch_tab_container>div.switch_tab>span.current");
if(element3){
//alert(element3.innerText);
if(element3.innerText==='班级'){
startTotalNumber=GLOBALDATA.classStatTotalNum;
}else if(element3.innerText==='年级'){
startTotalNumber=GLOBALDATA.gradeStatTotalNum;
}else{
startTotalNumber=GLOBALDATA.unionStatTotalNum;
}
}
//alert(GLOBALDATA.classStatTotalNum);
//alert(GLOBALDATA.gradeStatTotalNum);
//alert(GLOBALDATA.unionStatTotalNum);
var rank=getRank(startTotalNumber,percentage);
//alert("percent:"+percent);
var theElement = document.querySelector("div.score-level-change-head>h2.class-a-title");
if(theElement){
//theElement.innerText = '新的标题';
theElement.style.color='red';
theElement.innerText = "人数:"+startTotalNumber+" 排名:"+rank+" 超:"+percentDisplay+"%";
}
//let div = document.createElement("div");
//div.innerHTML ="成绩排名:"+leftValue+"";
//document.body.append(div);
}
}
function displayZhiXueRank() {
console.log(">>>displayZhiXueRank");
XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
patchRequest(url, this);
this._open(method, url, async, user, password);
};
}
(function() {
'use strict';
// Your code here...
displayZhiXueRank();
})();