// ==UserScript==
// @name 宜宾智慧校园助手
// @namespace 智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案===来自计算机科学与技术学院--修改自若离智慧校园
// @version 6.5
// @description 智慧校园,解决宜宾学院智慧校园的题目,能够自动获取宜宾学院的智慧校园的作业的答案,能够跳过秒看教学视频自动跳转下一个视频,支持自动答题。
// @author 计算机科学与技术学院---软工
// @match https://mooc.yibinu.edu.cn/*
// @icon https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png
// @resource cs1 https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.css
// @resource cs2 https://pan.ruoli.cc/s/8b0cc4
// @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js
// @require https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/ant-design-vue/1.7.8/antd.min.js
// @require https://cdn.sheetjs.com/xlsx-0.19.3/package/dist/xlsx.full.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// @run-at document-end
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getResourceText
// ==/UserScript==
// 脚本初始化
var setting = {
'logs': ['初始化脚本完成,', '当前脚本版本:V6.3'],
'datas': [],
'secretKey': '你好', // 暗号
'_vt': '274c8b3f2a8c63ffc960dd1e4e3f0eac',
};
// 时间值
function decryptValidDuration() {
try {
const validDurations = {
'c51ce410c124a10e0db5e4b97fc2af39': 86400000,
'274c8b3f2a8c63ffc960dd1e4e3f0eac': 172800000,
'e2ef524fbf3d9fe611d5a8e90fefdc9c': 259200000,
'069059b7ef840f0c74a814ec9237b6ec': 432000000,
'7f6ffaa6bb0b408017b62254211691b5': 604800000,
'149e9677a5989fd342ae44213df68868': 2592000000
};
return validDurations[setting._vt] || 172800000; // 默认返回2天
} catch (e) {
console.error('解密时间值出错:', e);
return 172800000; // 解密出错时返回默认值(2天)
}
}
// 简化的验证时间管理
function setValidTime() {
try {
const validDuration = decryptValidDuration();
const validUntil = Date.now() + validDuration;
const data = {
time: validUntil,
hash: CryptoJS.SHA256(validUntil.toString()).toString()
};
localStorage.setItem('scriptValidUntil', JSON.stringify(data));
return true;
} catch (e) {
console.error('设置验证时间出错:', e);
return false;
}
}
// 检查验证时间
function checkValidTime() {
try {
const data = localStorage.getItem('scriptValidUntil');
if (!data) return false;
const { time, hash } = JSON.parse(data);
const now = Date.now();
// 验证时间和哈希
if (time && hash &&
hash === CryptoJS.SHA256(time.toString()).toString() &&
time > now) {
return true;
}
localStorage.removeItem('scriptValidUntil');
return false;
} catch (e) {
console.error('检查验证时间出错:', e);
localStorage.removeItem('scriptValidUntil');
return false;
}
}
// 将验证函数定义为全局函数
window.verifySecret = function() {
const input = document.getElementById('secretInput');
if (!input) {
console.error('找不到输入框元素');
return;
}
const inputValue = input.value.trim();
if (!inputValue) {
input.style.borderColor = '#ff4d4f';
input.style.animation = 'shake 0.5s';
setTimeout(() => {
input.style.borderColor = '#e8e8e8';
input.style.animation = '';
}, 1000);
return;
}
if (inputValue !== setting.secretKey) {
const modalDiv = document.querySelector('#secretModal > div');
if (modalDiv) {
modalDiv.style.animation = 'shake 0.5s';
input.style.borderColor = '#ff4d4f';
setTimeout(() => {
modalDiv.style.animation = '';
input.style.borderColor = '#e8e8e8';
}, 1000);
}
return;
}
try {
// 设置验证时间
if (!setValidTime()) {
throw new Error('设置验证时间失败');
}
// 添加关闭动画
const modal = document.getElementById('secretModal');
if (modal) {
modal.style.animation = 'modalFadeOut 0.3s ease';
setTimeout(() => {
modal.remove();
continueInit();
// 添加成功提示
if (window.vue) {
window.vue.$message.success('验证成功!');
}
}, 300);
}
} catch (error) {
console.error('验证过程出错:', error);
if (window.vue) {
window.vue.$message.error('验证过程出错,请刷新页面重试');
}
}
};
// 添加键盘事件监听器的函数
function addKeyboardListener() {
document.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
const secretInput = document.getElementById('secretInput');
if (secretInput && document.activeElement === secretInput) {
verifySecret();
}
}
});
}
// 日志
function log(logText){
setting.logs.unshift(logText);
// Ensure Vue instance logs are updated
if (window.vue) {
window.vue.$nextTick(() => {
window.vue.logs = [...setting.logs];
});
}
}
// 添加一个清理HTML标签的函数
function cleanHtmlTags(text) {
if (!text) return '';
// 将HTML转换为纯文本
let temp = document.createElement('div');
temp.innerHTML = text;
let cleanText = temp.textContent || temp.innerText;
// 清理多余的空白字符
cleanText = cleanText.replace(/\s+/g, ' ').trim();
return cleanText;
}
// 从后台获取答案
function getAnswer(url, data){
log('正在获取答案...');
let id = url.match(/\/examSubmit\/(\d+)\/getExamPaper/)[1];
GM_xmlhttpRequest({
method: "post",
url: url,
data: data,
dataType: 'json',
headers: {
'Origin': location.origin,
'User-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
'Referer': `https://mooc.yibinu.edu.cn/examTest/stuExamList/${id}.mooc`
},
onload: function(res){
if(res.status == 200){
try {
let response = JSON.parse(res.responseText);
if (response && response.paper && response.paper.paperStruct) {
log("获取答案成功正在格式化答案!");
formatAnswer(response.paper.paperStruct);
} else {
log("答案数据格式异常,请检查接口返回");
}
} catch (error) {
log("解析答案数据失败:" + error.message);
}
} else {
log("获取答案失败,状态码:" + res.status);
}
},
onerror: function(error) {
log("求答案失败:" + error.message);
}
});
}
//格式化答案
function formatAnswer(str) {
try {
setting.datas = []; // 清空之前的数据
if (!Array.isArray(str)) {
log("答案数据式错误");
return;
}
str.forEach((listItem, index) => {
if (!listItem.quiz) {
return;
}
// 使用cleanHtmlTags清理题目内容
var question = cleanHtmlTags(listItem.quiz.quizContent) || "未知题目";
var options = {};
var optionContents = {}; // 存储选项内容
var answer = [];
const questionNum = (index + 1).toString();
// 处理选择题
if (listItem.quiz.quizOptionses && listItem.quiz.quizOptionses.length > 0) {
listItem.quiz.quizOptionses.forEach((optionItem, idx) => {
if (optionItem && optionItem.optionId !== undefined) {
const optionLabel = String.fromCharCode(65 + idx);
options[optionItem.optionId] = optionLabel;
optionContents[optionItem.optionId] = optionItem.optionContent || '';
}
});
// 处理答案
if (listItem.quiz.quizResponses) {
listItem.quiz.quizResponses.forEach(answerItem => {
if (answerItem && options[answerItem.optionId]) {
const label = options[answerItem.optionId];
const content = optionContents[answerItem.optionId];
answer.push(`${label}.${content}`);
}
});
}
// 并序号和选项标签
const answerLabels = listItem.quiz.quizResponses
.map(item => options[item.optionId])
.join('');
const idAndOptions = `${questionNum}.${answerLabels}`;
setting.datas.push({
'key': index.toString(),
'idAndOptions': idAndOptions,
'question': question,
'answer': answer.join('\n') // 每个选项答案换行显示
});
} else {
// 处理填空题
if (listItem.quiz.quizResponses) {
const fillAnswers = [];
listItem.quiz.quizResponses.forEach(answerItem => {
if (answerItem && answerItem.responseContent) {
fillAnswers.push(answerItem.responseContent);
}
});
setting.datas.push({
'key': index.toString(),
'idAndOptions': `${questionNum}.(填空)`,
'question': question,
'answer': fillAnswers.join('\n') // 多个填空答案换行显示
});
}
}
});
// 更新 Vue 实例中的数据
if (window.vue) {
Vue.nextTick(() => {
window.vue.answerList = [...setting.datas];
window.vue.hasAnswer = true; // 设置答案获取状态为 true
});
}
log(`成功处理 ${setting.datas.length} 道题目`);
log('答案获取完成,可以切换到答案列表查看');
} catch (error) {
log("格式化答案时出错:" + error.message);
if (window.vue) {
window.vue.hasAnswer = false;
}
}
}
//初始化界面
function initView(){
// 检查验证是否有效
if (checkValidTime()) {
// 验证仍然有效,直接继续初始化
continueInit();
return;
}
// 创建验证界面的HTML
const createModal = () => {
const modalHtml = `
<div id="secretModal" style="
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
">
<div style="
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 8px 24px rgba(0,0,0,0.2);
width: 320px;
text-align: center;
animation: modalFadeIn 0.3s ease;
">
<img src="https://pic.imgdb.cn/item/673c85b1d29ded1a8ce8b97c.png" style="
width: 64px;
height: 64px;
margin-bottom: 15px;
">
<h2 style="
margin: 0 0 20px 0;
color: #333;
font-size: 20px;
font-weight: 500;
">请输入暗号---你好</h2>
<input type="text" id="secretInput" style="
width: 100%;
padding: 12px;
margin-bottom: 15px;
border: 2px solid #e8e8e8;
border-radius: 8px;
font-size: 16px;
outline: none;
transition: all 0.3s;
box-sizing: border-box;
" placeholder="请输入暗号...">
<button id="verifyButton" style="
width: 100%;
padding: 12px;
border: none;
background-color: #1890ff;
color: white;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
box-sizing: border-box;
">验证</button>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalHtml);
// 添加事件监听器
const button = document.getElementById('verifyButton');
if (button) {
button.addEventListener('click', window.verifySecret);
}
// 添加键盘事件监听
addKeyboardListener();
// 自动聚焦输入框
setTimeout(() => {
const input = document.getElementById('secretInput');
if (input) {
input.focus();
}
}, 100);
};
// 添加动画样式
const style = document.createElement('style');
style.textContent = `
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
@keyframes modalFadeIn {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes modalFadeOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-20px); }
}
`;
document.head.appendChild(style);
createModal();
}
// 将原来的初始化代码移动到新的函数中
function continueInit() {
var $div =$('<div class="rlBox minimized">' +
' <a-card title="宜宾学院智慧校园助手" style="width: 100%;height: 100%;">' +
' <template slot="extra">' +
' <span v-show="!close" style="margin-right: 10px; font-size: 12px; color: #999;">{{validTimeRemaining}}</span>' +
' <a-button :type="buttonColor" shape="circle" :icon="buttonIcon" @click="toClose" size="small"/>' +
' </template>' +
' <div style="margin-bottom: 15px;" v-show="!close">' +
' <a-button-group style="width: 100%;">' +
' <a-button type="danger" style="width: 25%;" @click="passVideo()">秒过视频</a-button>' +
' <a-button type="primary" style="width: 25%;" @click="exportExcel()">导出题库</a-button>' +
' <a-button type="success" style="width: 25%;" @click="clearLogs()">清除日志</a-button>' +
' <a-button type="warning" style="width: 25%;" @click="autoAnswer()">自动答题</a-button>' +
' </a-button-group>' +
' </div>' +
' <a-tabs default-active-key="1" @change="callback" v-show="!close">' +
' <a-tab-pane key="1" tab="运行日志">' +
' <div class="rl-panel log">' +
' <p v-for="item in logs" class="log_content">' +
' {{item}}' +
' </p>' +
' </div>' +
' </a-tab-pane>' +
' <a-tab-pane key="2" :tab="answerTabTitle" :disabled="!hasAnswer">' +
' <div class="rl-panel">' +
' <a-table id="rlTable"' +
' :pagination="false" bordered size="small" :columns="columns" :data-source="answerList">' +
' </a-table>' +
' </div>' +
' </a-tab-pane>' +
' </a-tabs>' +
' </a-card>' +
'</div>');
// 更新样式
const customStyle = `
.rlBox {
position: fixed;
top: 10px;
right: 10px;
width: 400px;
z-index: 9999;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 10px;
transition: all 0.3s ease;
transform: none !important;
overflow: hidden;
}
/* 优化最小化状态 */
.rlBox.minimized {
width: 40px !important;
height: 40px !important;
padding: 0 !important;
overflow: hidden;
opacity: 0.8;
cursor: pointer;
border-radius: 50%;
background: #1890ff;
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 按钮样式优化 */
.ant-btn-danger {
background: #ff4d4f !important;
border-color: #ff4d4f !important;
color: white !important;
}
.ant-btn-danger:hover {
background: #ff7875 !important;
border-color: #ff7875 !important;
}
.ant-btn-primary {
background: #1890ff !important;
border-color: #1890ff !important;
}
.ant-btn-primary:hover {
background: #40a9ff !important;
border-color: #40a9ff !important;
}
.ant-btn-success {
background: #52c41a !important;
border-color: #52c41a !important;
color: white !important;
}
.ant-btn-success:hover {
background: #73d13d !important;
border-color: #73d13d !important;
}
/* 最小化状态下的卡片样式 */
.rlBox.minimized .ant-card {
background: transparent;
border: none;
box-shadow: none;
}
/* 最小化状态下的标题隐藏 */
.rlBox.minimized .ant-card-head-title {
display: none;
}
/* 最小化状态下的开按钮样式 */
.rlBox.minimized .ant-btn-circle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: transparent !important;
border: 2px solid white !important;
color: white !important;
box-shadow: none;
}
.rlBox.minimized .ant-btn-circle:hover {
background: rgba(255, 255, 255, 0.2) !important;
}
/* 最小化状态悬停效果 */
.rlBox.minimized:hover {
opacity: 1;
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4);
transform: translateY(-2px) !important;
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.rlBox {
width: 300px;
}
.rlBox.minimized {
width: 36px !important;
height: 36px !important;
}
}
/* 表格容器样式 */
.rl-panel {
height: auto;
max-height: calc(100vh - 250px);
overflow-y: auto;
overflow-x: hidden;
}
/* 表格样式优化 */
.ant-table-wrapper {
overflow: visible;
}
.ant-table {
min-width: 100%;
background: transparent;
}
/* 美化滚动条样式 */
.rl-panel::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.rl-panel::-webkit-scrollbar-thumb {
background: #d9d9d9;
border-radius: 3px;
}
.rl-panel::-webkit-scrollbar-track {
background: #f0f0f0;
border-radius: 3px;
}
/* 表格单元格样式 */
.ant-table-tbody > tr > td {
white-space: normal;
word-break: break-word;
padding: 8px 16px;
line-height: 1.5;
max-width: 0;
}
/* 表格头部样式 */
.ant-table-thead > tr > th {
background: #f5f5f5;
padding: 12px 16px;
white-space: nowrap;
position: sticky;
top: 0;
z-index: 2;
}
/* 保表格布局合理 */
#rlTable {
table-layout: fixed;
width: 100%;
}
/* 移动端适配 */
@media screen and (max-width: 768px) {
.rl-panel {
max-height: calc(100vh - 200px);
}
.ant-table-tbody > tr > td {
padding: 6px 12px;
}
.ant-table-thead > tr > th {
padding: 8px 12px;
}
}
`;
$("body").append($div);
GM_addStyle(GM_getResourceText("cs1"));
GM_addStyle(GM_getResourceText("cs2"));
GM_addStyle(customStyle);
var vue = new Vue({
el: '.rlBox',
data:{
logs: setting.logs,
close: true,
key: '1',
columns:[
{
title: '序号.选项',
dataIndex: 'idAndOptions',
key: 'idAndOptions',
width: '80px',
fixed: 'left',
align: 'center'
},
{
title: '题目',
dataIndex: 'question',
key: 'question',
width: '45%',
ellipsis: true
},
{
title: '答案',
dataIndex: 'answer',
key: 'answer',
width: '45%',
customRender: (text) => {
return text ? text.split('\n').join('<br/>') : '';
}
}
],
answerList: [], // 初化为空数组
isDragging: false,
currentX: 0,
currentY: 0,
initialX: 0,
initialY: 0,
xOffset: 0,
yOffset: 0,
hasAnswer: false, // 添加答案获取状态标志
validUntil: localStorage.getItem('scriptValidUntil') || null,
},
mounted() {
window.vue = this;
this.initDragEvents();
// 修改初始化位置设置
const box = document.querySelector('.rlBox');
box.style.right = '0px';
box.style.left = 'auto';
box.setAttribute('data-expand-side', 'right'); // 设置默认展开方向
// 添加窗口大小改变监听
window.addEventListener('resize', this.checkPosition);
// 每分钟更新一次验证时间显示
setInterval(() => {
this.validUntil = localStorage.getItem('scriptValidUntil');
}, 60000);
},
computed:{
isShow(){
return this.close ? 0.8 : 1.0;
},
buttonIcon(){
return this.close ? 'plus' : 'minus';
},
buttonColor(){
return this.close ? 'primary' : 'default';
},
answerTabTitle() {
return this.hasAnswer ? '答案列表' : '答案列表 (等待获取...)';
},
validTimeRemaining() {
const validTime = getValidTime();
if (!validTime) return '未验证';
const remaining = validTime - Date.now();
if (remaining <= 0) return '验证已过期';
const days = Math.floor(remaining / (24 * 60 * 60 * 1000));
const hours = Math.floor((remaining % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
return `验证剩余: ${days}天${hours}小时`;
}
},
methods: {
callback(key) {
if (key === '2' && !this.hasAnswer) {
this.$message.warning('请等待答案获取完成后再查看答案列表');
this.key = '1'; // 保持在日志页面
return;
}
this.key = key;
},
toClose() {
this.close = !this.close;
const box = document.querySelector('.rlBox');
const rect = box.getBoundingClientRect();
const windowWidth = window.innerWidth;
if (this.close) {
// 最小化时,判断靠近哪边
box.style.transition = 'all 0.3s ease';
const centerX = rect.left + rect.width / 2;
if (centerX > windowWidth / 2) {
// 靠右
box.style.right = '10px';
box.style.left = 'auto';
box.setAttribute('data-side', 'right');
} else {
// 靠左
box.style.left = '10px';
box.style.right = 'auto';
box.setAttribute('data-side', 'left');
}
box.classList.add('minimized');
} else {
// 展开时
box.style.transition = 'all 0.3s ease';
box.classList.remove('minimized');
const side = box.getAttribute('data-side') || 'right';
if (side === 'right') {
box.style.right = '10px';
box.style.left = 'auto';
} else {
box.style.left = '10px';
box.style.right = 'auto';
}
}
},
passVideo() {
let video = document.getElementsByTagName("video");
if(video.length == 0){
log("当前页面不存在视频,请确保视频已加载");
return;
}
let currentVideo = video[0];
// 移除之前的事件监听器(如果存在)
currentVideo.removeEventListener('ended', this.handleNextVideo);
// 添加新的视频结束监听器
currentVideo.addEventListener('ended', () => {
log("视频播放完成,准备切换到下一个视频...");
this.handleNextVideo();
});
try {
// 设置视频结束前的最后一秒
if (currentVideo.duration && !isNaN(currentVideo.duration)) {
currentVideo.currentTime = currentVideo.duration - 0.1;
currentVideo.playbackRate = 1;
log("正在完成当前视频...");
} else {
log("无法获取视频时长,请等待视频加载完成");
// 等待视频加载完成
currentVideo.addEventListener('loadedmetadata', () => {
currentVideo.currentTime = currentVideo.duration - 0.1;
currentVideo.playbackRate = 1;
log("视频加载完成,正在处理...");
});
}
} catch (error) {
log("处理视频时出错:" + error.message);
}
},
exportExcel(){
// 检查是否有答案数据
if (!this.answerList || this.answerList.length === 0) {
this.$message.error('没有可导出的答案数!请等待答案获取完成。');
log('导出失败:没有答案数据');
return;
}
// 准备数据
const data = this.answerList.map(item => ({
'序号.选项': item.idAndOptions,
'题目': item.question,
'答案': item.answer
}));
// 创建工作簿
const wb = XLSX.utils.book_new();
// 创建工作表
const ws = XLSX.utils.json_to_sheet(data);
// 设置列宽
const colWidths = {
'序号.选项': 10,
'题目': 50,
'答案': 30
};
ws['!cols'] = Object.keys(colWidths).map(key => ({
wch: colWidths[key]
}));
// 将工作表添加到工作簿
XLSX.utils.book_append_sheet(wb, ws, "题库");
// 生成并下载文件
XLSX.writeFile(wb, "题库.xlsx");
log('题库已导出为 Excel 文件');
},
clearLogs() {
// 清空所有现有日志
this.logs = [];
setting.logs = [];
// 添加清除提示
const clearMessage = [
'日志已清除',
'------------------------',
];
// 直接设置新的日志数组,而不是使用 log 函数
this.logs = clearMessage;
setting.logs = [...clearMessage];
// 阻止其他日志添加
setTimeout(() => {
// 确保清除状态保持
if (this.logs.length > clearMessage.length) {
this.logs = [...clearMessage];
setting.logs = [...clearMessage];
}
}, 200);
},
// 优化拖动处理
initDragEvents() {
const box = document.querySelector('.rlBox');
const dragZone = document.querySelector('.ant-card-head');
let startX, startY, initialMouseX, initialMouseY;
dragZone.addEventListener('mousedown', (e) => {
if (this.close) return; // 小化时禁止拖动
e.preventDefault();
this.isDragging = true;
const rect = box.getBoundingClientRect();
startX = rect.left;
startY = rect.top;
initialMouseX = e.clientX;
initialMouseY = e.clientY;
box.style.transition = 'none';
document.body.style.userSelect = 'none';
});
document.addEventListener('mousemove', (e) => {
if (!this.isDragging) return;
const dx = e.clientX - initialMouseX;
const dy = e.clientY - initialMouseY;
let newX = startX + dx;
let newY = startY + dy;
box.style.left = `${newX}px`;
box.style.top = `${newY}px`;
box.style.right = 'auto';
// 实时检查位置
this.checkPosition();
});
document.addEventListener('mouseup', () => {
if (this.isDragging) {
this.isDragging = false;
box.style.transition = 'all 0.2s';
document.body.style.userSelect = '';
}
});
// 添加摸支持
dragZone.addEventListener('touchstart', (e) => {
const touch = e.touches[0];
const rect = box.getBoundingClientRect();
startX = rect.left;
startY = rect.top;
initialMouseX = touch.clientX;
initialMouseY = touch.clientY;
this.isDragging = true;
});
document.addEventListener('touchmove', (e) => {
if (!this.isDragging) return;
e.preventDefault();
const touch = e.touches[0];
const dx = touch.clientX - initialMouseX;
const dy = touch.clientY - initialMouseY;
let newX = startX + dx;
let newY = startY + dy;
const maxX = window.innerWidth - box.offsetWidth;
const maxY = window.innerHeight - box.offsetHeight;
newX = Math.min(Math.max(0, newX), maxX);
newY = Math.min(Math.max(0, newY), maxY);
box.style.left = `${newX}px`;
box.style.top = `${newY}px`;
});
document.addEventListener('touchend', () => {
this.isDragging = false;
});
},
handleNextVideo() {
// 获取所有视频列表项
const videoItems = document.querySelectorAll('li[title*="mp4"]');
if (!videoItems || videoItems.length === 0) {
log("未找到视频列表");
return;
}
// 找到当前激活的视频
const currentVideoItem = Array.from(videoItems).find(item =>
item.classList.contains('tab-active') ||
item.querySelector('.tab-inner.current')
);
if (!currentVideoItem) {
log("无法确定当前视频位置");
return;
}
// 判断是否是最后一个视频
const isLastVideo = currentVideoItem === videoItems[videoItems.length - 1];
if (isLastVideo) {
log("当前已是最后一个视频");
return;
}
// 查找"开始下一条目"按钮
const nextButton = document.querySelector('.view-tip[itemid="1"]');
if (!nextButton) {
log("未找到一个视频按钮");
return;
}
// 点击"开始下一条目"按钮
log("正在切换到下一个视频...");
nextButton.click();
// 等待新视频加载
let attempts = 0;
const maxAttempts = 10;
const checkInterval = setInterval(() => {
const newVideo = document.getElementsByTagName("video")[0];
if (newVideo && newVideo.readyState >= 2) { // 确保视频已经加载足够的数据
clearInterval(checkInterval);
log("新视频加载成功,准备播放");
setTimeout(() => {
this.passVideo();
}, 1500); // 增加延迟以确保视频完全加载
} else {
attempts++;
if (attempts >= maxAttempts) {
clearInterval(checkInterval);
log("新视频加载超时,请手动切换");
}
}
}, 1000);
},
checkPosition() {
const box = document.querySelector('.rlBox');
const rect = box.getBoundingClientRect();
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// 检查并修正水平位置
if (rect.right > windowWidth) {
box.style.right = '0px';
box.style.left = 'auto';
}
if (rect.left < 0) {
box.style.left = '0px';
box.style.right = 'auto';
}
// 检查并修正垂直位置
if (rect.bottom > windowHeight) {
box.style.top = `${windowHeight - rect.height - 10}px`;
}
if (rect.top < 0) {
box.style.top = '10px';
}
},
autoAnswer() {
log("开始自动答题...");
// 检查是否已获取答案
if (!setting.datas || setting.datas.length === 0) {
log("未找到答案信息,请先获取答案");
return;
}
log(`共发现 ${setting.datas.length} 道题目的答案`);
let answeredCount = 0;
// 遍历答案列表
setting.datas.forEach((answerData, index) => {
try {
// 获取当前题目的答案
const answer = answerData.answer;
if (!answer) {
log(`第 ${index + 1} 题答案为空`);
return;
}
// 获取题目容器
const questionContainer = document.querySelector(`.view-test[data-num="${index + 1}"]`) ||
document.querySelector(`.view-test:nth-child(${index + 1})`);
if (!questionContainer) {
log(`第 ${index + 1} 题未找到题目容器`);
return;
}
// 判断题���类型
const isMultiChoice = questionContainer.querySelector('.input-c') !== null;
const isSingleChoice = questionContainer.querySelector('.input-r') !== null;
const isFillBlank = questionContainer.querySelector('.fillblank') !== null;
if (isMultiChoice || isSingleChoice) {
// 处理选择题
const answerLetters = answer.split('\n').map(a => a.trim().charAt(0));
const options = questionContainer.querySelectorAll('.t-option');
options.forEach((option, idx) => {
const letter = String.fromCharCode(65 + idx);
if (answerLetters.includes(letter)) {
// 查找选项的点击元素
const clickTarget = option.querySelector('a[href="javascript:void(0)"]') ||
option.querySelector(isMultiChoice ? '.input-c' : '.input-r');
if (clickTarget) {
// 检查是否已经选中
const isChecked = option.querySelector(isMultiChoice ? '.input-c.selected' : '.input-r.selected');
if (!isChecked) {
clickTarget.click();
log(`第 ${index + 1} 题选择了选项 ${letter}`);
}
}
}
});
answeredCount++;
} else if (isFillBlank) {
// 处理填空题
const fillBlanks = questionContainer.querySelectorAll('.fillblank');
const answers = answer.split('\n').map(a => a.replace(/^.*?\./, '').trim());
// 如果有多个填空框
if (fillBlanks.length > 1) {
fillBlanks.forEach((input, idx) => {
if (answers[idx]) {
input.value = answers[idx];
// 触发必要的事件
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
log(`第 ${index + 1} 题第 ${idx + 1} 个空填写了答案: ${answers[idx]}`);
}
});
} else if (fillBlanks.length === 1) {
// 单个填空框
const input = fillBlanks[0];
input.value = answers[0];
// 触发必要的事件
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
log(`第 ${index + 1} 题填写了答案: ${answers[0]}`);
}
answeredCount++;
}
} catch (error) {
log(`第 ${index + 1} 题自动答题出错: ${error.message}`);
}
});
// 显示完成提示
log(`自动答题完成!成功答题 ${answeredCount} 道题目`);
this.$message.success(`自动答题完成!成功答题 ${answeredCount} 道题目,请检查后手动提交。`);
}
}
});
}
// 初始化获取答案,延迟5秒防止流程崩溃
function initGetAnswer(settings){
var url = location.origin + settings.url;
var data = settings.data.replace(/(testPaperId=).*?(&)/,'$1' + '1250' + '$2');
console.log("=====")
console.log(url,'url')
console.log(data)
getAnswer(url,data);
}
// 自动答题功能
function autoAnswerQuestions() {
log("开始自动答题...");
// 获取所有题目
const questions = document.querySelectorAll('.view-test');
if(!questions || questions.length === 0) {
log("未找到题目,请确认是否在答题页面");
return;
}
log(`共发现 ${questions.length} 道题目`);
questions.forEach((question, index) => {
// 获取题目类型
const isMultiChoice = question.querySelector('.t-option .input-c') !== null; // 多选
const isSingleChoice = question.querySelector('.t-option .input-r') !== null; // 单选
const isFillBlank = question.querySelector('.fill_b .fillblank') !== null; // 填空
// 获取正确答案
const answerElem = question.querySelector('.test-ana[_type="response"]');
if(!answerElem) {
log(`第 ${index + 1} 题未找到答案信息`);
return;
}
const answerText = answerElem.textContent.replace('参考答案:','').trim();
// 根据题型选择答案
if(isMultiChoice) {
// 多选题
const answers = answerText.split(' ');
answers.forEach(answer => {
const options = question.querySelectorAll('.t-option');
options.forEach((option, idx) => {
if(answer === String.fromCharCode(65 + idx)) {
const checkbox = option.querySelector('.input-c');
if(checkbox && !checkbox.classList.contains('selected')) {
checkbox.click();
}
}
});
});
} else if(isSingleChoice) {
// 单选题
const options = question.querySelectorAll('.t-option');
options.forEach((option, idx) => {
if(answerText === String.fromCharCode(65 + idx)) {
const radio = option.querySelector('.input-r');
if(radio && !radio.classList.contains('selected')) {
radio.click();
}
}
});
} else if(isFillBlank) {
// 填空题
const input = question.querySelector('.fill_b .fillblank');
if(input) {
input.value = answerText;
// 触发input事件
const event = new Event('input', { bubbles: true });
input.dispatchEvent(event);
}
}
});
log("自动答题完成!");
}
// 添加自动答题按钮
function addAutoAnswerButton() {
const btnArea = document.querySelector('.practice-action');
if(!btnArea) return;
const btn = document.createElement('a');
btn.innerText = '自动答题';
btn.className = 'btn-public btn-min';
btn.href = 'javascript:void(0)';
btn.onclick = autoAnswerQuestions;
btnArea.appendChild(btn);
log("已添加自动答题按钮");
}
// 初始化
function initAutoAnswer() {
if(location.href.includes('/examSubmit/')) {
log("检测到答题页面");
setTimeout(addAutoAnswerButton, 2000);
}
}
initAutoAnswer();
// 脚本入口
initView();
//监听跳过视频按钮
$('#rl_passVideo').click(function(){passVideo();});
//监听url访问,当访问了加载题目的url时,将获取答案
$(document).ready(function(){
$(document).ajaxComplete(function (evt, request, settings) {
if(settings.url.search('getExamPaper') != -1){
setting.logs.unshift("您已打开作业界面,5秒后将为您获取答案")
setTimeout(initGetAnswer,5000, settings);
}
});
})
// 获取验证时间的函数
function getValidTime() {
try {
const data = localStorage.getItem('scriptValidUntil');
if (!data) return null;
const { time, hash } = JSON.parse(data);
if (!time || !hash) return null;
// 验证哈希值
if (hash !== CryptoJS.SHA256(time.toString()).toString()) {
localStorage.removeItem('scriptValidUntil');
return null;
}
return time;
} catch (e) {
console.error('获取验证时间出错:', e);
localStorage.removeItem('scriptValidUntil');
return null;
}
}