您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
广州华立科技职业学院挂机课程,适用于https://gzhlxy.sccchina.net/
// ==UserScript== // @name 广州华立科技职业学院课程助手 // @namespace http://tampermonkey.net/ // @version 2024-04-13 // @description 广州华立科技职业学院挂机课程,适用于https://gzhlxy.sccchina.net/ // @author ZaneBill // @match https://gzhlxy.sccchina.net/student/ // @icon https://www.google.com/s2/favicons?sz=64&domain=sccchina.net // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.2.1/jquery.min.js // @require http://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js // @grant none // @license AGPL License // ==/UserScript== (function() { 'use strict'; $(document).ready(function(){ // 定义全局变量 let UPDATE_COURSE_GAP = 60 // 多久上传一次进度 秒 let UPDATE_PROGRESS_GAP = UPDATE_COURSE_GAP + 2 // 多久更新一次列表进度 秒 let STUDY_DURATION = 6 // 每次观看进度 官方是30 setInterval间隔一分钟 let courseList = [] let timerMap = new Map() // 分割已观看/总时长分钟数 function splitPlayTime(str){ const arr = str.split(' / ') return { finished: arr[0], total: arr[1] } } // 根据我的课程列表 创建脚本界面 function makeLayout(list = courseList){ let operateBox = "<div class='operateBox'><table class='table'><tbody></tbody></table></div>" $('body').append(operateBox) let head = "<div class='head'><div><span id='tip1'></span><span id='tip2'></span></div><button id='oneKeyStart'>一键开刷</button></div>" let inputBox1 = `<div>列表更新频率(秒)<input type='number' value='${UPDATE_PROGRESS_GAP}' min='1' max='9999' placeholder='输入1-9999之间的数字' data-name='list'></input></div>` let inputBox2 = `<div>课程提交频率(秒)<input type='number' value='${UPDATE_COURSE_GAP}' min='1' max='9999' placeholder='输入1-9999之间的数字' data-name='course'></input></div>` let inputBox3 = `<div>课程提交跨度<input type='number' value='${STUDY_DURATION}' min='1' max='9999' placeholder='输入1-9999之间的数字' data-name='step'></input></div>` let inputBox = `<div class='inputBox'>${inputBox1}${inputBox2}${inputBox3}</div>` $('.operateBox .table').before(inputBox) $('.operateBox .table').before(head) $('#tip1').text(`每隔${UPDATE_PROGRESS_GAP}秒自动更新列表,每隔${UPDATE_COURSE_GAP}秒提交课程,跨度为${STUDY_DURATION}秒`) let content = list.reduce((pre, item, index)=> { let tr = `<tr><td>${index + 1}</td><td>${item.courseName}</td><td>${item.coursewareLearningProgress}</td><td>${splitPlayTime(item.realCoursewarePlayTime).finished}</td>` + `<td>${splitPlayTime(item.realCoursewarePlayTime).total}</td><td class='newMinute'></td><td class='newPercent'></td></tr>`; return pre += tr }, '<tr><th>序号</th><th>课程名</th><th>完成度</th><th>已完成分钟数</th><th>总分钟数</th><th>新分钟数</th><th>新完成度</th></tr>') $('.operateBox .table tbody').append(content) // 去掉操作栏的单个开刷按钮 用一键开刷 <th>操作</th> <td class='opt_btn' data-index='${index}'>开刷</td> $('.operateBox').css({ 'position': 'fixed', 'bottom': '50px', 'right': '30px', 'background': '#eee', 'border-radius': '4px', 'padding': '5px' }) $('.operateBox .inputBox').css({ 'display': 'flex', 'justify-content': 'space-between', 'padding': '10px 0' }) $('.operateBox .inputBox input').css({ 'width': '100px', }) $('.operateBox .head').css({ 'display':'flex', 'justify-content': 'space-between', 'align-items': 'center' }) $('#oneKeyStart').css({ 'cursor': 'pointer', }) $('table').css({ 'border-collapse':'collapse' }) $('td,th').css({ 'padding':'0 5px' }) $('table,th,td ').css({ 'border': '1px solid black', }) $('#oneKeyStart,.opt_btn').css({ 'cursor': 'pointer', 'color': '#1890ff' }) $('.newMinute,.newPercent').css({ 'min-width': '30px', }) } // 获取我的课程进度 function getList(){ return new Promise((resolve, reject)=> { try{ $.ajax({ url: '/student/student/coursestudy/getlist', type: 'POST', dataType: 'json', contentType: "application/json", processData: false, data: JSON.stringify({ data:"" }), success: function(response) { if(response.items){ courseList = response.items resolve(response.items) } }, error: function(xhr, status, error) { reject(error) }, }); }catch (error){ reject(error) } }) } // 获取token function getCourseToken(item){ return new Promise((resolve, reject)=> { try{ const { courseVersionID, sign, teachplanCourseVersionId } = item $.ajax({ url: '/student/common/common/getcoursewareaddress', type: 'POST', dataType: 'json', contentType: "application/json", processData: false, data: JSON.stringify({ data:{ courseVersionId: courseVersionID, sign, teachplanCourseVersionId, roleType: "1" } }), success: function(response) { if(response.code === 1){ resolve(response.data.token) } }, error: function(xhr, status, error) { reject(error) }, }); }catch(error){ reject(error) } }) } // 增加时长 function addDurationFn(data = {}){ const { courseVersionId, studyDuration, token } = data return new Promise((resolve, reject)=> { try{ $.ajax({ url: '/student/student/coursestudyrecord/adddurationpc', type: 'POST', dataType: 'json', contentType: "application/json", processData: false, data: JSON.stringify({ data:{ courseVersionId, studyDuration, token } }), success: function(response) { if(response.code === 1){ resolve('success') } }, error: function(xhr, status, error) { reject(error) }, }); }catch (error){ reject(error) } }) } // 主要逻辑 循环发送请求更新课程观看进度 async function updateCourse(curItem){ const token = await getCourseToken(curItem) const data = { courseVersionId:curItem.courseVersionID, studyDuration: STUDY_DURATION, token } let timer = setInterval(async ()=> { await addDurationFn(data) }, UPDATE_COURSE_GAP * 1000) timerMap.set(timer, curItem.courseName) } // 更新页面展示的进度 async function updateTable(){ let timer = setInterval(async ()=> { const list = await getList() // 新完成度 百分比 $('.operateBox .table .newPercent').text(function(index, currentText) { const item = list[index] const beforeText = `${item.coursewareLearningProgress}` if(currentText && beforeText != currentText){ // 增加对比样式 const node = $('.operateBox .table .newPercent')[index] $(node).css({ 'background-color': 'gold' }) } return beforeText || currentText; }); // 新分钟数 $('.operateBox .table .newMinute').text(function(index, currentText) { const item = list[index] const beforeText = `${splitPlayTime(item.realCoursewarePlayTime).finished}` if(currentText && beforeText != currentText){ // 增加对比样式 const node = $('.operateBox .table .newMinute')[index] $(node).css({ 'background-color': 'green' }) } return beforeText || currentText; }); }, UPDATE_PROGRESS_GAP * 1000) timerMap.set(timer, new Date()) } // 事件监听 function addListening(){ $('.operateBox .opt_btn').on("click", async function(e){ const index = e.target.dataset.index const curItem = courseList[index] await updateCourse(curItem) updateTable() }); $('.operateBox .inputBox input').on("input", async function(e){ const value = +e.target.value const name = e.target.dataset.name switch (name) { case 'list': UPDATE_PROGRESS_GAP = value || UPDATE_PROGRESS_GAP break; case 'course': UPDATE_COURSE_GAP = value || UPDATE_COURSE_GAP break; case 'step': STUDY_DURATION = value || STUDY_DURATION break; default: break; } }); $('#oneKeyStart').on("click",function(){ timerMap.forEach((item,key) => { clearInterval(key) }) timerMap = new Map() onFastStart() }) } // 一键开刷 function onFastStart(){ // 开刷前清空定时器 timerMap.forEach((item,key) => { clearInterval(key) }) timerMap = new Map() let taskNum = 0 for(const item of courseList){ // 未完成的才启动 const progress = item.coursewareLearningProgress.slice(0, -1) if(progress < 100){ taskNum++ updateCourse(item) } } updateTable() $('#tip1').text(`每隔${UPDATE_PROGRESS_GAP}秒自动更新列表,每隔${UPDATE_COURSE_GAP}秒提交课程,跨度为${STUDY_DURATION}秒`) $('#tip2').text(`,已启动${taskNum}个任务,正在更新···`) } // 程序运行入口 async function main (){ await getList() makeLayout() addListening() } // 稍微等一下主页面渲染出来再加载脚本 setTimeout(()=>{ main() },2000) }) })();