您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
查重流程:1. 在练习箱页面“保存试卷”; 2. 在试卷编辑页面“重题审核”
当前为
// ==UserScript== // @name quizii组卷查重工具——试卷管理 // @namespace http://jz.quizii.com/ // @version 0.4 // @description 查重流程:1. 在练习箱页面“保存试卷”; 2. 在试卷编辑页面“重题审核” // @author JinJunwei // @match http://jz.quizii.com/*/papers/mine* // @grant none // ==/UserScript== 'use strict'; // 悬浮按钮的容器 let referenceElement=function (referenceElement){ let newElement = document.createElement('div'); newElement.innerHTML = '<div class="myInjectContainer" ></div>'; newElement = newElement.firstChild // 定位 newElement.style.top = referenceElement.offsetTop+ referenceElement.offsetHeight+"px"; // 插入元素 referenceElement.parentElement.insertBefore(newElement, referenceElement.nextElementSibling); return newElement; }(document.querySelector('div#feedback_btn')); // 悬浮按钮,接受试卷 (function() { let newElement = document.createElement('div'); newElement.innerHTML = '<div class="tampermonker-like-feedback_btn" title="油猴脚本,接受全部试卷">接受试卷</div>'; newElement = newElement.firstChild referenceElement.appendChild(newElement); // 插入到页面 // 功能脚本 newElement.onclick = function(){ //全部接收 document.querySelectorAll("#sharing_list a:nth-child(1)").forEach(el=>el.click()); }; })(); // 第2个悬浮按钮,保存试卷 (function() { let newElement = document.createElement('div'); newElement.innerHTML = '<div class="tampermonker-like-feedback_btn" title="油猴脚本,导出当前页的全部或选中试卷到本地浏览器数据库,用于组卷排重">保存试卷</div>'; newElement = newElement.firstChild referenceElement.appendChild(newElement); // 插入到页面 // 功能脚本 newElement.onclick = async function(){ // 导出当前页的全部或选中试卷到本地浏览器数据库 // 选中的试卷id let papers = [...document.querySelectorAll("#paper_wrap tr.paper")].filter(el=>el.querySelector("input.paper_selected").checked); // 如果没有选中,则清空 if(papers.length===0){ if(confirm("是否清空保存到本地浏览器数据库的试卷习题内容")){ indexedDB.deleteDatabase("quizii"); } return; } // paper ids for(var paper of papers){ const href = paper.querySelector("a.paper_name").href; console.log("load iframe of:"+ href); let iframeElement = openPaperInIframe(href, paper); await waitRemoved(iframeElement); }; }; function openPaperInIframe(url,el){ const ifrm = document.createElement('iframe'); ifrm.setAttribute('width', el.clientWidth); //document.body.appendChild(ifrm); // to place at end of document el.parentNode.insertBefore(ifrm, el.nextElementSibling); // assign url ifrm.setAttribute('src',url); // 提取保存习题,并关闭iframe; const extractAndClose = ()=>extractAndSavePaper(ifrm.contentDocument,ifrm.contentWindow, ()=>ifrm.parentElement.removeChild(ifrm)); // 自动开始下载 ifrm.onload = ()=> ifrm.contentWindow.MathJax.Hub.Queue(extractAndClose); return ifrm; } function extractAndSavePaper(iframeDoc, iframeWin, callback){ console.log("开始处理页面,。。。"); //const paperTitle = iframeDoc.querySelector("div.paper_header #title").innerText; const g_paper = iframeWin.g_paper; const paperStoreName = g_paper.name+"@"+g_paper._id; // 提取所有题目 // ct -> content; sn -> Serial Number let exerciseList = [...iframeDoc.querySelectorAll("div.item")] .map(qItem=>{return{ "id": qItem.dataset.id, "ct":qItem.querySelector("q>ol>li").innerText.replace(/\s+/g,""), "pId":g_paper._id, "sn":qItem.querySelector("li").value, "pName":g_paper.name, "html":qItem.firstElementChild.outerHTML, };}); openAndResetDB(iframeWin.indexedDB, paperStoreName) .onsuccess = (event)=>batchPutOrAdd(event.target.result, paperStoreName, exerciseList, callback); } // openAndResetDB(storeName).onsuccess = (event)=>? function openAndResetDB(indexedDB, paperStoreName){ //每次open都触发onupgradeneeded const openRequest = indexedDB.open("quizii", Date.now()); openRequest.onerror = function(event) { console.error(event); }; openRequest.onupgradeneeded = function(event) { // 新建数据库 const db = event.target.result; const paperId = paperStoreName.substring(paperStoreName.lastIndexOf("@")); const oldStores = [...db.objectStoreNames].filter(n=>n.endsWith(paperId)) // 先删除 if(oldStores.length>0){ console.log("清空了数据表:"+oldStores.join("; ")); oldStores.forEach(n=>db.deleteObjectStore(n)) } // 再新建 db.createObjectStore(paperStoreName, { keyPath: 'id' }); console.log("新建了数据表:"+paperStoreName); }; return openRequest; } function batchPutOrAdd(db, paperStoreName, items, callback){ const transaction = db.transaction(paperStoreName, "readwrite"); const itemStore = transaction.objectStore(paperStoreName); putNext(0); if(callback){transaction.oncomplete = callback;} function putNext(i) { if (i<items.length) { itemStore.put(items[i]).onsuccess = ()=>putNext(i+1); } else { // complete console.log('重置或新建了'+items.length+"条数据到表:"+paperStoreName); } } } function saveText(title, content){ const link = document.createElement('a'); link.download = (title + '.txt').replace(/[/\\?%*:|"<>\s]/g, '-'); const blob = new Blob([content], { type: 'text/plain' }); link.href = window.URL.createObjectURL(blob); link.click(); } async function waitLoad(iframeDoc){ console.log("正在等待载入试卷。。。"); // 等待载入 while(iframeDoc.readyState !== 'complete') { // console.log(document.querySelector(selector).innerHTML); await new Promise(r => setTimeout(r, 500)); } } async function waitRemoved(iframeElement){ console.log("正在等待载入试卷。。。"); // 等待移除 while(iframeElement.parentElement) { // console.log(document.querySelector(selector).innerHTML); await new Promise(r => setTimeout(r, 500)); } } async function sleep(seconds){ // sleep 10s console.log("等待"+seconds+"s"); await new Promise(r => setTimeout(r, seconds*1000)); } })(); // 第3个悬浮按钮,生成试卷 (function() { let newElement = document.createElement('div'); newElement.innerHTML = '<div class="tampermonker-like-feedback_btn" title="油猴脚本,批量生成试卷">生成试卷</div>'; newElement = newElement.firstChild referenceElement.appendChild(newElement); // 插入到页面 // 功能脚本 newElement.onclick = function(){ // 批量生成试卷 let paperNameAndIdsList = prompt("请输入:试卷名1,id,...id;试卷名2,id,...id;", ""); paperNameAndIdsList = paperNameAndIdsList .split(" ;") .map(s=>s.trim().split(" , ")).filter(a=>a.length>1); batchGeneratePaper(paperNameAndIdsList); }; // 生成试卷api function customGeneratePaper(callback,paper_name, ...idList){ let item_list = idList.map(id=>{return {type:1001,subtype:1,difficulty:0,_id:id,"sources":[]}}).map(o=>JSON.stringify(o)); let teaching_basic = {grade:3,textbook_ver:"",grade_cat: 0}; qishi.http.post("/paper/generate", {item_list:item_list,teaching_basic:JSON.stringify(teaching_basic),paper_name:paper_name}, function(data) { var id=data.tid; if(id){ console.log("成功生成试卷:"+qishi.util.make_url('/paper/'+id+'/edit')) callback(); }else{ console.log("试卷生成出错了:"+paper_name); } } ); } function batchGeneratePaper(paperNameAndIdsList){ // 递归 if (paperNameAndIdsList.length===0){alert("生成试卷完成。");return;} customGeneratePaper( ()=>setTimeout(()=>batchGeneratePaper(paperNameAndIdsList),5000), ...paperNameAndIdsList.shift() ); } })(); // 第4个悬浮按钮,导出试卷 (function() { let newElement = document.createElement('div'); newElement.innerHTML = '<div class="tampermonker-like-feedback_btn" title="油猴脚本,导出保存的试卷习题id">导出试卷</div>'; newElement = newElement.firstChild referenceElement.appendChild(newElement); // 插入到页面 // 功能脚本 newElement.onclick = async function(){ // 统一输出 试卷名和习题id,用于导入excel console.log("开始统一输出 试卷名和习题id,用于导入excel"); const result=[] // 文件名 默认为 标签名,否则按时间命名 const tagEl=document.querySelector("#select_tag > option[selected]"); const filename = tagEl?tagEl.innerText:getDateFormat(); readNameAndIdsFromDB((...arr)=>result.push(arr), ()=>download(filename+".txt", result.map(arr=>arr.join("\t")).join("\n")) ); }; function readNameAndIdsFromDB(callback, callback2){ const openRequest = indexedDB.open("quizii"); openRequest.onerror = function(event) { console.error(event); }; openRequest.onsuccess = (event)=>{ const db = event.target.result; const dbNames = [...db.objectStoreNames]; const transaction = db.transaction(dbNames, "readonly"); dbNames.forEach(paperStoreName=>{ const itemStore = transaction.objectStore(paperStoreName); itemStore.getAllKeys().onsuccess =(event)=>{ callback(...paperStoreName.split("@").reverse(),...event.target.result) }; }); transaction.objectStore(dbNames[0]) .getAllKeys() .onsuccess =callback2; } } function download(filename, text) { var element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); } function getDateFormat(){ const d = new Date; return [d.getFullYear(), d.getMonth()+1,d.getDate(), d.getHours(),d.getMinutes(),d.getSeconds()].join('-'); } })(); // 设置样式 (function (){ var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = `.myInjectContainer{ position: fixed; top: 150px; right: -1px; width: 43px; } .tampermonker-like-feedback_btn{ width: 24px; padding:9px; border:1px solid #ccc; border-top-left-radius: 4px; border-bottom-left-radius: 4px; background-color: #fff; line-height: 16px; font-size: 12px; color: #666; cursor: pointer; z-index: 9999; } .tampermonker-like-feedback_btn:hover{ border-color: #1bbc9b; background-position: center -48px; background-color: #1bbc9b; color: #fff; }`; document.getElementsByTagName('head')[0].appendChild(style); })(); // 悬浮按钮,导入已审 (function() { let newElement = document.createElement('div'); newElement.innerHTML = '<div class="tampermonker-like-feedback_btn" title="油猴脚本,导入已审核的习题-试卷列表">导入已审</div>'; newElement = newElement.firstChild referenceElement.appendChild(newElement); // 插入到页面 // 功能脚本 newElement.onclick = function(){ // 保存到indexedDB本地数据库 batchSaveToIndexedDB(getEIdPIdsList()); }; function batchSaveToIndexedDB(items){ const objectStoreName="已审核习题";// 数据表名 const openRequest = indexedDB.open("quizii_已审核"); openRequest.onerror = function(event) {console.error(event);}; // 打开数据库失败 // 新建数据表 openRequest.onupgradeneeded = function(event) { // 新建数据库 const db = event.target.result; if (db.objectStoreNames.contains(objectStoreName)) { // 先删除 db.deleteObjectStore(objectStoreName); console.log("删除了数据表:"+objectStoreName); } // 再新建 db.createObjectStore(objectStoreName, { keyPath: 'id' }); console.log("新建了数据表:"+objectStoreName); }; openRequest.onsuccess = function(event){ // 批量保存数据 const db = event.target.result; const transaction = db.transaction(objectStoreName, "readwrite"); const itemStore = transaction.objectStore(objectStoreName); putNext(0); function putNext(i) { if (i<items.length) { itemStore.put(items[i]).onsuccess = ()=>putNext(i+1); } else { console.log('重置或新建了'+items.length+"条数据到表:"+objectStoreName); alert('保存了 '+items.length+" 道已审核的习题id到浏览器的本地数据库"); } } } } function getEIdPIdsList(idsText){ idsText=idsText?idsText:getDefaultIdsText(); const m = new Map(); // "eID: sn@pName@pId;..." idsText.split("\n").forEach(p=>{ const ids = p.split("\t"); const pId = ids[1]+"@"+ids[0]; for(let i=3;i<ids.length;i++){ const eId = ids[i]; if(m.has(eId)){ m.set(eId,m.get(eId)+";"+i+"@"+pId); }else{ m.set(eId,i+"@"+pId); } } }); return [...m.entries()].map(arr=>{return{id:arr[0],pIds:arr[1]}});// map -> list } function getDefaultIdsText(){ // 手动添加习题id列表 return `5da19ee0eec34760214d3040 55210301 56c527fb5417d10805017fe9 56d8f5dd5417d156e0347dd8 56f299065417d140a1f1b099 57109d37def2970a166517ca 572fe521def2977544be3cd1 5b766c1bf8a01b7942bac247 5da19ee0eec347601c5bc0c4 55210302 56c527fb5417d10805017ff9 56f29f755417d140a1f1b0de 570e64315417d147c179e01d 570e65d45417d147c00701b6 5733df95def297635ff91ae8 `; } })();