// ==UserScript==
// @name 东南大学抢课助手正式版
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @description 听说你抢不到课
// @author realhuhu
// @license MIT
// @match http://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 = [1, 0, 2]
//请求
let request = axios.create();
//提示
let tip = grablessonsVue.$message
//设置
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": () => {
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.createCourseDetailPop(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
`
},
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.createCourseDetailPop = course => self.createNode({
tagName: "div",
obj: {
"style": `margin:30px`
},
})
//生成高级操作
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
}
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 currentType = grablessonsVue.teachingClassType
let currentCourseList = grablessonsVue.courseList
let node = document.getElementById("input-box")
let code = node.value.toUpperCase()
if (!code) return
if (enrollDict[code]) {
tip({
type: "warning",
message: "已经添加过了",
duration: 1000
})
return
}
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) {
tip({
type: "warning",
message: "没有查找到课程,请检查课程代码",
duration: 1000
})
} else if (!teacherFlag) {
tip({
type: "warning",
message: "没有查找到该教师,请检查教师号",
duration: 1000
})
} else {
tip({
type: "success",
message: "添加成功",
duration: 1000
})
node.value = ""
window.Components.reloadList()
methods.saveCourse()
}
}
},
//一键抢课
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
})
}
axios.all(key_list.map(key => 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
})
})))
}
}
window.Components.mount();
methods.init()
})();