您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download OJ problems as markdown files
// ==UserScript== // @name IAI OJ Downloader // @namespace iai-sh-cn // @version 0.2 // @description Download OJ problems as markdown files // @license AGPL-3.0-or-later // @author Y.V // @match https://iai.sh.cn/* // @icon https://iai.sh.cn/images/logo.png // @grant GM_xmlhttpRequest // @require https://code.jquery.com/jquery-3.6.0.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js // ==/UserScript== (function() { 'use strict'; // ---------------------------- 常量定义 ---------------------------- const BUTTON_ID = 'download-button'; const BUTTON_TEXT_DEFAULT = '下载题目'; const BUTTON_TEXT_LOADING = '加载中...'; const BUTTON_STYLE = { 'position': 'fixed', 'top': '10px', 'right': '10px', 'width': '100px', 'height': '40px', 'border': 'none', 'border-radius': '5px', 'background': '#00a0e9', 'color': '#fff', 'fontSize': '16px', 'fontWeight': 'bold', 'cursor': 'pointer' }; const API_ENDPOINT = 'https://api.iai.sh.cn/contest/listProblem'; // ---------------------------- 函数定义 ---------------------------- /** * 创建下载按钮并添加到页面。 * @returns {JQuery<HTMLElement>} 下载按钮的 jQuery 对象。 */ function createDownloadButton() { const button = $(`<button id="${BUTTON_ID}">${BUTTON_TEXT_DEFAULT}</button>`); button.css(BUTTON_STYLE); $('body').append(button); return button; } /** * 从当前 URL 中提取比赛 ID。 * @returns {string|null} 比赛 ID,如果提取失败则返回 null。 */ function getContestIdFromUrl() { const urlParts = window.location.href.split('/'); return urlParts[4] || null; } /** * 从 API 获取题目数据。 * @param {string} contestId 比赛 ID。 * @returns {Promise<Array>} 包含题目数据的 Promise。 * @throws {Error} 如果 API 请求失败。 */ async function fetchProblemData(contestId) { const apiUrl = `${API_ENDPOINT}?contestId=${contestId}`; const response = await axios.get(apiUrl); return response.data; } /** * 按等级对题目数据进行分组。 * @param {Array} problems 题目数据数组。 * @returns {Object} 按等级分组的题目对象。 */ function groupProblemsByLevel(problems) { return problems.reduce((groups, problem) => { const level = problem.level || '未分类'; groups[level] = groups[level] || '未分级'; groups[level].push(problem); return groups; }, {}); } /** * 生成单个题目的 Markdown 内容。 * @param {Object} problem 题目对象。 * @param {number} index 题目在当前分组中的索引。 * @returns {string} 题目的 Markdown 内容。 */ function generateProblemMarkdown(problem, index) { let md = `### 题目 ${index + 1}\n\n`; md += `${problem.title}\n\n`; md += `#### 题目描述\n\n${problem.description}\n\n`; md += `#### 输入格式\n\n${problem.inputFormat}\n\n`; md += `#### 输出格式\n\n${problem.outputFormat}\n\n`; md += `#### 数据范围\n\n${problem.dataRange}\n\n`; md += `#### 样例数据\n\n`; problem.exampleList.forEach(example => { const input = example.input.replace(/\n/g, '\n> '); const output = example.output.replace(/\n/g, '\n> '); md += `**输入**\n\n> ${input}\n\n`; md += `**输出**\n\n> ${output}\n\n`; if (example.note) { md += `**说明**\n\n${example.note}\n\n`; } }); md += '\n\n\n'; // 题目之间添加空行 return md; } /** * 处理下载逻辑。 * @param {JQuery<HTMLElement>} button 下载按钮的 jQuery 对象。 */ async function handleDownload(button) { button.prop('disabled', true).text(BUTTON_TEXT_LOADING); try { const contestId = getContestIdFromUrl(); if (!contestId) { alert('无法从 URL 中获取比赛 ID。'); return; } const problemData = await fetchProblemData(contestId); const contestTitle = $('h2.ant-typography').text() || '题目'; const zip = new JSZip(); const problemsByLevel = groupProblemsByLevel(problemData); for (const level in problemsByLevel) { let markdownContent = ''; problemsByLevel[level].forEach((problem, index) => { markdownContent += generateProblemMarkdown(problem, index); }); zip.file(`${level}.md`, markdownContent); } const blob = await zip.generateAsync({ type: 'blob' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `${contestTitle}.zip`; link.click(); } catch (error) { console.error('下载题目失败:', error); alert('下载题目失败,请检查控制台错误信息。'); } finally { button.prop('disabled', false).text(BUTTON_TEXT_DEFAULT); } } // ---------------------------- 初始化 ---------------------------- $(document).ready(function() { const downloadButton = createDownloadButton(); downloadButton.click(() => handleDownload(downloadButton)); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址