// ==UserScript== // @name 中南林一键评教 // @namespace http://tampermonkey.net/ // @version 1.3 // @description 自动完成教学质量平台的学生评教(满分策略,随机一题减1分) // @author spider-go // @match *://*.webvpn.csuft.edu.cn/* // @match *://jxzlpt.csuft.edu.cn/* // @icon https://www.csuft.edu.cn/favicon.ico // @grant none // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; var host = window.location.hostname; if (host.indexOf('jxzlpt') === -1) return; console.log('[一键评教] 已加载'); // ========== 评语库 ========== var COMMENTS = [ '老师授课认真负责,教学效果好', '老师教学内容丰富,讲解清晰透彻', '老师备课充分,课堂互动良好', '老师治学严谨,对学生要求严格但耐心', '老师上课生动有趣,能调动学生积极性', '老师知识渊博,能理论联系实际', '老师讲课重点突出,逻辑性强', '老师认真负责,关心学生学习情况', '老师教学方法灵活,课堂气氛活跃', '老师讲解深入浅出,易于理解', ]; var useRandom = localStorage.getItem('spider_eval_random') !== '0'; // ========== 样式 ========== var style = document.createElement('style'); style.textContent = [ '.spider-eval-btn{position:fixed!important;z-index:99999!important;padding:clamp(8px,2vw,16px) clamp(16px,4vw,32px)!important;', 'background:linear-gradient(135deg,#667eea,#764ba2)!important;color:#fff!important;', 'border:none!important;border-radius:clamp(25px,5vw,50px)!important;font-size:clamp(12px,2.5vw,18px)!important;', 'font-weight:600!important;cursor:pointer!important;', 'box-shadow:0 4px 15px rgba(102,126,234,0.4)!important;', 'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif!important;}', '.spider-eval-btn:hover{box-shadow:0 6px 20px rgba(102,126,234,0.6)!important;}', '.spider-eval-btn:disabled{opacity:.6!important;cursor:not-allowed!important;}', '.spider-eval-box{position:fixed!important;z-index:99999!important;', 'display:inline-flex!important;align-items:center!important;gap:6px!important;', 'background:rgba(248,249,252,.92)!important;backdrop-filter:blur(6px)!important;', 'border-radius:16px!important;padding:6px 10px!important;', 'box-shadow:0 4px 20px rgba(0,0,0,.1)!important;', 'border:1px solid rgba(255,255,255,.6)!important;', 'cursor:grab!important;user-select:none!important;', 'font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif!important;}', '.spider-eval-box.dragging{cursor:grabbing!important;opacity:.85!important;}', '.spider-eval-box .spider-eval-btn{position:relative!important;z-index:auto!important;cursor:pointer!important;}', '.spider-eval-box .spider-eval-tg{position:relative!important;z-index:auto!important;bottom:auto!important;right:auto!important;}', '.spider-eval-tg{position:fixed!important;z-index:99999!important;display:flex!important;', 'align-items:center!important;gap:6px!important;font-size:12px!important;color:#666!important;', 'cursor:pointer!important;user-select:none!important;background:rgba(255,255,255,.95)!important;', 'padding:4px 10px!important;border-radius:20px!important;', 'box-shadow:0 2px 8px rgba(0,0,0,.1)!important;font-family:inherit!important;}', '.spider-eval-tg input{display:none!important;}', '.spider-eval-tg .sl{width:32px!important;height:18px!important;background:#ccc!important;', 'border-radius:9px!important;position:relative!important;transition:background .3s!important;flex-shrink:0!important;}', '.spider-eval-tg .sl::after{content:""!important;width:14px!important;height:14px!important;', 'background:#fff!important;border-radius:50%!important;position:absolute!important;', 'top:2px!important;left:2px!important;transition:transform .3s!important;}', '.spider-eval-tg input:checked+.sl{background:#667eea!important;}', '.spider-eval-tg input:checked+.sl::after{transform:translateX(14px)!important;}', '.spider-eval-lg{position:fixed!important;z-index:99998!important;width:400px!important;', 'max-height:250px!important;overflow-y:auto!important;background:rgba(0,0,0,.85)!important;', 'color:#fff!important;border-radius:12px!important;padding:10px 14px!important;', 'font-size:13px!important;font-family:Consolas,monospace!important;line-height:1.6!important;', 'display:none!important;box-shadow:0 4px 20px rgba(0,0,0,.3)!important;}', '.spider-eval-lg .ok{color:#52c41a;}.spider-eval-lg .er{color:#ff4d4f;}', '.spider-eval-lg .in{color:#1890ff;}.spider-eval-lg .wa{color:#faad14;}', ].join(''); document.head.appendChild(style); var BASE = window.location.origin; var btn, lg; function log(msg, t) { if (!lg) return; var d = document.createElement('div'); d.className = t; d.textContent = '[' + new Date().toLocaleTimeString() + '] ' + msg; lg.appendChild(d); lg.scrollTop = lg.scrollHeight; } function gt() { var m = document.cookie.match(/(?:^|;\s*)token=([^;]*)/); return m ? m[1] : null; } async function ap(p, b) { var h = { 'Accept': 'application/json, text/plain, */*' }; var t = gt(); if (t) h['Authorization'] = 'Bearer' + t; if (b && typeof b === 'string') h['Content-Type'] = 'application/json;charset=UTF-8'; var r = await fetch(BASE + p, { method: 'POST', headers: h, credentials: 'include', body: b }); return await r.json(); } async function getTasks() { var d = await ap('/api/xspj/xspj/getXspjtask'); if (d.code === 200 && d.data && d.data.pageData) return d.data.pageData; throw new Error((d && d.message) || '获取任务失败'); } async function getCourses(id) { var d = await ap('/api/xspj/xspj/getXspjStudentCourses?taskid=' + id); if (d.code === 200 && d.data && d.data.pageData) return d.data.pageData; throw new Error((d && d.message) || '获取课程失败'); } async function getQuestions(iid, ct) { var d = await ap('/api/xspj/xspj/getXspjTindexSystem?indexid=' + iid + '&pjcoursetype=' + encodeURIComponent(ct)); if (d.code === 200 && d.data && d.data.pageData) return d.data.pageData; throw new Error((d && d.message) || '获取题目失败'); } async function sub(e) { var d = await ap('/api/xspj/xspj/saveStudentComment', JSON.stringify(e)); if (d.code === 200) return true; throw new Error((d && d.message) || '提交失败'); } function gen(qs) { var ans = [], ts = 0, si = []; qs.forEach(function(q, i) { var a = { index_order: q.ordor, sfbt: q.isemptyed || '否', yjzb: q.firstlevlindex, index_type: q.type, indexid: q.indexid }; if (q.type === '打分题' && q.isscored === '是') { var s = Math.round(q.score || 10); a.index_score = String(s); a.index_title = String(s); ts += s; si.push(i); } else if (q.type === '问答题') { a.index_score = '0'; a.index_title = (q.isemptyed === '否' && useRandom) ? COMMENTS[Math.floor(Math.random() * COMMENTS.length)] : ''; } ans.push(a); }); if (si.length > 0) { var idx = si[Math.floor(Math.random() * si.length)]; var d = Math.max(0, Math.round(qs[idx].score) - 1); ans[idx].index_score = String(d); ans[idx].index_title = String(d); ts = ts - Math.round(qs[idx].score) + d; } return { answers: ans, totalScore: ts }; } async function start() { if (!btn || !lg) return; btn.disabled = true; btn.textContent = '⏳ 评教中...'; lg.innerHTML = ''; lg.style.display = 'block'; try { log('获取评教任务...', 'in'); var tasks = await getTasks(); log('找到 ' + tasks.length + ' 个任务', 'in'); var ok = 0, fail = 0, skip = 0; for (var t = 0; t < tasks.length; t++) { var task = tasks[t]; if (task.currentStatus !== '进行中') { log('任务已结束,跳过', 'wa'); continue; } log('任务:' + task.taskname, 'in'); var courses = await getCourses(task.taskid); log('该任务 ' + courses.length + ' 门课', 'in'); for (var c = 0; c < courses.length; c++) { var co = courses[c]; var key = co.coursename + ' - ' + co.teachername; if (co.hassubmit === 1) { log('⏭️ 已评:' + key, 'wa'); skip++; continue; } log('评教:' + key, 'in'); try { var qs = await getQuestions(task.indexid, co.pjcoursetype); if (!qs || qs.length === 0) { log('❌ ' + key + ' 未开放', 'er'); fail++; continue; } var r = gen(qs); var now = new Date(); var ct = now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0') + '-' + String(now.getDate()).padStart(2,'0') + ' ' + String(now.getHours()).padStart(2,'0') + ':' + String(now.getMinutes()).padStart(2,'0') + ':' + String(now.getSeconds()).padStart(2,'0'); await sub([{ taskid: task.taskid, classno: co.classno, coursecode: co.coursecode, coursename: co.coursename, jobnumber: co.jobnumber, studentid: co.studentid, studentname: co.studentname, teachername: co.teachername, yearterm: co.yearterm, totalscore: r.totalScore, pjcoursetype: co.pjcoursetype, courseorgcode: co.courseorgcode, courseorgname: co.courseorgname, evaluateResult: r.answers, commit_time: ct, }]); log('✅ ' + key + '(' + r.totalScore + '分)', 'ok'); ok++; await new Promise(function(r2) { setTimeout(r2, 500); }); } catch (e) { log('❌ ' + key + ':' + e.message, 'er'); fail++; } } } log('==============', 'in'); if (ok === 0 && fail === 0 && skip > 0) { log('全部已评教完毕 ✅', 'ok'); btn.textContent = '✅ 全部已评'; } else { log('成功 ' + ok + ',失败 ' + fail + ',跳过 ' + skip, ok > 0 ? 'ok' : 'wa'); btn.textContent = '✅ 完成'; setTimeout(function() { window.location.reload(); }, 1500); } } catch (e) { log('❌ 错误:' + e.message, 'er'); btn.textContent = '🤖 一键评教'; } btn.disabled = false; } // ========== 拖动 ========== function makeDraggable(el) { var x0, y0, l0, t0, dragging = false; function isInteractive(t) { while (t && t !== el) { if (t.tagName === 'BUTTON' || t.tagName === 'INPUT') return true; t = t.parentNode; } return false; } function onStart(e) { if (btn.disabled) return; if (isInteractive(e.target)) return; var ev = e.touches ? e.touches[0] : e; x0 = ev.clientX; y0 = ev.clientY; l0 = el.offsetLeft; t0 = el.offsetTop; el.classList.add('dragging'); dragging = true; e.preventDefault(); } function onMove(e) { if (!dragging) return; var ev = e.touches ? e.touches[0] : e; el.style.left = (l0 + ev.clientX - x0) + 'px'; el.style.top = (t0 + ev.clientY - y0) + 'px'; el.style.right = 'auto'; el.style.bottom = 'auto'; e.preventDefault(); } function onEnd() { if (!dragging) return; dragging = false; el.classList.remove('dragging'); } el.addEventListener('mousedown', onStart); document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onEnd); el.addEventListener('touchstart', onStart, { passive: false }); document.addEventListener('touchmove', onMove, { passive: false }); document.addEventListener('touchend', onEnd); } // ========== 注入 ========== function inject() { if (document.getElementById('spider-box')) return; var box = document.createElement('div'); box.id = 'spider-box'; box.className = 'spider-eval-box'; box.style.bottom = '20px'; box.style.right = '20px'; btn = document.createElement('button'); btn.id = 'spider-btn'; btn.className = 'spider-eval-btn'; btn.textContent = '🤖 一键评教'; btn.onclick = start; box.appendChild(btn); var tg = document.createElement('label'); tg.className = 'spider-eval-tg'; tg.innerHTML = '随机评语'; tg.addEventListener('click', function(e) { e.preventDefault(); var cb = tg.querySelector('input'); cb.checked = !cb.checked; useRandom = cb.checked; localStorage.setItem('spider_eval_random', useRandom ? '1' : '0'); }); box.appendChild(tg); document.body.appendChild(box); makeDraggable(box); lg = document.createElement('div'); lg.id = 'spider-lg'; lg.className = 'spider-eval-lg'; lg.style.bottom = 'calc(20px + 66px)'; lg.style.right = '20px'; lg.onclick = function() { lg.style.display = lg.style.display === 'none' ? 'block' : 'none'; }; document.body.appendChild(lg); log('🟢 就绪,点击按钮评教', 'in'); log('💡 可拖动容器到任意位置', 'in'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', inject); } else { inject(); } var iv = setInterval(function() { if (document.body && !document.getElementById('spider-box')) inject(); if (document.getElementById('spider-box')) clearInterval(iv); }, 2000); setTimeout(function() { clearInterval(iv); }, 10000); })();