// ==UserScript== // @name 粉笔题库 - 试卷跳转面板 // @namespace http://tampermonkey.net/ // @version 1.0.5 // @description 在粉笔行测题库页右上角悬浮展示年份/季/类别面板,季选项直接标注当前类别完成情况(依据接口 exercise.finishCount 真实完成次数),选择后跳转到对应试卷 // @author Marvis Browser Agent // @match *://www.fenbi.com/spa/tiku/guide/realTest/xingce/xingce* // @match *://www.fenbi.com/spa/tiku/guide/realTest/shenlun/shenlun* // @match *://www.fenbi.com/spa/tiku/guide/realTest/shuaxin/shuaxin* // @run-at document-end // @grant none // ==/UserScript== (function() { 'use strict'; var CATS = ['地市级', '行政执法类', '副省级']; var CATID = '1000371'; var LIST_URL = 'https://tiku.fenbi.com/api/xingce/papers/v2?labelId=2972&toPage='; var EXER_URL = 'https://tiku.fenbi.com/api/xingce/exercises?app=web&kav=131&av=134&hav=128&version=3.0.0.0&deviceId=&gav=2&apcId=0&examcatid=' + CATID; var DEF_Y = '2027', DEF_S = '三十六', DEF_C = '行政执法类'; var css = '#fb-jump-panel{position:fixed;top:16px;right:16px;z-index:2147483647;width:300px;background:#fff;border:1px solid #e3e6eb;border-radius:12px;box-shadow:0 8px 28px rgba(0,0,0,.16);font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;font-size:13px;color:#1f2329;overflow:hidden}' + '#fb-jump-panel .fb-hd{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:#1f7cf6;color:#fff;font-size:14px;font-weight:600;user-select:none}' + '#fb-jump-panel .fb-min{cursor:pointer;border:none;background:rgba(255,255,255,.22);color:#fff;border-radius:6px;padding:2px 8px;font-size:12px;line-height:18px}' + '#fb-jump-panel .fb-bd{padding:12px 14px 14px}' + '#fb-jump-panel .fb-row{display:flex;align-items:center;margin-bottom:10px}' + '#fb-jump-panel .fb-lb{width:52px;color:#646a73;flex:0 0 auto}' + '#fb-jump-panel select{flex:1 1 auto;height:32px;border:1px solid #dcdfe6;border-radius:8px;padding:0 8px;background:#fff;font-size:13px;color:#1f2329;outline:none;cursor:pointer}' + '#fb-jump-panel select:focus{border-color:#1f7cf6}' + '#fb-jump-panel .fb-btn{width:100%;height:36px;border:none;border-radius:8px;background:#1f7cf6;color:#fff;font-size:14px;font-weight:600;cursor:pointer;margin-top:2px}' + '#fb-jump-panel .fb-btn:hover{background:#3d8ef7}' + '#fb-jump-panel .fb-btn:disabled{background:#c9cdd4;cursor:not-allowed}' + '#fb-jump-panel .fb-st{margin-top:9px;font-size:12px;color:#8a9099;min-height:16px;line-height:16px;word-break:break-all}' + '#fb-jump-panel.fb-collapsed .fb-bd{display:none}'; var st = document.createElement('style'); st.textContent = css; document.head.appendChild(st); var box = document.createElement('div'); box.id = 'fb-jump-panel'; box.innerHTML = '
粉笔试卷跳转
' + '
' + '
年份
' + '
第几季
' + '
类别
' + '' + '
正在加载试卷数据...
' + '
'; document.body.appendChild(box); var yearSel = box.querySelector('#fb-year'); var seasonSel = box.querySelector('#fb-season'); var catSel = box.querySelector('#fb-cat'); var goBtn = box.querySelector('#fb-go'); var statusEl = box.querySelector('#fb-status'); function setStatus(t){ statusEl.textContent = t; } box.querySelector('#fb-min').onclick = function(){ box.className = box.className.indexOf('fb-collapsed') >= 0 ? '' : 'fb-collapsed'; this.textContent = box.className.indexOf('fb-collapsed') >= 0 ? '展开' : '收起'; }; // 中文数字转换(1~99) var CN = {'一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9}; var CN_D = ['零','一','二','三','四','五','六','七','八','九']; function cn2n(s){ if (!s) return 0; if (s.indexOf('十') >= 0){ var p = s.split('十'); var t = p[0] ? (CN[p[0]] || 0) : 1; var o = p[1] ? (CN[p[1]] || 0) : 0; return t * 10 + o; } return CN[s] || 0; } function n2cn(n){ if (n <= 0) return '零'; if (n < 10) return CN_D[n]; if (n < 20) return '十' + (n % 10 ? CN_D[n % 10] : ''); var t = Math.floor(n / 10), o = n % 10; return CN_D[t] + '十' + (o ? CN_D[o] : ''); } function doneLabel(done){ if (done <= 0) return '未完成'; return '完成' + n2cn(done) + '次'; } var index = {}; var years = []; function build(all){ for (var i=0;i 0) done += fc; } return done; } function seasonText(season){ return season === '终极' ? '终极模考' : ('第' + season + '季'); } function fillSeasons(){ var y = yearSel.value; var c = catSel.value; var prev = seasonSel.value; var seasons = index[y] ? Object.keys(index[y]) : []; seasons.sort(function(a,b){ if (a === '终极') return 1; if (b === '终极') return -1; return cn2n(b) - cn2n(a); }); seasonSel.innerHTML = ''; for (var i=0;i= 0) ? countDone(y, s, c) : 0; var o = document.createElement('option'); o.value = s; o.textContent = seasonText(s) + '(' + doneLabel(done) + ')'; seasonSel.appendChild(o); } if (prev && seasons.indexOf(prev) >= 0) seasonSel.value = prev; else if (seasons.indexOf(DEF_S) >= 0) seasonSel.value = DEF_S; } yearSel.onchange = function(){ fillSeasons(); }; catSel.onchange = function(){ fillSeasons(); }; goBtn.onclick = function(){ var y = yearSel.value, s = seasonSel.value, c = catSel.value; var arr = (index[y] && index[y][s]) ? index[y][s][c] : null; var paper = (arr && arr.length) ? arr[0] : null; if (!paper){ setStatus('未找到该组合的试卷'); return; } if (paper.exercise && paper.exercise.key){ setStatus('正在跳转:' + paper.name); location.href = 'https://spa.fenbi.com/ti/exam/exercise/' + paper.exercise.key + '?routecs=xingce&examcatid=' + CATID; return; } setStatus('正在创建试卷(' + paper.name + ')...'); goBtn.disabled = true; fetch(EXER_URL, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'type=1&paperId=' + paper.id + '&exerciseTimeMode=2' }).then(function(r){ return r.json(); }).then(function(j){ goBtn.disabled = false; if (j && j.key){ setStatus('正在跳转:' + paper.name); location.href = 'https://spa.fenbi.com/ti/exam/exercise/' + j.key + '?routecs=xingce&examcatid=' + CATID; } else { setStatus('创建试卷失败,请重试'); } }).catch(function(e){ goBtn.disabled = false; setStatus('创建试卷失败:' + e.message); }); }; function init(){ var all = []; function pageUrl(p){ return LIST_URL + p + '&pageSize=100&app=web&examcatid=' + CATID; } fetch(pageUrl(0), {credentials:'include'}) .then(function(r){ return r.json(); }) .then(function(j0){ all = j0.list || []; var tp = (j0.pageInfo && j0.pageInfo.totalPage) || 1; var chain = Promise.resolve(); for (var p=1;p= 0) yearSel.value = DEF_Y; // 类别(先就位,供季度标注统计使用) catSel.innerHTML = ''; for (var c=0;c