您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
一个让vjudge更美观更便捷的脚本
// ==UserScript== // @name Vjudge Better! // @namespace https://github.com/1000ttank/vjudge-better // @version 1.2.2 // @description 一个让vjudge更美观更便捷的脚本 // @author 1000ttank // @match https://vjudge.net/* // @icon https://vjudge.net/favicon.ico // @require https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js // @run-at document-body // @license MIT // @grant GM_addStyle // ==/UserScript== (function () { const PAGE_TYPES = [ /\/problem$/, /\/problem\/[^\/]+$/, /\/problem\/[^\/]+\/origin$/, /\/problem\/description\/[^\/]+$/, /\/status$/, /\/solution\/[^\/]+\/origin$/, /\/solution\/[^\/]+\/[^\/]+$/, /\/contest$/, /\/contest\/[^\/]+$/, /\/contest\/statistic$/, /\/workbook$/, /\/article\/create$/, /\/article\/[^\/]+$/, /\/user$/, /\/user\/[^\/]+$/, /\/group$/, /\/group\/[^\/]+$/, /\/comment$/, /\/message$/ ]; function detectPageType(path) { const cleanPath = path.replace(/\/$/, ""); return PAGE_TYPES.findIndex(re => re.test(cleanPath)); } const pageId = detectPageType(location.pathname); if (pageId === -1 && location.pathname !== "/") { console.warn("Unknown VJudge page – possibly 404."); } const pagesNeedingBg = new Set([0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]); const frostedBackground = `body { background: #f0f0f0 url(http://static.rqnoj.cn/images/bg.jpg) no-repeat center top fixed !important; background-size: cover !important; }`; if (pagesNeedingBg.has(pageId) || pageId === -1) { GM_addStyle(frostedBackground); $("body").prepend("<nav style='height: 60px'></nav>"); } if (pageId === 3) { GM_addStyle("dd {background-color: rgba(255,255,255,0.7) !important; border-radius: 4px !important;}"); } // Global UI Theme GM_addStyle(` .navbar { background-color: rgba(255, 255, 255, 0.75) !important; backdrop-filter: blur(10px); position: fixed; top: 0; left: 0; width: 100%; z-index: 1000; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .navbar a, .navbar .nav-link, .navbar-brand { color: #000 !important; font-weight: 500; } .navbar a:hover, .navbar .nav-link:hover { background-color: rgba(255, 255, 255, 1.0); color: #000 !important; } .navbar .nav-item.active > .nav-link { font-weight: bold !important; } ::-webkit-scrollbar { display: none; } #prob-ads, #img-support { display: none !important; } .card, .list-group-item, .btn-secondary, .page-link, .dropdown-menu, .modal-content, .form-control, .tab-content { background-color: rgba(255, 255, 255, 0.65) !important; backdrop-filter: blur(6px); border-radius: 0.5rem; } .card:hover, .dropdown-menu:hover { background-color: rgba(255, 255, 255, 0.85) !important; } .form-control { border: 1px solid #ccc; } .body-footer { color: #333; background: rgba(255, 255, 255, 0.85); padding: 1em; border-top: 1px solid #ddd; } .list-group-item.active, .page-item.active .page-link, .navbar .nav-link.active, .btn.active, .dropdown-item.active, .tag.active { background-color: #007bff !important; color: #fff !important; } @media (max-width: 768px) { .navbar { font-size: 14px; } } /* 新增内容区域高度设置 */ main, .container, .content, .body-content, .container-fluid { min-height: 100vh !important; } `); // Footer credit $("body > div.body-footer").append( '<p style="text-align:center">Theme enhanced by <a href="https://github.com/1000ttank/vjudge-better" target="_blank">vjudge-better</a></p>' ); })(); (function () { 'use strict'; const defaultSettings = { lang: 'zh', bgColor: '#f0f8ff', bgImg: '', foldBlock: false, prettyDropdown: false, }; function getSetting(key) { return GM_getValue(key, defaultSettings[key]); } function setSetting(key, value) { GM_setValue(key, value); } window.addEventListener('load', function () { const logoutBtn = document.querySelector('#header .logout'); if (!logoutBtn || document.querySelector('#vjudge-better-btn')) return; const settingBtn = document.createElement('button'); settingBtn.id = 'vjudge-better-btn'; settingBtn.textContent = 'Vjudge Better! 设置'; settingBtn.style.cssText = ` background-color: #003366; color: white; margin-left: 12px; padding: 6px 12px; border: none; border-radius: 6px; cursor: pointer; `; logoutBtn.parentNode.insertBefore(settingBtn, logoutBtn.nextSibling); settingBtn.addEventListener('click', () => { showSettingsPanel(); }); applyCustomBackground(); }); function applyCustomBackground() { const color = getSetting('bgColor'); const imgUrl = getSetting('bgImg') || ''; document.body.style.backgroundColor = color; document.body.style.backgroundImage = imgUrl ? `url(${imgUrl})` : 'none'; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundRepeat = 'no-repeat'; document.body.style.backgroundAttachment = 'fixed'; document.body.style.backgroundPosition = 'center top'; } function showSettingsPanel() { const mask = document.createElement('div'); mask.id = 'vjudge-better-mask'; mask.style.cssText = ` position: fixed; inset: 0; background: rgba(0,0,0,0.3); z-index: 9998; `; mask.addEventListener('click', closeSettingsPanel); const panel = document.createElement('div'); panel.id = 'vjudge-better-panel'; panel.style.cssText = ` width: 650px; height: 800px; background: #f9fbff; border-radius: 12px; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 9999; display: flex; box-shadow: 0 0 20px rgba(0,0,0,0.2); font-family: sans-serif; `; panel.addEventListener('click', e => e.stopPropagation()); const closeBtn = document.createElement('div'); closeBtn.style.cssText = ` position: absolute; top: 10px; right: 10px; width: 14px; height: 14px; background: #e74c3c; border-radius: 50%; cursor: pointer; `; closeBtn.title = '关闭'; closeBtn.addEventListener('click', closeSettingsPanel); const menu = document.createElement('div'); menu.style.cssText = ` width: 120px; background: #e6f0ff; border-right: 1px solid #ccc; padding-top: 40px; box-sizing: border-box; display: flex; flex-direction: column; `; const contentArea = document.createElement('div'); contentArea.style.cssText = ` flex: 1; padding: 20px; overflow-y: auto; `; const sections = ['基本', '个性化', '翻译', '关于']; const menuButtons = []; sections.forEach((label, idx) => { const btn = document.createElement('div'); btn.textContent = label; btn.style.cssText = ` padding: 10px 16px; cursor: pointer; color: #003366; transition: background 0.2s; `; btn.addEventListener('click', () => { menuButtons.forEach(b => b.style.background = ''); btn.style.background = '#d6eaff'; renderSection(label); }); if (idx === 0) { btn.style.background = '#d6eaff'; renderSection(label); } menu.appendChild(btn); menuButtons.push(btn); }); function renderSection(section) { contentArea.innerHTML = ''; if (section === '基本') { contentArea.innerHTML = ` <h2>基本设置</h2> <label>脚本语言:</label> <select id="langSelect"> <option value="zh">中文</option> <option value="en">English</option> </select> `; contentArea.querySelector('#langSelect').value = getSetting('lang'); contentArea.querySelector('#langSelect').addEventListener('change', e => { setSetting('lang', e.target.value); alert('语言设置已保存'); }); } else if (section === '个性化') { contentArea.innerHTML = ` <h2>个性化设置</h2> <label>背景颜色:</label> <input type="color" id="bgColorPicker"><br><br> <label>背景图片链接:</label> <input type="text" id="bgImgUrl" placeholder="可为空,不填则无背景图" style="width: 100%;"><br><br> <label><input type="checkbox" id="foldBlock"> 启用折叠块优化</label><br> <label><input type="checkbox" id="prettyDropdown"> 渲染下拉框美化</label> `; const bgPicker = contentArea.querySelector('#bgColorPicker'); bgPicker.value = getSetting('bgColor'); bgPicker.addEventListener('input', e => { setSetting('bgColor', e.target.value); applyCustomBackground(); }); const bgImg = contentArea.querySelector('#bgImgUrl'); bgImg.value = getSetting('bgImg') || ''; bgImg.addEventListener('change', e => { setSetting('bgImg', e.target.value.trim()); applyCustomBackground(); }); contentArea.querySelector('#foldBlock').checked = getSetting('foldBlock'); contentArea.querySelector('#foldBlock').addEventListener('change', e => { setSetting('foldBlock', e.target.checked); }); contentArea.querySelector('#prettyDropdown').checked = getSetting('prettyDropdown'); contentArea.querySelector('#prettyDropdown').addEventListener('change', e => { setSetting('prettyDropdown', e.target.checked); }); } else if (section === '翻译') { contentArea.innerHTML = ` <h2>翻译设置</h2> <label>翻译引擎:</label> <select id="engineSelect"> <option value="deepl">DeepL(免费试用)</option> <option value="youdao">有道翻译</option> </select><br><br> <label>目标语言:</label> <select id="targetLang"> <option value="zh">中文</option> <option value="en">英文</option> <option value="ja">日文</option> </select> `; } else if (section === '关于') { contentArea.innerHTML = ` <h2>关于</h2> <p>这是一个用于增强 VJudge 页面体验的油猴脚本。</p> <ul> <li>支持自定义样式</li> <li>优化界面布局</li> <li>新增翻译与渲染增强功能</li> </ul> `; } } function closeSettingsPanel() { document.getElementById('vjudge-better-panel')?.remove(); document.getElementById('vjudge-better-mask')?.remove(); } panel.appendChild(closeBtn); panel.appendChild(menu); panel.appendChild(contentArea); document.body.appendChild(mask); document.body.appendChild(panel); } })();