您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
帮助你直接下载国自然结题报告
// ==UserScript== // @name NSFC_conclusion_2024.2.4 // @namespace https://blog.rhilip.info/ // @version 24.2.7 // @description 帮助你直接下载国自然结题报告 // @author Rhilip // @match https://kd.nsfc.gov.cn/finalDetails* // @match https://kd.nsfc.cn/finalDetails* // @require https://unpkg.com/[email protected]/dist/jspdf.umd.min.js // @require https://unpkg.com/[email protected]/dist/jquery.js // ==/UserScript== /* globals $, jspdf */ // 异步函数,用于加载图片并确保图片完全加载 async function loadImage(url) { return new Promise((resolve, reject) => { const image = new Image(); // 当图片加载成功时解析 Promise image.onload = function() { resolve(image); }; // 当图片加载失败时拒绝 Promise,并传递错误信息 image.onerror = function() { reject(new Error('Failed to load image.')); }; // 设置图片的 URL,触发加载过程 image.src = url; }); } // 异步函数,用于加载图片并具备重试机制和检查机制 async function loadImageWithRetryAndCheck(url, maxRetries = 5, delay = 10000, checkInterval = 1000) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const image = await loadImage(url); return image; // 图片加载成功,解析 Promise } catch (error) { // 图片加载失败,等待一段时间后再次尝试 console.error(`Image loading attempt ${attempt} failed. Retrying in ${delay / 1000} seconds. Error: ${error.message}`); await new Promise(resolve => setTimeout(resolve, delay)); // 检查图片是否已经加载完成,如果是则立即返回 try { await loadImage(url); console.log(`Image loaded after delay during check.`); return; // 图片已加载完成,不再等待 } catch (checkError) { // 图片还未加载完成,继续检查 } } } // 图片加载尝试达到最大次数后,拒绝 Promise throw new Error(`Failed to load image after ${maxRetries} attempts.`); } // 核心下载循环,使用 loadImageWithRetryAndCheck 函数确保图片完全加载后再添加到 PDF (async function() { 'use strict'; // 准备交互按钮 const downloadBtn = $('<div class="el-button is-round">下载全文</div>'); // 点击交互按钮时需要开始下载操作 downloadBtn.click(async () => { downloadBtn.prop('disabled', true).addClass('is-disabled'); if (!/暂无结题报告全文/.test($('#related').text())) { // 获得项目信息: 编号(加密后)、批准号、项目名称 const urlParams = new URLSearchParams(location.search); const dependUintID = urlParams.get('id'); const projectID = $('.basic_info > div.el-row div:contains("项目批准号:") + div').text(); const projectName = $('.basic_info > div.el-row div:contains("项目名称:") + div').text(); // 准备需要的PDF文件,并删除初始页 const doc = new jspdf.jsPDF(); doc.deletePage(1); doc.setDocumentProperties({ title: `${projectID} ${projectName}`, subject: location.href, creator: 'NSFC_conclusion_downloader' }); let has_download_error = 0; // 核心下载方法 for (let i = 1;; i++) { downloadBtn.text(`正在下载第 ${i} 页`); try { // 获得图片链接 const { data: requestData } = await $.post('/api/baseQuery/completeProjectReport', { id: dependUintID, index: i }); // 获得Blob形式的imageData,这样可以防止image.src和jsPDF.addImage会产生两次图片请求,浪费带宽 // (实际变成了一次请求服务器和两次请求本地blob) const imageDataAsBlob = await $.ajax({ url: requestData.url, method: 'GET', xhrFields: { responseType: 'blob' } }); // 使用 loadImageWithRetryAndCheck 函数确保图片加载完整 await loadImageWithRetryAndCheck(URL.createObjectURL(imageDataAsBlob)); // 将图片添加进PDF中 const loadedImage = await loadImage(URL.createObjectURL(imageDataAsBlob)); doc.addPage([loadedImage.width, loadedImage.height], loadedImage.width < loadedImage.height ? 'p' : 'l'); doc.addImage(loadedImage, "PNG", 0, 0, loadedImage.width, loadedImage.height); if (requestData.hasnext === false) { console.log('No more pages to download.'); break; // hasnext可能为null,此处应该明确为false才break } } catch (e) { // 处理下载图片失败的情况 console.error(`Failed to download Page.${i}, Please check and re-download it. Error: ${e.message}`); has_download_error += 1; downloadBtn.prop('disabled', false).removeClass('is-disabled'); break; // 增加一行,确保在下载失败时停止下载 } } // 我们并没法 await 保存过程,所以直接显示下载完成就好,浏览器处理好会自动显示下载文件 doc.save(`${projectID} ${projectName}.pdf`); downloadBtn.text(has_download_error > 0 ? '下载过程中可能出错,请检查下载文件或尝试重新下载': '下载完成'); } }); // 将交互按钮插入到页面中 const observer = new MutationObserver(mutations => { const conclusion_another = $('div.link_title:has(div:contains("结题报告"),div:contains("全文"))'); if (conclusion_another.length > 0 && downloadBtn.is(':hidden')) { conclusion_another.find('div.verticalBar_T').after(downloadBtn); } }); observer.observe(document.body, { childList: true, subtree: true }); })();