// ==UserScript==
// @name 东南大学抢课助手修改版
// @namespace http://tampermonkey.net/
// @version 2.1.0
// @description 听说你抢不到课
// @author july
// @license MIT
// @match newxk.urp.seu.edu.cn/xsxk/elective/grablessons?*
// @run-at document-loaded
// @icon https://huhu-1304907527.cos.ap-nanjing.myqcloud.com/share/qkzs
// ==/UserScript==
(function () {
//版本
let version = [2, 1, 0];
//请求
let request = axios.create();
//提示
let tip = grablessonsVue.$message;
let isRunning = false;
//设置
let settings = {
auto: false,
};
//所选课程
let enrollDict = {};
//挂载的顶层组件
let app = document.getElementById("xsxkapp");
//组件生成
((self) => {
//生成组件
self.mount = () => {
self.createTag();
self.createPanel();
self.createMask();
};
//生成节点
self.createNode = ({ tagName, text, HTML, obj, ev, children }) => {
let node = document.createElement(tagName);
if (obj) {
for (let key of Object.keys(obj)) {
node.setAttribute(key, obj[key]);
}
}
if (text) {
node.innerText = text;
}
if (HTML) {
node.innerHTML = HTML;
}
if (ev) {
for (let key of Object.keys(ev)) {
node.addEventListener(key, ev[key]);
}
}
if (children) {
children.map((x) => node.appendChild(x));
}
return node;
};
//生成打开和关闭面板的按钮
self.createTag = () => {
let node = self.createNode({
tagName: "div",
obj: {
class: "slideMenu",
style: `
position: fixed;
top: 250px;
left:30px;width:
40px;z-index: 1314;
`,
},
children: [
self.createNode({
tagName: "div",
obj: {
class: "centre-btn item el-icon-date",
style: `background-color: #2b2b2b`,
},
ev: {
mousedown: (e) => {
methods.drag(e, node);
},
},
}),
],
});
app.appendChild(node);
};
//生成面板
self.createPanel = () => {
app.appendChild(
self.createNode({
tagName: "div",
obj: {
id: "panel",
style: `
position: fixed;
right: 0;
top:0 ;
z-index: 520;
width: 350px;
height: 100%;
background-color: rgba(61,72,105,0.8);
display: none
`,
},
children: [
self.createNode({ tagName: "hr" }),
self.createNode({
tagName: "h1",
text: "东大抢课脚本",
obj: {
style: "color: #c7e6e6; text-align: center",
},
}),
self.createNode({ tagName: "hr" }),
self.createNode({
tagName: "input",
obj: {
id: "input-box",
class: "el-input__inner",
style: `
width: 96%;
margin-left: 2%;
height: 30px
`,
placeholder: "输入课程代码(不区分大小写),按回车确定",
},
ev: {
keydown: methods.enter,
},
}),
self.createNode({
tagName: "div",
obj: {
id: "list-wrap",
style: `
overflow: auto;
margin: 10px;
border:1px solid white;
height: 50%
`,
},
}),
self.createNode({
tagName: "button",
obj: {
id: "enroll-button",
class: "el-button el-button--primary el-button--small is-round",
style: `
margin: 20px;
position: absolute;
right:2%;
bottom:25%
`,
},
text: "一键抢课",
ev: {
click: () => {
if (isRunning) {
tip({
type: "error",
message: "脚本已启动,请勿重复点击",
duration: 1000,
});
return;
}
isRunning = true;
methods.enroll();
},
},
}),
self.createNode({
tagName: "button",
obj: {
id: "enroll-button",
class: "el-button el-button--default el-button--small is-round",
style: `
margin: 20px;
position: absolute;
right:2%;
bottom:20%
`,
},
text: "高级设置",
ev: {
click: () => {
document.getElementById("mask").style.display = "block";
self.createPopUp("高级设置", self.createAdvancedPop());
},
},
}),
self.createNode({
tagName: "div",
obj: {
style: `
margin: 20px;
position: absolute;
right:2%;
bottom:10%;
color: white;
float: right
`,
},
text: "ver" + version.join("."),
}),
self.createNode({
tagName: "div",
obj: {
id: "update-tip",
style: `
margin: 20px;
position: absolute;
right:2%;
bottom:5%;
color: red;
float: right;
cursor: pointer;
display:none
`,
},
text: "有新版本,点击更新。更新后请重新进入选课页面",
ev: {
click: () => {
window.open("https://greasyfork.org/scripts/427237");
},
},
}),
],
})
);
self.reloadList();
};
//生成遮罩
self.createMask = () => {
let node = self.createNode({
tagName: "div",
obj: {
id: "mask",
style: `
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 2002;
background-color: rgba(66, 66, 66, 0.6);
display: none
`,
},
ev: {
click: () => {
node.style.display = "none";
app.removeChild(document.getElementsByClassName("temp")[0]);
},
},
});
app.appendChild(node);
};
//生成抢课表格
self.reloadList = () => {
let list_wrap = document.querySelector("#panel #list-wrap");
list_wrap.innerHTML = "";
if (JSON.stringify(enrollDict) === "{}") {
list_wrap.innerHTML =
"<h3 style='text-align: center;color:lightblue;margin-top: 50%'>还未选择课程</h3>";
} else {
list_wrap.appendChild(
self.createNode({
tagName: "table",
obj: {
width: "100%",
border: "1",
style: `
background-color: rgba(0,0,0,0);
color: lightblue
`,
},
children: [
self.createNode({
tagName: "tr",
obj: {
style: `
height: 30px;
background-color: #255e95
`,
},
HTML: `
<th style="text-align:center;width: 55%">课程</th>
<th style="text-align:center;width: 15%">教师</th>
<th style="text-align:center;width: 30%">操作</th>
`,
}),
...Object.keys(enrollDict)
.filter(
(key) =>
enrollDict[key].courseBatch ===
grablessonsVue.lcParam.currentBatch.code
)
.map((key) => {
return self.createNode({
tagName: "tr",
obj: {
style: `height: 30px`,
},
children: [
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: enrollDict[key].courseName,
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: enrollDict[key].teacherName,
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
children: [
self.createNode({
tagName: "button",
text: "删除",
obj: {
style: `
color: red;
background: transparent;
border: 1px solid red;
border-radius: 6px;
text-align: center;
cursor: pointer;
text-decoration: none;
margin-right: 2px
`,
},
ev: {
click: () => {
delete enrollDict[key];
methods.saveCourse();
tip({
type: "success",
message: "已删除",
duration: 1000,
});
self.reloadList();
},
},
}),
self.createNode({
tagName: "button",
text: "更多",
obj: {
style: `
color: orange;
background: transparent;
border: 1px solid orange;
border-radius: 6px;
text-align: center;
cursor: pointer;
text-decoration: none;
margin-left: 2px
`,
},
ev: {
click: () => {
document.getElementById("mask").style.display =
"block";
self.createPopUp(
"更多操作",
self.showCourseDetails(enrollDict[key])
);
},
},
}),
],
}),
],
});
}),
],
})
);
}
};
//生成弹出窗
self.createPopUp = (title, node, width, height) =>
app.appendChild(
self.createNode({
tagName: "div",
obj: {
class: "temp",
style: `
position: fixed;
left: ${width ? 50 - 0.5 * width : 30}%;
top: ${height ? 50 - 0.5 * height : 30}%;
width: ${width || 40}%;
height: ${height || 40}%;
z-index: 2021;
background-color: white;
border-radius: 30px;
overflow: auto;
`,
},
children: [
self.createNode({
tagName: "h1",
obj: {
style: `
margin: 20px 0;
width: 100%;
text-align: center;
`,
},
text: title,
}),
node,
self.createNode({
tagName: "button",
obj: {
class: "el-button el-button--default el-button--large is-round",
style: `
margin: 20px;
position: absolute;
right:10%;
bottom:0
`,
},
text: "确定",
ev: {
click: () => {
document.getElementById("mask").style.display = "none";
app.removeChild(document.getElementsByClassName("temp")[0]);
},
},
}),
],
})
);
self.showCourseDetails = (course) => {
return self.createNode({
tagName: "div",
obj: {
style: `margin:30px`,
},
children: [
self.createNode({
tagName: "table",
obj: {
width: "80%",
border: "1",
style: `
background-color: rgba(0,0,0,0);
color: black;
margin: 0 auto; /* 居中显示 */
`,
},
children: [
self.createNode({
tagName: "tr",
obj: {
style: `
height: 30px;
background-color: #255e95;
color: lightblue;
`,
},
HTML: `
<th style="text-align:center;width: 30%">属性</th>
<th style="text-align:center;width: 50%">值</th>
`,
}),
self.createNode({
tagName: "tr",
obj: {
style: `height: 30px`,
},
children: [
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: "课程名称",
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: course.courseName,
}),
],
}),
self.createNode({
tagName: "tr",
obj: {
style: `height: 30px`,
},
children: [
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: "教师名称",
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: course.teacherName,
}),
],
}),
self.createNode({
tagName: "tr",
obj: {
style: `height: 30px`,
},
children: [
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: "课程代码",
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: course.courseCode,
}),
],
}),
self.createNode({
tagName: "tr",
obj: {
style: `height: 30px`,
},
children: [
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: "课程类型",
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: course.courseType,
}),
],
}),
self.createNode({
tagName: "tr",
obj: {
style: `height: 30px`,
},
children: [
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: "批次",
}),
self.createNode({
tagName: "td",
obj: {
style: `text-align: center`,
},
text: course.courseBatch,
}),
],
}),
],
}),
],
});
};
//生成高级操作
self.createAdvancedPop = () =>
self.createNode({
tagName: "div",
obj: {
style: `
margin:50px
`,
},
children: [
self.createNode({
tagName: "div",
children: [
self.createNode({
tagName: "input",
obj: {
id: "auto",
type: "checkbox",
value: "settings.auto",
},
}),
self.createNode({
tagName: "label",
obj: {
for: "auto",
},
text: "自动抢课(开发中)",
}),
],
}),
],
});
})((window.Components = window.Components || {}));
let methods = {
//初始化数据
init() {
methods.checkVersion();
let raw = JSON.parse(localStorage.getItem("huhu"));
if (raw) {
settings = raw.settings;
if (settings.jwt === sessionStorage.token) {
enrollDict = raw.enrollDict;
} else if (JSON.stringify(raw.enrollDict) !== "{}") {
tip({
type: "warning",
message: "登录信息发生变动,已清空抢课列表",
duration: 1000,
});
enrollDict = {};
settings.jwt = sessionStorage.token;
methods.saveCourse();
}
} else {
settings.jwt = sessionStorage.token;
}
isRunning = false;
window.Components.reloadList();
},
checkVersion() {
request
.get("https://api.seutools.com/enroll/", {
transformRequest: [
(data, headers) => {
delete headers.Authorization;
delete headers.batchId;
return data;
},
],
})
.then((res) => {
if (res.data.version.split(".").map((x) => parseInt(x)) > version) {
document.getElementById("update-tip").style.display = "block";
}
});
},
//保存数据
saveCourse() {
localStorage.setItem("huhu", JSON.stringify({ enrollDict, settings }));
},
//处理按钮拖动与点击
drag(e, node) {
let is_move = false;
let x = e.pageX - node.offsetLeft;
let y = e.pageY - node.offsetTop;
document.onmousemove = function (e) {
node.style.left = e.pageX - x + "px";
node.style.top = e.pageY - y + "px";
is_move = true;
};
document.onmouseup = function () {
document.onmousemove = document.onmouseup = null;
if (!is_move) {
let panel = document.getElementById("panel");
panel.style.display === "block"
? (panel.style.display = "none")
: (panel.style.display = "block");
}
is_move = false;
};
},
// 处理输入框事件
enter(e) {
let evt = window.event || e;
if (evt.keyCode === 13) {
let node = document.getElementById("input-box");
let codeArray = node.value.toUpperCase().split(" ");
let failedCodes = methods.addEnrollDict(codeArray.join(" "));
node.value = failedCodes.join(" "); // 将失败的课程代码替换到输入框中
}
},
// 添加课程到抢课列表
addEnrollDict(str) {
if (!str) return [];
let currentType = grablessonsVue.teachingClassType;
let currentCourseList = grablessonsVue.courseList;
let codeArray = str.split(" ");
let failedCodes = []; // 用于存储添加失败的课程代码
for (let i = 0; i < codeArray.length; i++) {
let code = codeArray[i];
if (!code) continue;
if (enrollDict[code]) {
tip({
type: "warning",
message: "已经添加过了",
duration: 1000,
});
continue;
}
let courseCode = code.substring(0, 8);
let teacherCode = code.substring(8);
let courseFlag = false,
teacherFlag = false;
for (let course of currentCourseList) {
// 检查课程是否存在
if (course.KCH === courseCode) {
courseFlag = true;
// 检查教师是否存在
if (grablessonsVue.teachingClassType !== "XGKC") {
for (let teacher of course.tcList) {
if (teacher.KXH === teacherCode) {
enrollDict[code] = {
courseBatch: grablessonsVue.lcParam.currentBatch.code,
courseCode: teacher.JXBID,
courseType: currentType,
courseName: course.KCM,
teacherName: teacher.SKJS,
secretVal: teacher.secretVal,
};
teacherFlag = true;
}
}
} else {
if (course.KXH === teacherCode) {
enrollDict[code] = {
courseBatch: grablessonsVue.lcParam.currentBatch.code,
courseCode: course.JXBID,
courseType: currentType,
courseName: course.KCM,
teacherName: course.SKJS,
secretVal: course.secretVal,
};
teacherFlag = true;
}
}
}
}
if (!courseFlag) {
failedCodes.push(code); // 添加到失败的课程代码列表
} else if (!teacherFlag) {
tip({
type: "error",
message: "没有查找到该教师,请检查教师号",
duration: 1000,
});
console.log("无效的教师号: ", teacherCode);
failedCodes.push(code); // 添加到失败的课程代码列表
} else {
tip({
type: "success",
message: "添加成功",
duration: 1000,
});
}
}
methods.saveCourse();
return failedCodes; // 返回失败的课程代码
},
//一键抢课
enroll() {
let key_list = Object.keys(enrollDict).filter(
(key) =>
enrollDict[key].courseBatch ===
grablessonsVue.lcParam.currentBatch.code
);
if (!key_list.length) {
tip({
type: "warning",
message: "还没有输入课程",
duration: 1000,
});
return;
}
const interval = 375; // 设置时间间隔,单位为毫秒
let index = 0;
const enrollCourse = () => {
if (index >= key_list.length) {
return;
}
const key = key_list[index];
request({
url: "/elective/clazz/add",
method: "POST",
headers: {
batchId: enrollDict[key].courseBatch,
"content-type": "application/x-www-form-urlencoded",
},
data: Qs.stringify({
clazzType: enrollDict[key].courseType,
clazzId: enrollDict[key].courseCode,
secretVal: enrollDict[key].secretVal,
}),
}).then((res) => {
let type = res.data.code === 100 ? "success" : "warning";
tip({
type,
message: enrollDict[key].courseName + ":" + res.data.msg,
duration: 1000,
});
index++;
setTimeout(enrollCourse, interval); // 在每次请求后设置时间间隔
});
};
enrollCourse(); // 开始执行抢课
},
};
window.Components.mount();
methods.init();
})();