// ==UserScript== // @name 粉笔折叠查看 v2 // @namespace http://tampermonkey.net/ // @version 2.0.0 // @description 点击题号折叠/展开题干与统计区,点击「解析」折叠/展开解析;悬浮题号显示正确答案与全站正确率;题号按正确率着色 // @author You // @match *://spa.fenbi.com/* // @match *://www.fenbi.com/* // @grant none // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const TIP_ID = 'fenbi-fold-tip'; const BTN_ID = 'fenbi-fold-toggle-btn'; const VERIFY_LOW = 25; // <=25% 紫色 const RED_LIMIT = 80; // <80% 红色 // 每题中"内容区"的几块(题干 / 选项 / 统计信息),点题号统一显示或隐藏 const SEL_BODY = ['app-question-choice', 'app-choice-radio', 'app-solution-overall']; /* ---------- 题目集合 ---------- */ function getItems() { let items = Array.from(document.querySelectorAll('app-ti.question-multiple')); if (!items.length) items = Array.from(document.querySelectorAll('app-ti')); if (!items.length) items = Array.from(document.querySelectorAll('.ti-container')); return items; } /* ---------- 数据读取(按标题文本定位,不依赖通用类名) ---------- */ function readOverall(item, key, cls) { const boxes = item.querySelectorAll('.overall-item'); for (let i = 0; i < boxes.length; i++) { const t = boxes[i].querySelector('.overall-item-title'); if (!t || t.textContent.indexOf(key) === -1) continue; const v = boxes[i].querySelector(cls); if (!v) continue; return v.textContent.trim(); } return ''; } function getRate(item) { let t = readOverall(item, '全站正确率', '.correct-rate').replace(/\s+/g, ''); if (t && !/%$/.test(t)) t += '%'; return t || '无数据'; } function getAnswer(item) { return readOverall(item, '正确答案', '.correct-answer') || '无数据'; } function toNum(s) { const m = String(s || '').match(/\d+(\.\d+)?/); return m ? parseFloat(m[0]) : NaN; } /* ---------- 悬浮提示 ---------- */ function removeTip() { const t = document.getElementById(TIP_ID); if (t) t.remove(); } function showTip(anchor, rate, answer) { removeTip(); const tip = document.createElement('div'); tip.id = TIP_ID; tip.style.cssText = 'position:fixed;z-index:2147483647;background:rgba(30,30,30,.96);' + 'color:#fff;font-size:13px;line-height:1.8;padding:8px 12px;border-radius:6px;' + 'font-family:Menlo,Consolas,monospace;white-space:nowrap;pointer-events:none;' + 'box-shadow:0 4px 16px rgba(0,0,0,.25);'; const n = toNum(rate); const l1 = document.createElement('div'); l1.textContent = '全站正确率:' + rate; l1.style.fontWeight = 'bold'; if (!isNaN(n) && n <= VERIFY_LOW) l1.style.color = '#d8a0ff'; else if (!isNaN(n) && n < RED_LIMIT) l1.style.color = '#ff8080'; tip.appendChild(l1); const l2 = document.createElement('div'); l2.textContent = '正确答案:' + answer; tip.appendChild(l2); document.body.appendChild(tip); const r = anchor.getBoundingClientRect(); const tw = tip.offsetWidth; const th = tip.offsetHeight; let left = r.left; let top = r.bottom + 6; if (left + tw > window.innerWidth - 8) left = window.innerWidth - tw - 8; if (top + th > window.innerHeight - 8) top = r.top - th - 6; tip.style.left = Math.max(8, left) + 'px'; tip.style.top = Math.max(8, top) + 'px'; } /* ---------- 显隐控制(只改 display,不动 DOM 结构) ---------- */ function bodyEls(item) { return SEL_BODY.map(function (s) { return item.querySelector(s); }).filter(Boolean); } function resEl(item) { return item.querySelector('app-result-common'); } function isBodyOpen(item) { const els = bodyEls(item); if (!els.length) return true; return els[0].style.display !== 'none'; } function isResOpen(item) { const el = resEl(item); return el ? el.style.display !== 'none' : true; } function setBody(item, open) { bodyEls(item).forEach(function (el) { el.style.display = open ? '' : 'none'; }); } function setRes(item, open) { const el = resEl(item); if (el) el.style.display = open ? '' : 'none'; if (item.__fenbiResBtn) { item.__fenbiResBtn.textContent = open ? '解析 ▾' : '解析 ▸'; } } /* ---------- 单题处理 ---------- */ function processItem(item) { if (item.__fenbiFoldDone) return; const noEl = item.querySelector('.title-index'); if (!noEl) return; // 题干区还没渲染出来时先跳过,等下一轮轮询 if (!item.querySelector('.solution-choice-container')) return; item.__fenbiFoldDone = true; const rate = getRate(item); const answer = getAnswer(item); const n = toNum(rate); // 题号着色 if (!isNaN(n) && n <= VERIFY_LOW) { noEl.style.color = '#e5e500'; // ≤25% → 黄色 noEl.style.fontWeight = 'bold'; } else if (!isNaN(n) && n < RED_LIMIT) { noEl.style.color = '#d93025'; noEl.style.fontWeight = 'bold'; } // 点题号:展开/收起题干、选项、统计信息 noEl.style.cursor = 'pointer'; noEl.addEventListener('click', function () { setBody(item, !isBodyOpen(item)); }); // 悬浮题号:显示正确率与正确答案 noEl.addEventListener('mouseenter', function () { showTip(noEl, rate, answer); }); noEl.addEventListener('mouseleave', removeTip); // 题号右侧的「解析」开关(独立于题号点击) const resBtn = document.createElement('span'); resBtn.textContent = isResOpen(item) ? '解析 ▾' : '解析 ▸'; resBtn.style.cssText = 'margin-left:10px;font-size:12px;color:#0066cc;' + 'cursor:pointer;user-select:none;'; resBtn.addEventListener('click', function (e) { e.stopPropagation(); setRes(item, !isResOpen(item)); }); noEl.insertAdjacentElement('afterend', resBtn); item.__fenbiResBtn = resBtn; } /* ---------- 全局按钮:收起所有 / 展开所有 ---------- */ function applyAll(open) { getItems().forEach(function (item) { processItem(item); setBody(item, open); setRes(item, open); }); } function createToggleBtn() { if (document.getElementById(BTN_ID)) return; const btn = document.createElement('button'); btn.id = BTN_ID; btn.textContent = '收起所有'; btn.style.cssText = 'position:fixed;top:10px;right:10px;z-index:2147483647;' + 'padding:8px 14px;font-size:13px;cursor:pointer;background:#f5f5f5;' + 'border:1px solid #ccc;border-radius:4px;'; btn.addEventListener('click', function () { const anyOpen = getItems().some(function (item) { return isBodyOpen(item) || isResOpen(item); }); // 只要还有展开的,就全部收起;全部收起了,再点就全部展开 const target = !anyOpen; applyAll(target); btn.textContent = target ? '收起所有' : '展开所有'; }); document.body.appendChild(btn); } /* ---------- 初始化(SPA 轮询兜底) ---------- */ function init() { createToggleBtn(); getItems().forEach(processItem); } init(); setTimeout(init, 1200); setTimeout(init, 3000); setInterval(function () { getItems().forEach(processItem); }, 3000); })();