// ==UserScript== // @name 学习通作业导出助手 // @namespace http://tampermonkey.net/ // @version 3.2 // @description 本脚本专为超星学习通(chaoxing.com)作业/考试查看页面设计,旨在将网页上的题目高保真地转化为可编辑的文档 // @author spikeding // @license MIT // @match *://mooc1.chaoxing.com/mooc2/work/view* // @match *://mooc1.chaoxing.com/exam-ans/exam/test/reVersionPaperMarkContentNew* // @match *://mooc1.chaoxing.com/mooc-ans/mooc2/work/* // @grant GM_addStyle // @grant GM_setClipboard // @require https://unpkg.com/docx@7.1.1/build/index.js // @require https://unpkg.com/file-saver@2.0.5/dist/FileSaver.min.js // @require https://unpkg.com/xlsx@0.17.0/dist/xlsx.full.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js // @downloadURL https://update.greasyfork.icu/scripts/559656/%E5%AD%A6%E4%B9%A0%E9%80%9A%E4%BD%9C%E4%B8%9A%E5%AF%BC%E5%87%BA%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/559656/%E5%AD%A6%E4%B9%A0%E9%80%9A%E4%BD%9C%E4%B8%9A%E5%AF%BC%E5%87%BA%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; let parsedData = []; // 1. UI 样式表 GM_addStyle(` #menu-trigger { position: fixed; bottom: 30px; right: 30px; width: 60px; height: 60px; background: #fff; border: 2px solid #ff9a9e; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 8px 24px rgba(255,154,158,0.25); z-index: 10002; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); } #menu-trigger:hover { transform: scale(1.15) rotate(10deg); } #menu-trigger:active { transform: scale(0.9); } #menu-trigger .icon { font-size: 28px; } #export-panel { position: fixed; bottom: 105px; right: 30px; width: 280px; background: #fff; border-radius: 26px; box-shadow: 0 20px 60px rgba(0,0,0,0.12); z-index: 10001; padding: 24px; display: none; border: 1px solid #fdf2f2; transform-origin: bottom right; overflow: hidden; } .panel-show { display: block !important; animation: dropletIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; } @keyframes dropletIn { 0% { transform: scale(0.3) translateY(60px); opacity: 0; } 100% { transform: scale(1) translateY(0); opacity: 1; } } /* 按钮通用样式 */ .btn-stack button { position: relative; overflow: hidden; width: 100%; padding: 12px; border-radius: 14px; cursor: pointer; font-size: 13px; border: 1.5px solid #ffe4e6; background: transparent; color: #ff9a9e; font-weight: 500; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } /* 悬停放大效果 */ .btn-stack button:hover:not(:disabled) { transform: translateY(-2px) scale(1.04); box-shadow: 0 5px 15px rgba(0,0,0,0.08); } /* 按压瞬间反馈 */ .btn-stack button:active:not(:disabled) { transform: scale(0.96); } /* 流光渐变动画 */ @keyframes flow { 0% { background-position: 0% 50%; } 100% { background-position: 100% 50%; } } .word-btn:hover:not(:disabled) { background: linear-gradient(90deg, #2b579a, #4a90e2, #2b579a) !important; background-size: 200% !important; animation: flow 2s infinite linear !important; color: white !important; border-color: transparent !important; } .pdf-btn:hover:not(:disabled) { background: linear-gradient(90deg, #f56c6c, #ff9a9e, #f56c6c) !important; background-size: 200% !important; animation: flow 2s infinite linear !important; color: white !important; border-color: transparent !important; } .xlsx-btn:hover:not(:disabled) { background: linear-gradient(90deg, #217346, #34a853, #217346) !important; background-size: 200% !important; animation: flow 2s infinite linear !important; color: white !important; border-color: transparent !important; } .md-btn:hover:not(:disabled) { background: linear-gradient(90deg, #444, #888, #444) !important; background-size: 200% !important; animation: flow 2s infinite linear !important; color: white !important; border-color: transparent !important; } /* 初始高亮状态 (红色系) */ .highlight-red { background: linear-gradient(135deg, #ff9a9e, #fecfef) !important; color: white !important; border: none !important; } /* 解析成功后的绿色状态 */ .success-green { background: #f0fdf4 !important; color: #22c55e !important; border-color: #22c55e !important; font-weight: bold; } /* 水滴波纹 */ .ripple { position: absolute; background: rgba(255, 255, 255, 0.5); border-radius: 50%; transform: scale(0); animation: rippleEffect 0.6s ease-out; pointer-events: none; } @keyframes rippleEffect { to { transform: scale(4); opacity: 0; } } #log-area { background: #fdf2f2; border-radius: 12px; padding: 10px; font-size: 11px; color: #ff9a9e; margin-bottom: 15px; font-family: monospace; max-height: 60px; overflow-y: auto; border: 1px solid #ffe4e6; } .btn-stack { display: flex; flex-direction: column; gap: 10px; } button:disabled { opacity: 0.3 !important; cursor: not-allowed !important; filter: grayscale(1); transform: none !important; } #pdf-render-area { position: absolute; left: -9999px; width: 800px; background: #fff; padding: 50px; } `); // 水滴反馈函数 function createRipple(event) { const btn = event.currentTarget; const circle = document.createElement("span"); const diameter = Math.max(btn.clientWidth, btn.clientHeight); const radius = diameter / 2; const rect = btn.getBoundingClientRect(); circle.style.width = circle.style.height = `${diameter}px`; circle.style.left = `${event.clientX - rect.left - radius}px`; circle.style.top = `${event.clientY - rect.top - radius}px`; circle.classList.add("ripple"); const oldRipple = btn.getElementsByClassName("ripple")[0]; if (oldRipple) oldRipple.remove(); btn.appendChild(circle); } function addLog(msg) { const logArea = document.getElementById('log-area'); const line = document.createElement('div'); line.innerText = `> ${msg}`; logArea.appendChild(line); logArea.scrollTop = logArea.scrollHeight; } // 核心解析逻辑 function parsePage(e) { createRipple(e); parsedData = []; const pBtn = document.getElementById('p-btn'); const eBtns = document.querySelectorAll('.e-btn'); addLog("正在提取页面题目..."); pBtn.innerText = "正在解析..."; pBtn.disabled = true; const keepAns = document.getElementById('c-ans').checked; const items = document.querySelectorAll('.questionLi'); if (items.length === 0) { addLog("未找到题目,请确认在作业查看页!"); pBtn.disabled = false; pBtn.innerText = "1. 解析本页题目"; return; } items.forEach((el, i) => { const type = el.querySelector('.colorShallow')?.innerText.replace(/[()()]/g, '') || '题型'; let title = el.querySelector('.qtContent')?.innerText.trim() || el.querySelector('.mark_name').innerText.replace(/^\d+\.\s*/, '').trim(); title = title.replace(/\((?:\s*)\)|((?:\s*))/g, '( )'); const options = []; el.querySelectorAll('.mark_letter li').forEach(li => options.push(li.innerText.trim())); const ans = keepAns ? (el.querySelector('.rightAnswerContent')?.innerText.trim() || el.querySelector('.colorGreen')?.innerText.replace('正确答案:', '').trim() || "未记录") : ""; parsedData.push({ id: i + 1, type, title, options, answer: ans }); }); setTimeout(() => { addLog(`提取完毕!共 ${parsedData.length} 道题目`); pBtn.disabled = false; pBtn.innerText = "✅ 解析成功"; // 切换为绿色成功状态 pBtn.classList.remove('highlight-red'); pBtn.classList.add('success-green'); eBtns.forEach(b => b.disabled = false); }, 500); } // 构建 UI const trigger = document.createElement('div'); trigger.id = 'menu-trigger'; trigger.innerHTML = '🐱'; document.body.appendChild(trigger); const panel = document.createElement('div'); panel.id = 'export-panel'; panel.innerHTML = `