您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
爬取小雅平台的课件
当前为
// ==UserScript== // @name 小雅爬爬爬 // @match https://*.ai-augmented.com/* // @grant none // @description 爬取小雅平台的课件 // @license MIT // @author Yi // @version 1.1.5 // @namespace https://greasyfork.org/users/1268039 // ==/UserScript== var isProgressBarVisible = true; // 添加样式规则来更改 placeholder 的颜色 var styleSheet = document.createElement('style'); styleSheet.type = 'text/css'; styleSheet.innerText = ` #searchInput::placeholder { color: #ffa500; } .custom-checkbox { width: 20px; height: 20px; appearance: none; background-color: #fff; border: 2px solid #ffa500; border-radius: 4px; margin-right: 10px; outline: none; cursor: pointer; transition: background-color 0.3s ease, border-color 0.3s ease; } .custom-checkbox:checked { background-color: #ffa500; border-color: #ffa500; } .custom-checkbox:checked::before { content: '✓'; display: block; text-align: center; line-height: 18px; color: #fff; font-size: 16px; } .custom-checkbox:hover { border-color: #ff8c00; } @keyframes flowingGradient { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } `; document.head.appendChild(styleSheet); (function() { 'use strict'; // 动态导入dotlottie-player模块 const script = document.createElement('script'); script.src = 'https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs'; script.type = 'module'; // 指定导入类型为module document.head.appendChild(script); // 将脚本添加到<head>中 // 监听脚本的加载事件以确认导入成功 script.onload = () => { console.log('dotlottie-player模块已导入成功!'); }; // 监听错误事件以确认导入失败 script.onerror = () => { console.error('无法导入dotlottie-player模块!'); }; })(); let course_resources; let svgElementIds = []; let originalOrder = null; // 等待 iframe 加载完成 function waitForIframe(selector, callback) { const iframe = document.querySelector(selector); if (iframe && iframe.contentDocument.readyState === 'complete') { callback(iframe); // iframe 已加载,执行回调 } else { setTimeout(() => waitForIframe(selector, callback), 50); // 等待 50ms 后重试 } } // 获取 SVG 元素 ID (仅数字) function getSvgElementIds(iframe) { const iframeDocument = iframe.contentDocument; const targetSvgElement = iframeDocument.querySelector("body > svg > g"); const gElementsWithId = targetSvgElement.querySelectorAll("g[id]"); // 过滤出纯数字 ID const numericIds = Array.from(gElementsWithId) .filter(element => /^\d+$/.test(element.id)) // 正则表达式匹配纯数字 .map(element => element.id); return numericIds; } // 处理 iframe 的回调函数 function handleIframeLoad(iframe) { console.log("目标 iframe 已加载完成!"); // 跨域处理 try { svgElementIds = getSvgElementIds(iframe); // 更新外部作用域中的 svgElementIds console.log(svgElementIds); } catch (error) { console.error("无法访问 iframe 内容,可能存在跨域限制:", error); } } // 监视页面的变化 const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.tagName === 'IFRAME') { waitForIframe("#xy_app_content iframe", handleIframeLoad); } } } } }); // 开始监视 observer.observe(document.body, { childList: true, subtree: true }); function parseContent() { console.oldLog("::parseContent"); // 在解析课程内容前清除筛选条件 window.currentSearchKeyword = ''; window.currentFilterCategory = ''; const searchInput = document.getElementById("searchInput"); if (searchInput) { searchInput.value = ''; } const quickFilterSelect = document.getElementById("quickFilterSelect"); if (quickFilterSelect) { quickFilterSelect.selectedIndex = 0; // 选择 "全部" } var hostname = window.location.hostname; var download_url = 'https://' + hostname + '/api/jx-oresource/cloud/file_url/'; var download_list = document.getElementById("download_list"); download_list.innerHTML = '<h3 style="color:#fcbb34; font-weight:bold;">课程附件清单</h3>'; for (let i in course_resources) { let resource = course_resources[i]; let file_name = resource.name; let create_time = new Date(resource.created_at).toLocaleDateString(); if (resource.mimetype) { const token = getCookie("HS-prd-access-token"); // 获取 token fetch(download_url + resource.quote_id, { // 添加 headers headers: { Authorization: `Bearer ${token}` } }) .then(function(response) { return response.json(); }) .then(function(data) { if (data.success) { let file_url = data.data.url; var file_container = document.createElement('div'); file_container.className = 'file-item'; file_container.style.display = 'flex'; file_container.style.alignItems = 'center'; var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'custom-checkbox'; checkbox.style.marginRight = '10px'; checkbox.setAttribute('data-visible', 'true'); var file_info = document.createElement('a'); file_info.innerHTML = file_name; file_info.href = file_url; file_info.target = "_blank"; file_info.className = 'download-link link-style'; // 添加'download-link'类 file_info.style.textDecoration = 'none'; file_info.title = `创建日期:${create_time}`; file_info.setAttribute('data-created-at', resource.created_at); file_info.setAttribute('data-origin-name', file_name); // 保存原始文件名称 file_info.setAttribute('data-resource-id', resource.id); file_info.setAttribute('data-parent-id', resource.parent_id); file_info.draggable = true; // 允许拖动 file_info.addEventListener('dragstart', (event) => { event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', file_url); // 保存下载地址 event.dataTransfer.setData('text/filename', file_name); // 保存文件名 }); file_info.style.display = 'inline-block'; file_info.style.padding = '5px 10px'; file_info.style.borderRadius = '5px'; // 添加圆角 file_info.style.transition = 'all 0.3s ease'; // 过渡效果,用于动画 // 鼠标悬停样式 file_info.addEventListener('mouseover', () => { file_info.style.backgroundColor = '#FFD180'; // 背景变色 file_info.style.color = 'white'; // 文字颜色 file_info.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.2)'; // 阴影效果 }); // 鼠标移出样式 file_info.addEventListener('mouseout', () => { file_info.style.backgroundColor = 'transparent'; // 背景透明 file_info.style.color = '#007bff'; // 恢复文字颜色 file_info.style.boxShadow = 'none'; // 移除阴影 }); // 点击时的样式变化 file_info.addEventListener('mousedown', () => { file_info.style.transform = 'scale(0.95)'; // 按下效果 }); file_info.addEventListener('mouseup', () => { file_info.style.transform = 'scale(1)'; // 恢复原样 }); file_info.addEventListener('mouseover', () => { file_info.style.textDecoration = 'underline'; file_info.style.color = '#000'; file_info.style.fontWeight = "bold"; }); file_info.addEventListener('mouseout', function() { file_info.style.textDecoration = 'none'; file_info.style.color = ''; }); // 拦截点击事件 file_info.addEventListener('click', function(event) { event.preventDefault(); // 阻止默认行为 courseDownload(file_url, file_name); // 调用下载函数 }); file_container.appendChild(checkbox); file_container.appendChild(file_info); file_container.style.borderBottom = '1px solid #eee'; file_container.style.fontWeight = "bold"; file_container.style.padding = '5px 10px'; file_container.style.justifyContent = 'flex-start'; file_container.style.alignItems = 'center'; console.oldLog('::parse', file_name, file_url); download_list.append(file_container); } }).catch(function(e) { console.oldLog('!!error', e); }); } } } let toggleButton; // 在文档中创建一个容器来放置所有下载进度条 let downloadsContainer = document.getElementById('downloadsContainer'); if (!downloadsContainer) { downloadsContainer = document.createElement('div'); downloadsContainer.id = 'downloadsContainer'; downloadsContainer.style.position = 'fixed'; downloadsContainer.style.bottom = '150px'; downloadsContainer.style.left = '10px'; downloadsContainer.style.right = 'auto'; downloadsContainer.style.zIndex = '9999'; downloadsContainer.style.backgroundColor = '#FFF3E0'; downloadsContainer.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.2)'; downloadsContainer.style.borderRadius = '8px'; downloadsContainer.style.padding = '30px'; downloadsContainer.style.width = 'auto'; downloadsContainer.style.minWidth = '300px'; downloadsContainer.style.boxSizing = 'border-box'; downloadsContainer.style.margin = '0 auto'; downloadsContainer.style.border = '1px solid #FFD180'; // 应用过渡效果到 transform 属性 downloadsContainer.style.transition = 'transform 0.3s ease-in-out'; // 设置最大高度限制和溢出滚动 downloadsContainer.style.maxHeight = '190px'; downloadsContainer.style.overflowY = 'auto'; downloadsContainer.style.transform = 'translateX(-90%)'; downloadsContainer.addEventListener('dragover', function(e) { e.preventDefault(); // 阻止默认行为以允许拖放 e.dataTransfer.dropEffect = 'move'; }); downloadsContainer.addEventListener('drop', function(e) { e.preventDefault(); // 阻止默认行为 var downloadUrl = e.dataTransfer.getData('text/plain'); var filename = e.dataTransfer.getData('text/filename'); if (downloadUrl && filename) { courseDownload(downloadUrl, filename); } }); // 创建收起/展示按钮 toggleButton = document.createElement('button'); toggleButton.title = '点击展开/收折进度条' toggleButton.textContent = '\u25BA'; // 初始为右箭头 toggleButton.style.position = 'absolute'; toggleButton.style.top = '50%'; toggleButton.style.right = '8px'; toggleButton.style.transform = 'translateY(-50%) rotate(0deg)'; toggleButton.style.zIndex = '10000'; toggleButton.style.border = 'none'; toggleButton.style.boxShadow = '0 2px 4px 0 rgba(0, 0, 0, 0.24), 0 4px 5px 0 rgba(0, 0, 0, 0.19)'; // 添加层次感阴影 toggleButton.style.background = 'linear-gradient(45deg, #FFC107, #FF9800)'; // 渐变背景 toggleButton.style.borderRadius = '8px'; // 圆角 toggleButton.style.padding = '4px 8px'; // 内边距 toggleButton.style.paddingRight = '2px'; toggleButton.style.paddingLeft = '2px'; toggleButton.style.color = 'white'; // 按钮颜色为白色 toggleButton.style.fontSize = '12px'; // 字体大小 toggleButton.style.cursor = 'pointer'; toggleButton.style.transition = 'background-color 0.3s, box-shadow 0.3s, transform 0.3s'; // 过渡效果 toggleButton.style.textShadow = '0 0 0px transparent'; toggleButton.onmouseover = function() { toggleButton.style.background = 'linear-gradient(45deg, #FFEB3B, #FFC107)'; // 悬停时的渐变背景 toggleButton.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.24), 0 6px 10px 0 rgba(0, 0, 0, 0.19)'; // 悬停时增加阴影的层次和模糊度 toggleButton.style.transform = 'translateY(-60%) rotate(0deg)'; // 按钮向上移动,增加点击感 }; toggleButton.onmouseout = function() { toggleButton.style.background = 'linear-gradient(45deg, #FFC107, #FF9800)'; // 正常时的渐变背景 toggleButton.style.boxShadow = '0 2px 4px 0 rgba(0, 0, 0, 0.24), 0 4px 5px 0 rgba(0, 0, 0, 0.19)'; // 恢复正常阴影 toggleButton.style.transform = 'translateY(-50%) rotate(0deg)'; // 恢复原位置 }; let isCollapsed = true; // 初始状态设为已收起 toggleButton.onclick = function() { // 切换收起/展开状态 isCollapsed = !isCollapsed; if (isCollapsed) { downloadsContainer.style.transform = `translateX(-90%)`; toggleButton.textContent = '\u25BA'; // 右箭头,表示容器已收起 } else { downloadsContainer.style.transform = 'translateX(0)'; toggleButton.textContent = '\u25C4'; // 左箭头,表示容器已展开 } toggleButton.style.transform = 'translateY(-50%) rotate(360deg)'; // 旋转箭头 }; // 将收起/展示按钮添加到 downloadsContainer downloadsContainer.appendChild(toggleButton); // 创建 dotlottie-player 容器 let lottieContainer = document.createElement('div'); lottieContainer.style.position = 'absolute'; lottieContainer.style.top = '0'; lottieContainer.style.left = '0'; lottieContainer.style.width = '100%'; lottieContainer.style.height = '100%'; lottieContainer.style.zIndex = '-1'; // 确保 lottie 在底层 lottieContainer.innerHTML = ` <dotlottie-player src="https://lottie.host/0500ecdb-7f3b-4f73-ab09-155a70f85ce3/ZCLltVc7A4.json" background="transparent" speed="1" style="width: 100%; height: 100%;" loop autoplay> </dotlottie-player>`; // 将 dotlottie-player 容器添加到 downloadsContainer 的顶部 downloadsContainer.prepend(lottieContainer); } // 确保 downloadsContainer 被添加到文档中 if (!document.body.contains(downloadsContainer)) { document.body.appendChild(downloadsContainer); } function updateContainerPosition() { // 获取窗口的高度 let windowHeight = window.innerHeight; // 计算容器的高度(包括内边距和边框) let downloadsContainerHeight = downloadsContainer.offsetHeight; // 获取当前容器的顶部位置 let currentTop = downloadsContainer.getBoundingClientRect().top; // 如果容器底部将要超出屏幕,则向上移动容器 if (currentTop + downloadsContainerHeight > windowHeight) { let newTop = windowHeight - downloadsContainerHeight - 10; // 留出10px的边距 downloadsContainer.style.top = `${newTop}px`; downloadsContainer.style.bottom = 'auto'; } } function updateIndicator() { let indicator = document.getElementById('progressIndicator'); let progressBarCount = downloadsContainer.querySelectorAll('.progressBar').length; if (!indicator) { // 如果指示器不存在,创建指示器 indicator = document.createElement('div'); indicator.id = 'progressIndicator'; indicator.style.position = 'absolute'; indicator.style.top = '10px'; indicator.style.right = '5px'; indicator.style.backgroundColor = '#f00'; indicator.style.backgroundImage = 'linear-gradient(to bottom right, #ffcc80, #ff8c00)'; indicator.style.color = 'white'; indicator.style.textShadow = '0px 1px 2px rgba(0, 0, 0, 0.7)'; // 添加文本阴影 indicator.style.borderRadius = '50%'; indicator.style.width = '20px'; indicator.style.height = '20px'; indicator.style.display = 'flex'; indicator.style.alignItems = 'center'; indicator.style.justifyContent = 'center'; indicator.style.fontSize = '12px'; indicator.style.fontWeight = 'bold'; indicator.style.boxShadow = '0px 2px 4px rgba(0, 0, 0, 0.4)'; // 添加外阴影 indicator.style.transition = 'background-color 0.3s, box-shadow 0.3s'; // 平滑变化的过渡效果 downloadsContainer.appendChild(indicator); } // 更新指示器的文本,即使进度条数量为0也显示 indicator.textContent = progressBarCount; } function updateDownloadsContainerVisibility() { // 检查是否存在除 lottieContainer、提示信息、收起按钮和指示器外的其他子元素 const nonEmptyNodes = Array.from(downloadsContainer.children).filter(child => { return !child.classList.contains('slide-out') && child.tagName !== 'P' && child !== downloadsContainer.firstChild && child !== toggleButton && child.id !== 'progressIndicator'; }); if (nonEmptyNodes.length === 0) { if (!downloadsContainer.querySelector('p')) { let emptyText = document.createElement('p'); emptyText.innerHTML = ` 暂无下载内容 ᓚᘏᗢ<br> <span style="font-size: 10px;">(可拖动文件下载)</span> `; emptyText.style.color = '#FF9800'; emptyText.style.textAlign = 'center'; emptyText.style.fontFamily = 'Microsoft YaHei'; emptyText.style.fontSize = '16px'; emptyText.style.fontWeight = 'bold'; emptyText.style.padding = '20px'; emptyText.style.marginTop = '15px'; emptyText.style.borderRadius = '8px'; emptyText.style.opacity = '0'; emptyText.style.transition = 'opacity 1s ease-in-out, transform 1s'; emptyText.style.transform = 'translateY(-20px)'; emptyText.style.top = '50%'; // 添加到DOM后触发动画 setTimeout(() => { emptyText.style.opacity = '1'; emptyText.style.transform = 'translateY(0)'; }, 100); // 轻微上下浮动动画 emptyText.animate([ { transform: 'translateY(0)' }, { transform: 'translateY(-10px)' }, { transform: 'translateY(0)' } ], { duration: 3000, iterations: Infinity, easing: 'ease-in-out' }); downloadsContainer.appendChild(emptyText); } // 显示或隐藏进度条容器基于 isProgressBarVisible 变量 downloadsContainer.style.display = isProgressBarVisible ? 'block' : 'none'; } else { let emptyText = downloadsContainer.querySelector('p'); if (emptyText) { downloadsContainer.removeChild(emptyText); } } // 更新按钮可见性 toggleButton.style.display = isProgressBarVisible ? 'block' : 'none'; } updateIndicator() updateDownloadsContainerVisibility(); function courseDownload(file_url, file_name) { const token = getCookie("HS-prd-access-token"); const downloadsContainer = document.getElementById('downloadsContainer'); // 创建进度条相关元素。 let progressText = document.createElement('span'); let progressBar = document.createElement('div'); let progressBarContainer = document.createElement('div'); let progressContainer = document.createElement('div'); // 设置文本。 progressText.innerText = `正在下载: ${file_name}`; progressText.style.color = '#FF9800'; progressText.style.fontWeight = 'bold'; progressText.style.fontSize = '16px'; progressText.style.fontFamily = 'Microsoft YaHei'; progressText.style.textShadow = '1px 1px 2px rgba(0, 0, 0, 0.1)'; progressText.style.padding = '5px 0'; progressText.style.borderRadius = '4px'; // 设置progressBar。 progressBar.style.height = '18px'; progressBar.style.width = '0%'; progressBar.style.borderRadius = '8px'; progressBar.className = 'progressBar' // 设置progressBarContainer。 progressBarContainer.style.background = '#E0E0E0'; progressBarContainer.style.borderRadius = '8px'; progressBarContainer.style.height = '18px'; progressBarContainer.style.width = '100%'; progressBarContainer.style.overflow = 'hidden'; progressBarContainer.appendChild(progressBar); // 设置 progressContainer。 progressContainer.style.display = 'flex'; progressContainer.style.flexDirection = 'column'; // 子元素垂直排列 progressContainer.style.alignItems = 'center'; // 子元素在交叉轴上居中对齐 progressContainer.style.justifyContent = 'space-around'; // 子元素沿着主轴均匀分布 progressContainer.appendChild(progressText); // 创建一个用来显示下载百分比的span元素 let progressPercentText = document.createElement('span'); progressPercentText.style.fontFamily = 'Arial Rounded MT Bold, Helvetica Rounded, Arial, sans-serif'; progressPercentText.style.fontSize = '16px'; progressPercentText.style.marginLeft = '10px'; // 与进度条保持一定距离 progressPercentText.style.position = 'absolute'; progressPercentText.style.left = '0'; progressPercentText.style.top = '0'; progressPercentText.style.width = '100%'; progressPercentText.style.textAlign = 'center'; progressPercentText.style.lineHeight = '18px'; progressPercentText.style.zIndex = '1'; // 调整字体粗细 progressPercentText.style.fontWeight = 'bold'; // 设置 progressBarContainer 的样式并添加到 progressContainer progressContainer.appendChild(progressBarContainer); // 插入用于显示百分比的 DOM 元素到 progressContainer progressBarContainer.style.position = 'relative'; // 相对定位,以便内部的百分比文本能正确定位 progressBarContainer.appendChild(progressPercentText); progressContainer.classList.add('slide-in'); updateIndicator() updateDownloadsContainerVisibility(); // 添加 progressContainer 到 downloadsContainer downloadsContainer.appendChild(progressContainer); const controller = new AbortController(); const signal = controller.signal; window.abortControllers = window.abortControllers || {}; window.abortControllers[file_name] = controller; // 添加文件大小和停止按钮到一个新的横向排列的容器 let controlContainer = document.createElement('div'); controlContainer.style.display = 'flex'; controlContainer.style.justifyContent = 'space-between'; controlContainer.style.alignItems = 'center'; controlContainer.style.width = '100%'; // 创建文件大小显示元素 let fileSizeSpan = document.createElement('span'); // 设置样式为微软雅黑和圆润 fileSizeSpan.style.fontFamily = 'Microsoft YaHei'; fileSizeSpan.style.fontSize = '14px'; fileSizeSpan.style.marginRight = 'auto'; fileSizeSpan.style.fontWeight = 'bold'; let stopButton = document.createElement('button'); stopButton.textContent = '停止'; stopButton.style.padding = '5px 10px'; stopButton.style.border = 'none'; stopButton.style.borderRadius = '5px'; stopButton.style.backgroundColor = '#FF4136'; stopButton.style.color = 'white'; stopButton.style.cursor = 'pointer'; stopButton.style.fontSize = '14px'; stopButton.style.fontWeight = 'bold'; stopButton.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; stopButton.style.transition = 'transform 0.3s ease'; stopButton.style.marginTop = '10px'; // 增加顶部外边距以调整与进度条的距离 stopButton.onmouseover = function() { stopButton.style.transform = 'scale(1.2)'; }; stopButton.onmouseout = function() { stopButton.style.transform = 'scale(1)'; }; stopButton.onclick = function() { console.log(file_name + ' 的下载已经停止了'); controller.abort(); progressContainer.classList.replace('slide-in', 'slide-out'); progressContainer.addEventListener('animationend', () => { progressContainer.remove(); updateIndicator(); updateDownloadsContainerVisibility(); }); }; controlContainer.appendChild(fileSizeSpan); controlContainer.appendChild(stopButton); progressContainer.appendChild(controlContainer); let styleElement = document.getElementById('progress-bar-styles'); if (!styleElement) { let styles = document.createElement('style'); styles.id = 'progress-bar-styles'; styles.textContent = ` .progressBar { background-color: #FF9800; background-image: repeating-linear-gradient( 45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent ); background-size: 40px 40px; animation: moveBackground 2s linear infinite; } @keyframes moveBackground { from { background-position: 0 0; } to { background-position: 40px 0; } } .slide-in { animation: slideIn 0.5s ease-out forwards; } .slide-out { animation: slideOut 0.5s ease-in forwards; } @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(styles); } function bytesToSize(bytes) { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes === 0) return '0 Byte'; const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i]; } updateIndicator() updateDownloadsContainerVisibility(); updateContainerPosition(); // 使用fetch API开始下载文件 fetch(file_url, { signal, headers: { Authorization: `Bearer ${token}` } }) .then(response => { // 获取文件大小信息 const contentLength = response.headers.get('Content-Length'); if (contentLength) { // 更新文件大小显示 const fileSize = bytesToSize(contentLength); fileSizeSpan.innerText = `文件大小: ${fileSize}`; } else { // 如果无法获取文件大小,则显示消息 fileSizeSpan.innerText = `无法获取文件大小`; updateIndicator() updateDownloadsContainerVisibility(); } const reader = response.body.getReader(); let receivedBytes = 0; let chunks = []; reader.read().then(function processResult(result) { if (result.done) { // 下载完成后的处理 const blob = new Blob(chunks, { type: 'application/octet-stream' }); const downloadUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = file_name; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(downloadUrl); progressContainer.classList.add('slide-out'); progressContainer.addEventListener('animationend', () => { progressContainer.remove(); updateIndicator(); // 更新指示器 updateDownloadsContainerVisibility(); // 刷新下载列表的可见性 }, { once: true }); return; } // 存储接收到的数据块 chunks.push(result.value); receivedBytes += result.value.length; // 计算下载的百分比并更新UI let percentComplete = (receivedBytes / contentLength) * 100; progressBar.style.width = `${percentComplete.toFixed(2)}%`; // 更新进度条的宽度 progressPercentText.innerText = `${percentComplete.toFixed(2)}%`; // 读取下一部分数据 reader.read().then(processResult); }) .catch(e => { // 下载失败或被中止时的处理 if (e.name === 'AbortError') { console.error(`${file_name} 的下载被用户取消了`); } else { console.error(e); } progressContainer.remove(); updateIndicator() updateDownloadsContainerVisibility(); }); }); } window.currentSearchKeyword = ''; window.currentFilterCategory = ''; window.updateUI = function() { var download_list = document.getElementById("download_list"); if (!document.getElementById("lottie-animation-container")) { var lottieContainer = document.createElement("div"); lottieContainer.id = "lottie-animation-container"; lottieContainer.innerHTML = ` <dotlottie-player src="https://lottie.host/f6cfdc36-5c9a-4dac-bb71-149cdf2e7d92/VRIhn9vXE5.json" background="transparent" speed="1" style="width: 300px; height: 300px; transform: scaleX(-1);" loop autoplay> </dotlottie-player>`; lottieContainer.style.position = "absolute"; lottieContainer.style.top = "50%"; lottieContainer.style.right = "0"; lottieContainer.style.transform = "translateY(-50%)"; lottieContainer.style.zIndex = "-1"; download_list.appendChild(lottieContainer); } // 为新创建的链接添加拖放事件监听器 const fileLinks = document.querySelectorAll("#download_list .file-item a"); fileLinks.forEach(fileLink => { fileLink.draggable = true; fileLink.addEventListener('dragstart', (event) => { event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', fileLink.href); event.dataTransfer.setData('text/filename', fileLink.getAttribute('data-origin-name')); }); }); var container = document.getElementById("searchAndFilterContainer"); if (!container) { container = document.createElement("div"); container.id = "searchAndFilterContainer"; container.style.position = "relative" container.style.display = "flex"; container.style.marginBottom = '10px'; download_list.prepend(container); } var searchInput = document.getElementById("searchInput"); if (!searchInput) { searchInput = document.createElement("input"); searchInput.type = "text"; searchInput.placeholder = "搜索文件名/后缀名"; searchInput.id = "searchInput"; searchInput.style.padding = '5px'; searchInput.style.marginRight = '10px'; searchInput.style.border = '2px solid #ffa500'; searchInput.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)'; searchInput.style.borderRadius = '4px'; searchInput.style.outline = 'none'; searchInput.style.color = '#ffa500'; searchInput.style.fontWeight = 'bold'; searchInput.style.backgroundColor = '#fffbe6'; searchInput.addEventListener("input", function () { filterList(this.value); }); container.appendChild(searchInput); } var quickFilterSelect = document.getElementById("quickFilterSelect"); if (!quickFilterSelect) { quickFilterSelect = document.createElement("select"); quickFilterSelect.id = "quickFilterSelect"; quickFilterSelect.style.padding = '5px'; quickFilterSelect.style.marginRight = '10px'; quickFilterSelect.style.border = '2px solid #ffa500'; quickFilterSelect.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)'; quickFilterSelect.style.borderRadius = '4px'; quickFilterSelect.style.outline = 'none'; quickFilterSelect.style.cursor = 'pointer'; quickFilterSelect.style.color = '#ffa500'; quickFilterSelect.style.fontWeight = 'bold'; quickFilterSelect.style.backgroundColor = '#fffbe6'; window.quickFilters.forEach(function (filter) { var option = document.createElement("option"); option.value = filter.value; option.text = filter.label; quickFilterSelect.appendChild(option); }); quickFilterSelect.addEventListener("change", function () { filterListByCategory(this.value); }); container.appendChild(quickFilterSelect); } else { quickFilterSelect.innerHTML = ''; // 清空已有的选项 // 重新添加筛选选项 window.quickFilters.forEach(function (filter) { var option = document.createElement("option"); option.value = filter.value; option.text = filter.label; option.style.fontWeight = 'bold'; quickFilterSelect.appendChild(option); }); // 更新选中状态以匹配当前的筛选条件 for (var i = 0; i < quickFilterSelect.options.length; i++) { if (quickFilterSelect.options[i].value === window.currentFilterCategory) { quickFilterSelect.selectedIndex = i; break; } } } // 添加排序下拉框 var sortSelect = document.getElementById("sortSelect"); if (!sortSelect) { // 如果还没有排序下拉框,则创建 sortSelect = document.createElement("select"); sortSelect.id = "sortSelect"; // 修改排序下拉框样式 sortSelect.style.padding = '5px'; sortSelect.style.marginRight = '10px'; sortSelect.style.border = '2px solid #ffa500'; sortSelect.style.boxShadow = 'inset 0 1px 3px rgba(0,0,0,0.1)'; // 添加内部阴影效果 sortSelect.style.borderRadius = '4px'; sortSelect.style.outline = 'none'; sortSelect.style.cursor = 'pointer'; sortSelect.style.color = '#ffa500'; // 设置文本颜色 sortSelect.style.fontWeight = 'bold'; sortSelect.style.backgroundColor = '#fffbe6'; // 添加排序选项 var sortOptions = [ { value: '', label: '排序方式' }, { value: 'date_asc', label: '日期升序' }, { value: 'date_desc', label: '日期降序' }, { value: 'xiaoya_order', label: '小雅排序' } ]; sortOptions.forEach(function (option) { var opt = document.createElement("option"); opt.value = option.value; opt.text = option.label; opt.style.fontWeight = 'bold'; sortSelect.appendChild(opt); }); // 添加排序下拉框事件监听器 sortSelect.addEventListener("change", function () { sortList(this.value); // 调用原有的日期排序函数 }); container.appendChild(sortSelect); // 将排序下拉框添加到容器中 } var existingSelectAllCheckbox = document.getElementById("selectAllCheckbox"); if (!existingSelectAllCheckbox) { var selectAllCheckbox = document.createElement('input'); selectAllCheckbox.type = 'checkbox'; selectAllCheckbox.className = 'custom-checkbox'; selectAllCheckbox.id = 'selectAllCheckbox'; selectAllCheckbox.style.marginRight = '10px'; var selectAllLabel = document.createElement('label'); selectAllLabel.htmlFor = 'selectAllCheckbox'; selectAllLabel.textContent = '全选'; selectAllLabel.style.fontWeight = 'bold'; selectAllLabel.style.color = '#FFA500'; selectAllLabel.style.userSelect = 'none'; var checkboxContainer = document.createElement('div'); checkboxContainer.appendChild(selectAllCheckbox); checkboxContainer.appendChild(selectAllLabel); download_list.prepend(checkboxContainer); checkboxContainer.style.display = 'flex'; checkboxContainer.style.position = "relative" checkboxContainer.style.alignItems = 'center'; checkboxContainer.style.padding = '5px 10px'; checkboxContainer.style.marginBottom = '10px'; selectAllCheckbox.addEventListener('change', function() { var checkboxes = document.querySelectorAll("#download_list input[type='checkbox']:not(#selectAllCheckbox)"); checkboxes.forEach(function(checkbox) { if (checkbox.getAttribute('data-visible') === 'true') { checkbox.checked = selectAllCheckbox.checked; var event = new Event('change', { 'bubbles': true, 'cancelable': true }); checkbox.dispatchEvent(event); } }); }); } var existingBulkDownloadButton = document.getElementById("bulkDownloadButton"); if (!existingBulkDownloadButton) { var bulkDownloadButton = document.createElement('button'); bulkDownloadButton.innerHTML = '<span style="font-weight: bold;">批量下载</span>'; bulkDownloadButton.id = "bulkDownloadButton"; bulkDownloadButton.title = '点击下载所选文件'; bulkDownloadButton.style.cssText = ` position: fixed; top: 15px; right: 15px; background: linear-gradient(90deg, #ffa500, #ff8c00, #ffa500); background-size: 200% 100%; animation: flowingGradient 3s ease infinite; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.2); transition: filter 0.3s, transform 0.3s; /* 修改 transition 属性 */ `; bulkDownloadButton.addEventListener('mouseover', function() { this.style.transform = 'scale(1.05)'; this.style.backgroundColor = '#ffd564'; }); bulkDownloadButton.addEventListener('mouseout', function() { this.style.transform = 'scale(1)'; this.style.backgroundColor = '#fcbb34'; }); bulkDownloadButton.addEventListener('click', function() { var checkboxes = document.querySelectorAll("#download_list input[type='checkbox']:checked"); checkboxes.forEach(function(checkbox) { if (checkbox.checked && checkbox.getAttribute('data-visible') === 'true') { var container = checkbox.closest('div'); var link = container.querySelector('a'); var file_name = link.getAttribute('data-origin-name'); // 使用原始文件名 var file_url = link.href; courseDownload(file_url, file_name); } }); }); download_list.appendChild(bulkDownloadButton); window.bulkDownloadButton = bulkDownloadButton; } } function sortList(order) { const downloadList = document.getElementById("download_list"); const items = Array.from(downloadList.querySelectorAll(".file-item")); // 构建文件夹 ID 到索引的映射(用于小雅排序) const folderIndexMap = {}; svgElementIds.forEach((id, index) => { folderIndexMap[id] = index; }); items.sort((a, b) => { const aParentId = a.querySelector('a').getAttribute('data-parent-id'); const bParentId = b.querySelector('a').getAttribute('data-parent-id'); const aId = a.querySelector('a').getAttribute('data-resource-id'); const bId = b.querySelector('a').getAttribute('data-resource-id'); if (order === 'xiaoya_order') { const aFolderIndex = folderIndexMap[aParentId] || Infinity; const bFolderIndex = folderIndexMap[bParentId] || Infinity; if (aFolderIndex !== bFolderIndex) { return aFolderIndex - bFolderIndex; // 不同文件夹,按文件夹顺序排序 } else { return aId.localeCompare(bId); // 同一文件夹,按文件 ID 排序 } } else { const aDate = new Date(a.querySelector('a').getAttribute('data-created-at')); const bDate = new Date(b.querySelector('a').getAttribute('data-created-at')); if (order === 'date_asc') { return aDate - bDate; } else if (order === 'date_desc') { return bDate - aDate; } } }); items.forEach(item => downloadList.appendChild(item)); // 重新应用筛选条件 applyFilters(); } window.toggleListVisibility = function() { var download_list = document.getElementById("download_list"); // 切换列表的显示和隐藏 if (download_list.style.maxHeight === '0px' || download_list.style.maxHeight === '') { // 展开列表 download_list.style.maxHeight = "300px"; download_list.style.opacity = "1"; download_list.style.overflowY = "auto"; // 添加垂直滚动条 } else { // 折叠列表 download_list.style.maxHeight = "0"; download_list.style.opacity = "0"; download_list.style.overflowY = "hidden"; // 隐藏垂直滚动条 } } function filterList(keyword) { window.currentSearchKeyword = keyword.toLowerCase(); // 保存当前搜索关键词 // 更新搜索框的值 const searchInput = document.getElementById("searchInput"); if (searchInput) { searchInput.value = keyword; } applyFilters(); } function filterListByCategory(categoryValue) { window.currentFilterCategory = categoryValue; // 保存当前分类 applyFilters(); // 应用筛选和搜索 } function applyFilters() { var searchKeyword = window.currentSearchKeyword; var filterCategory = window.currentFilterCategory; var extensions = filterCategory ? filterCategory.split(',').map(ext => ext.trim()) : []; var containers = document.querySelectorAll("#download_list .file-item"); containers.forEach(function(container) { var file = container.querySelector("a"); var checkbox = container.querySelector("input[type='checkbox']"); var fileName = file.getAttribute('data-origin-name').toLowerCase(); var fileExtension = fileName.slice(((fileName.lastIndexOf(".") - 1) >>> 0) + 2); var isSearchMatch = searchKeyword === '' || fileName.includes(searchKeyword); var isFilterMatch = filterCategory === "" || extensions.includes(fileExtension); var isVisible = isSearchMatch && isFilterMatch; container.style.display = isVisible ? "flex" : "none"; // 保持布局结构 checkbox.setAttribute('data-visible', isVisible.toString()); }); const selectAllCheckbox = document.getElementById('selectAllCheckbox'); if (selectAllCheckbox) { const visibleCheckboxes = Array.from(document.querySelectorAll("#download_list .file-item input[type='checkbox'][data-visible='true']")); const allVisibleChecked = visibleCheckboxes.every(checkbox => checkbox.checked); selectAllCheckbox.checked = visibleCheckboxes.length > 0 && allVisibleChecked; } // 检查筛选后是否还有可见的课件 var visibleItems = document.querySelectorAll("#download_list .file-item[style*='display: flex']"); // 获取下载列表容器 var download_list = document.getElementById("download_list"); // 获取已有的提示信息元素 var noResultsMessage = download_list.querySelector('p'); if (visibleItems.length === 0) { // 如果没有可见的课件 if (!noResultsMessage) { // 如果提示信息元素不存在,则创建 noResultsMessage = document.createElement('p'); noResultsMessage.style.color = '#FFA500'; // 保持橙色 noResultsMessage.style.textAlign = 'center'; noResultsMessage.style.fontWeight = 'bold'; // 加粗字体 noResultsMessage.innerHTML = '<span style="font-size: 1.2em;"><span style="color: red;">没有</span></span>课件( ´・・)ノ(._.`)'; download_list.appendChild(noResultsMessage); } } else { // 如果有可见的课件,则移除之前的提示信息 if (noResultsMessage) { download_list.removeChild(noResultsMessage); } } } function add_download_button() { // 创建下载图标容器 var downloadIconContainer = document.createElement('div'); downloadIconContainer.id = "download_icon_container"; downloadIconContainer.innerHTML = ` <dotlottie-player class="download-icon" src="https://lottie.host/604bb467-91d8-46f3-a7ce-786e25f8fded/alw6gwjRdU.json" background="transparent" speed="1" style="width: 60px; height: 60px; margin: -15px;" loop autoplay onclick="toggleListVisibility()" title="点击展开或关闭列表"></dotlottie-player> `; // 设置下载图标容器的样式 downloadIconContainer.style.cssText = ` position: fixed; right: 10px; bottom: 10px; z-index: 9000; cursor: pointer; `; // 创建下载列表容器 var downloadListContainer = document.createElement('div'); downloadListContainer.id = "download_list"; downloadListContainer.style.cssText = ` z-index: 999; backdrop-filter: blur(10px); border: 2px solid #fcbb34; border-radius: 5px; max-height: 0; overflow-y: hidden; padding: 20px; flex-direction: column; align-items: flex-start; transition: max-height 0.5s ease-in-out, opacity 0.5s ease-in-out; opacity: 0; position: fixed; right: 30px; bottom: 50px; background-color: white; `; downloadListContainer.innerHTML = ` <h3 style="text-align: center;"> <span style=" background: linear-gradient(90deg, #ffa500, #ff8c00, #ffa500); background-size: 200% 100%; animation: flowingGradient 3s ease infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight:bold; ">暂无课件♪(´▽`)</span> </h3> <br> <p style="text-align: center; font-size: 12px; color: #888; font-weight:bold;">(在课程首页才能获取到资源)</p> `; // 添加下载图标的样式 var downloadIconStyle = document.createElement('style'); downloadIconStyle.innerHTML = ` .download-icon { padding: 2px; margin: -20px; background-color: rgba(255, 255, 255, 0); border-radius: 10px; transition: transform 0.3s ease; // 确保动画效果的平滑过渡 cursor: pointer; } .download-icon:hover { background-color: rgba(255, 255, 255, 0.3); transform: scale(1.1); // 悬停时放大 } `; document.head.appendChild(downloadIconStyle); document.body.appendChild(downloadIconContainer); document.body.appendChild(downloadListContainer); } // 获取cookie function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } function initializeControlPanel() { const controlPanel = document.createElement('div'); const checkboxesContainer = document.createElement('div'); const downloadInterfaceCheckbox = document.createElement('input'); const downloadButtonCheckbox = document.createElement('input'); const progressBarCheckbox = document.createElement('input'); const toggleButton = document.createElement('button'); const markReadButton = document.createElement('button'); let isControlPanelVisible = false; // 创建Lottie动画播放器元素 const lottiePlayer = document.createElement('dotlottie-player'); lottiePlayer.setAttribute('src', "https://lottie.host/4f5910c1-63a3-4ffa-965c-7c0e46a29928/PCa2EgPj4N.json"); lottiePlayer.setAttribute('background', 'transparent'); lottiePlayer.setAttribute('speed', '1'); lottiePlayer.style.width = '100%'; lottiePlayer.style.height = '100%'; lottiePlayer.style.position = 'absolute'; lottiePlayer.style.zIndex = '-1'; lottiePlayer.style.top = '0'; lottiePlayer.style.left = '0'; lottiePlayer.setAttribute('loop', ''); lottiePlayer.setAttribute('autoplay', ''); // 设置控制面板样式 controlPanel.style.position = 'fixed'; controlPanel.style.top = '210px'; controlPanel.style.right = isControlPanelVisible ? '30px' : '-500px'; controlPanel.style.zIndex = '10000'; controlPanel.style.backgroundColor = '#fff'; controlPanel.style.padding = '10px'; controlPanel.style.borderRadius = '5px'; controlPanel.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)'; controlPanel.style.border = '1px solid #fcbb34'; controlPanel.style.transition = 'right 0.3s ease-in-out'; controlPanel.style.overflow = 'hidden'; controlPanel.appendChild(lottiePlayer); // 创建流光线元素 const glowingLine = document.createElement('div'); glowingLine.style.position = 'relative'; glowingLine.style.height = '3px'; glowingLine.style.bottom = '22px'; glowingLine.style.right = '10px'; glowingLine.style.width = '110%'; glowingLine.style.zIndex = '9000'; glowingLine.style.background = 'linear-gradient(90deg, #ffa500, #ff8c00, #ffa500)'; glowingLine.style.backgroundSize = '200% 100%'; glowingLine.style.animation = 'flowingGradient 3s ease infinite'; // 一键更新按钮 const updateButton = document.createElement('button'); updateButton.textContent = '检查更新'; updateButton.style.marginTop = '10px'; updateButton.style.padding = '8px 15px'; updateButton.style.border = 'none'; updateButton.style.borderRadius = '25px'; updateButton.style.background = 'linear-gradient(90deg, #ffa500, #ff8c00, #ffa500)'; updateButton.style.backgroundSize = '200% 100%'; updateButton.style.animation = 'flowingGradient 3s ease infinite'; updateButton.style.color = '#fff'; updateButton.style.cursor = 'pointer'; updateButton.style.fontWeight = 'bold'; updateButton.style.fontSize = '16px'; updateButton.style.transition = 'all 0.3s ease'; updateButton.style.zIndex = '9999'; // 悬停、点击效果 updateButton.onmouseover = function() { updateButton.style.filter = 'brightness(1.2)'; // 悬停时增加亮度 updateButton.style.boxShadow = '0px 4px 6px rgba(0, 0, 0, 0.2)'; updateButton.style.transform = 'scale(1.05) translateY(-2px)'; }; updateButton.onmouseout = function() { updateButton.style.filter = 'brightness(1)'; // 恢复原始亮度 updateButton.style.boxShadow = 'none'; updateButton.style.transform = 'scale(1) translateY(0)'; }; updateButton.onmousedown = function() { updateButton.style.transform = 'scale(0.95) translateY(2px)'; // 点击时缩小并下移 }; updateButton.onmouseup = function() { updateButton.style.transform = 'scale(1.05) translateY(-2px)'; // 放开鼠标时恢复悬停效果 }; // 更新按钮容器,用于居中 const updateButtonContainer = document.createElement('div'); updateButtonContainer.style.display = 'flex'; updateButtonContainer.style.justifyContent = 'center'; updateButtonContainer.style.marginTop = '10px'; updateButtonContainer.appendChild(updateButton); // 按钮悬停效果 updateButton.onmouseover = function() { updateButton.style.transform = 'scale(1.15)'; }; updateButton.onmouseout = function() { updateButton.style.transform = 'scale(1)'; }; function compareVersions(currentVersion, latestVersion) { const currentParts = currentVersion.split('.').map(Number); const latestParts = latestVersion.split('.').map(Number); const maxLength = Math.max(currentParts.length, latestParts.length); for (let i = 0; i < maxLength; i++) { // 确保每个部分的位数相同 const currentPart = String(currentParts[i] || 0).padStart(3, '0'); // 补零到3位 const latestPart = String(latestParts[i] || 0).padStart(3, '0'); if (latestPart > currentPart) { return -1; // 最新版本较大 } else if (currentPart > latestPart) { return 1; // 当前版本较大 } } return 0; // 版本相同 } // 按钮点击事件:检查更新 updateButton.onclick = function() { const scriptInfo = GM_info; const currentVersion = scriptInfo.script.version; // 构建元数据文件的 URL const metaURL = scriptInfo.script.downloadURL.replace(/\.user\.js$/, '.meta.js'); fetch(metaURL) .then(response => response.text()) .then(text => { console.log('Fetched meta data:', text); // 打印获取到的元数据内容 const match = text.match(/\/\/\s*@version\s+([\d.]+)/); if (match && match[1]) { const latestVersion = match[1]; // 使用修改后的版本比较函数 const comparisonResult = compareVersions(currentVersion, latestVersion); if (comparisonResult < 0) { if (confirm(`检测到新版本 ${latestVersion},是否立即更新?`)) { window.location.href = scriptInfo.script.downloadURL; } } else { alert('当前已是最新版本!'); } } else { throw new Error('无法从更新信息中提取版本号。'); } }) .catch(error => { console.error('更新检查失败:', error); alert('更新检查失败,请稍后再试。'); }); }; // 定义按钮鼠标悬浮时的发光效果 const hoverGlowName = 'hover-glow'; const styleSheet = document.createElement('style'); styleSheet.type = 'text/css'; styleSheet.innerText = ` @keyframes ${hoverGlowName} { from { box-shadow: 0 0 8px #fcbb34; } to { box-shadow: 0 0 20px #fcbb34, 0 0 30px #fcbb34; } } #toggleButton:hover { animation: ${hoverGlowName} 1s ease-in-out infinite alternate; } .switch { position: relative; display: inline-block; width: 40px; height: 20px; } .switch:hover .slider { transform: scale(1.1); animation: ${hoverGlowName} 1s ease-in-out infinite alternate; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: background-color 0.4s, transform 0.4s ease; transition: 0.4s; border-radius: 20px; } .slider:before { position: absolute; content: ""; height: 14px; width: 14px; left: 3px; bottom: 3px; background-color: white; transition: 0.4s; border-radius: 50%; } input:checked + .slider { background-color: #fcbb34; } input:checked + .slider:before { transform: translateX(20px); } `; document.head.appendChild(styleSheet); // 设置切换按钮样式 const gearIcon = document.createElement('span'); gearIcon.textContent = '⚙️'; gearIcon.style.fontSize = '16px'; // 保持与按钮相同的字体大小 // 齿轮图标的样式 gearIcon.style.transition = 'transform 0.3s ease'; gearIcon.style.display = 'inline-block'; gearIcon.style.verticalAlign = 'middle'; // 将齿轮图标添加到按钮中 toggleButton.appendChild(gearIcon); toggleButton.id = 'toggleButton'; toggleButton.style.fontSize = '16px'; toggleButton.title = '控制面板'; toggleButton.style.position = 'fixed'; toggleButton.style.top = '210px'; toggleButton.style.right = '0'; toggleButton.style.zIndex = '10001'; toggleButton.style.backgroundColor = '#fcbb34'; toggleButton.style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.2)'; toggleButton.style.color = '#fff'; toggleButton.style.border = 'none'; toggleButton.style.borderRadius = '5px 0 0 5px'; toggleButton.style.padding = '10px'; toggleButton.style.cursor = 'pointer'; toggleButton.style.transition = 'right 0.3s ease-in-out, transform 0.3s ease'; // 添加悬停效果 toggleButton.style.transform = 'scale(1)'; // 默认比例 toggleButton.addEventListener('mouseover', function() { toggleButton.style.transform = 'scale(1.1)'; // 悬停时放大 }); toggleButton.addEventListener('mouseout', function() { toggleButton.style.transform = 'scale(1)'; // 鼠标移出时恢复原大小 }); // 切换按钮点击事件 toggleButton.addEventListener('click', function() { isControlPanelVisible = !isControlPanelVisible; controlPanel.style.right = isControlPanelVisible ? '40px' : '-500px'; gearIcon.style.transform = 'rotate(360deg)'; gearIcon.addEventListener('transitionend', function() { gearIcon.style.transform = 'none'; }, { once: true }); }); // 初始化复选框状态 function initializeCheckboxState(checkbox, index) { const savedState = localStorage.getItem(`checkbox-${index}`); checkbox.checked = savedState === null ? true : savedState === 'true'; progressBarCheckbox.addEventListener('change', () => { isProgressBarVisible = progressBarCheckbox.checked; updateVisibility(); }); } // 保存复选框状态 function saveCheckboxState(checkbox, index) { localStorage.setItem(`checkbox-${index}`, checkbox.checked); } // 初始化复选框 [downloadInterfaceCheckbox, downloadButtonCheckbox, progressBarCheckbox].forEach((checkbox, index) => { checkbox.type = 'checkbox'; initializeCheckboxState(checkbox, index); // 调用函数以应用保存的状态 checkbox.id = `checkbox-${index}`; checkbox.style.display = 'none'; // 隐藏原始复选框 const label = document.createElement('label'); label.className = 'switch'; label.htmlFor = `checkbox-${index}`; const slider = document.createElement('span'); slider.className = 'slider'; label.appendChild(checkbox); label.appendChild(slider); const labelText = document.createElement('span'); labelText.textContent = ['右上角下载栏', '右下角下载栏', '左下角进度条'][index]; labelText.style.color = '#fcbb34'; // 橙色文字 labelText.style.marginRight = '10px'; labelText.style.fontWeight = 'bold'; // 文字加粗 labelText.style.verticalAlign = 'middle'; // 设置文字垂直居中 checkbox.addEventListener('change', () => { if (index === 1) { updateDownloadListVisibility(); // 仅当右下角下载列表复选框变化时,调用此函数 } else { updateVisibility(); // 否则调用一般的更新可见性函数 } saveCheckboxState(checkbox, index); // 保存复选框状态 }); const container = document.createElement('div'); container.style.display = 'flex'; container.style.justifyContent = 'space-between'; container.style.alignItems = 'center'; container.appendChild(labelText); container.appendChild(label); checkboxesContainer.appendChild(container); }); // 将复选框容器添加到控制面板中 controlPanel.appendChild(checkboxesContainer); // 按钮用于标记所有消息为已读 markReadButton.textContent = '已读所有消息'; markReadButton.style.marginTop = '10px'; markReadButton.style.padding = '5px 10px'; markReadButton.style.border = 'none'; markReadButton.style.borderRadius = '5px'; markReadButton.style.backgroundColor = '#fcbb34'; // 橙色背景 markReadButton.style.color = '#fff'; // 白色文字 markReadButton.style.cursor = 'pointer'; markReadButton.style.fontWeight = 'bold'; // 文字加粗 markReadButton.style.transition = 'transform 0.3s ease'; // 平滑过渡效果 // 按钮悬停效果 markReadButton.onmouseover = function() { markReadButton.style.transform = 'scale(1.1)'; // 悬停时放大 }; markReadButton.onmouseout = function() { markReadButton.style.transform = 'scale(1)'; // 鼠标移出时恢复原大小 }; // 绑定点击事件处理函数 markReadButton.onclick = function() { markAllMessagesAsRead(); }; controlPanel.appendChild(markReadButton); document.body.appendChild(controlPanel); document.body.appendChild(toggleButton); // 定义标记所有消息为已读的函数 async function markAllMessagesAsRead() { const token = getCookie("HS-prd-access-token"); const hostname = window.location.hostname; const pageSize = 20; const messageTypes = ["todo", "group", "personal", "system"]; let hasUnreadMessages = false; // 遍历每种消息类型 for (const messageType of messageTypes) { try { let pageIndex = 1; let unreadMessageIds = []; // 循环获取所有未读消息 while (true) { const apiUrl = `https://${hostname}/api/jx-iresource/message/im/${messageType}?page_size=${pageSize}&page_index=${pageIndex}&msg_status=0`; const response = await fetch(apiUrl, { headers: { "Authorization": `Bearer ${token}` } }); if (response.ok) { const data = await response.json(); const messages = data.data.list; if (messages.length === 0) { break; } unreadMessageIds = unreadMessageIds.concat(messages.map(message => message.id)); pageIndex++; } else { console.error(`获取未读${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, response.status, response.statusText); alert(`获取未读${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`); return; } } // 更新消息状态为已读 if (unreadMessageIds.length > 0) { hasUnreadMessages = true; const updateResponse = await fetch(`https://${hostname}/api/jx-iresource/message/im/updateStatus`, { method: "PUT", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, body: JSON.stringify({ message_ids: unreadMessageIds, status: 1 }) }); if (updateResponse.ok) { console.log(`所有${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息已标记为已读`); } else { console.error(`标记${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, updateResponse.status, updateResponse.statusText); alert(`标记${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`); return; } } else { console.log(`没有未读${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息`); } } catch (error) { console.error('Error:', error); alert(`标记${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`); return; } } if (hasUnreadMessages) { alert("所有消息已标记为已读"); } else { console.log("没有未读消息"); alert("没有未读消息"); } } // 按钮用于清空所有消息 const clearMessagesButton = document.createElement('button'); clearMessagesButton.textContent = '清空所有消息'; clearMessagesButton.style.marginTop = '10px'; clearMessagesButton.style.marginLeft = '5px'; // 与标记已读按钮留出一些间隙 clearMessagesButton.style.padding = '5px 10px'; clearMessagesButton.style.border = 'none'; clearMessagesButton.style.borderRadius = '5px'; clearMessagesButton.style.backgroundColor = '#fcbb34'; // 橙色背景 clearMessagesButton.style.color = '#fff'; // 白色文字 clearMessagesButton.style.cursor = 'pointer'; clearMessagesButton.style.fontWeight = 'bold'; // 文字加粗 clearMessagesButton.style.transition = 'transform 0.3s ease'; // 平滑过渡效果 // 按钮悬停效果 clearMessagesButton.onmouseover = function() { clearMessagesButton.style.transform = 'scale(1.1)'; // 悬停时放大 }; clearMessagesButton.onmouseout = function() { clearMessagesButton.style.transform = 'scale(1)'; // 鼠标移出时恢复原大小 }; // 绑定点击事件处理函数 clearMessagesButton.onclick = function() { // 弹出确认对话框 if (confirm("确定要清空所有消息吗?该操作无法撤销。")) { clearAllMessages(); } }; controlPanel.appendChild(clearMessagesButton); addShowCourseNameText(); addTipsDisplay(); // 定义清空所有消息的函数 async function clearAllMessages() { const token = getCookie("HS-prd-access-token"); const hostname = window.location.hostname; const pageSize = 20; const messageTypes = ["todo", "group", "personal", "system"]; let hasMessagesToClear = false; for (const messageType of messageTypes) { try { let pageIndex = 1; let messageIdsToClear = []; while (true) { const apiUrl = `https://${hostname}/api/jx-iresource/message/im/${messageType}?page_size=${pageSize}&page_index=${pageIndex}&msg_status=0`; const response = await fetch(apiUrl, { headers: { "Authorization": `Bearer ${token}` } }); if (response.ok) { const data = await response.json(); const messages = data.data.list; if (messages.length === 0) { break; } messageIdsToClear = messageIdsToClear.concat(messages.map(message => message.id)); pageIndex++; } else { console.error(`获取${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, response.status, response.statusText); alert(`获取${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败,请重试`); return; } } if (messageIdsToClear.length > 0) { hasMessagesToClear = true; const clearResponse = await fetch(`https://${hostname}/api/jx-iresource/message/im/selected/empty`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, body: JSON.stringify({ message_ids: messageIdsToClear }) }); if (clearResponse.ok) { console.log(`所有${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息已清空`); } else { const errorText = await clearResponse.text(); console.error(`清空${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败:`, errorText); alert(`清空${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息失败: ${errorText}`); return; } } } catch (error) { console.error('Error:', error); alert(`清空${messageType === 'todo' ? '待办' : messageType === 'group' ? '课程' : messageType === 'personal' ? '个人' : '系统'}消息出错: ${error.message}`); return; } } if (hasMessagesToClear) { alert("所有消息已清空"); } else { console.log("没有消息可清空"); alert("没有消息可清空"); } } // 获取课程名称 function getCourseName() { const footer = document.querySelector('.img_footer'); if (footer) { const groupNameElement = footer.querySelector('.group_name'); if (groupNameElement) { return groupNameElement.textContent; // 返回课程名称 } } return '未知'; // 如果没有找到课程名称,返回默认文本 } function addShowCourseNameText() { const showCourseNameText = document.createElement('div'); showCourseNameText.style.marginTop = '10px'; showCourseNameText.style.marginLeft = '5px'; showCourseNameText.style.backgroundColor = 'transparent'; showCourseNameText.style.color = '#fcbb34'; showCourseNameText.style.fontWeight = 'bold'; showCourseNameText.style.maxWidth = '200px'; const observer = new MutationObserver((mutations, obs) => { // 始终尝试更新课程名称 const courseName = getCourseName(); showCourseNameText.textContent = `当前课程:${courseName}`; }); observer.observe(document.body, { childList: true, subtree: true, attributes: false }); controlPanel.appendChild(showCourseNameText); } function addTipsDisplay() { const tipsDisplay = document.createElement('div'); tipsDisplay.id = 'tipsDisplay'; // 设置样式 tipsDisplay.style.padding = '10px'; tipsDisplay.style.marginTop = '10px'; tipsDisplay.style.backgroundColor = 'rgba(252, 187, 52, 0.2)'; // 浅橙色背景 tipsDisplay.style.border = '1px solid #fcbb34'; // 橙色边框 tipsDisplay.style.color = '#fcbb34'; // 橙色文字 tipsDisplay.style.borderRadius = '5px'; tipsDisplay.style.fontSize = '14px'; tipsDisplay.style.fontWeight = 'bold'; tipsDisplay.style.textAlign = 'center'; tipsDisplay.style.position = 'relative'; // 用于动画定位 tipsDisplay.style.overflow = 'hidden'; // 隐藏溢出的文本 tipsDisplay.textContent = '右上角下载栏可下载单个文件。'; tipsDisplay.style.opacity = '1'; tipsDisplay.style.transform = 'translateY(0)'; tipsDisplay.style.transition = 'opacity 0.5s, transform 0.5s'; controlPanel.appendChild(tipsDisplay); controlPanel.appendChild(updateButtonContainer); controlPanel.appendChild(glowingLine); // 提示信息列表 const tipsList = [ '右上角下载栏可下载单个文件。', '右下角下载栏可下载多个文件。', '可拖动链接至左下角进行下载。', '进度条位于左下角折叠列表中。', '搜索栏也可搜索后缀名等信息。', '右上角支持文档和压缩包下载。', '浏览器可以直接右键下载图片。', '视频可使用其他现成插件下载。', '如有建议请在油猴反馈处留言。', ]; // 当前显示的提示信息索引 let currentTipIndex = 0; let intervalId; // 用于存储 setInterval 的 ID // 更换提示信息的函数 function changeTip() { // 更新当前提示索引 currentTipIndex = (currentTipIndex + 1) % tipsList.length; // 执行移出动画 tipsDisplay.style.opacity = '0'; tipsDisplay.style.transform = 'translateX(-100%)'; } // 动画结束后更新提示文本并执行移入动画 tipsDisplay.addEventListener('transitionend', () => { if (tipsDisplay.style.opacity === '0') { tipsDisplay.textContent = tipsList[currentTipIndex]; tipsDisplay.style.opacity = '1'; tipsDisplay.style.transform = 'translateX(100%)'; requestAnimationFrame(() => { tipsDisplay.style.transform = 'translateX(0)'; }); } }); // 页面可见性变化时处理提示信息切换 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { // 页面可见时,重新开始切换提示信息 intervalId = setInterval(changeTip, 5000); } else { // 页面不可见时,停止切换提示信息 clearInterval(intervalId); } }); // 开始定时切换提示信息 intervalId = setInterval(changeTip, 5000); } function updateVisibility() { const downloadInterface = document.getElementById('downloadInterface'); const progressBarInterface = document.getElementById('downloadsContainer'); const downloadIconContainer = document.getElementById('download_icon_container'); const downloadListContainer = document.getElementById('download_list'); if (downloadInterface) { downloadInterface.style.display = downloadInterfaceCheckbox.checked ? 'block' : 'none'; } if (progressBarInterface) { const isVisible = progressBarCheckbox.checked; progressBarInterface.style.display = isVisible ? 'block' : 'none'; isProgressBarVisible = isVisible; // 更新 isProgressBarVisible 的值 } } let isFirstLoad = true; // 专门更新右下角下载列表可见性的函数 function updateDownloadListVisibility() { const downloadIconContainer = document.getElementById('download_icon_container'); const downloadListContainer = document.getElementById('download_list'); const isVisible = downloadButtonCheckbox.checked; if (downloadIconContainer && downloadListContainer) { downloadIconContainer.style.display = isVisible ? 'block' : 'none'; downloadListContainer.style.display = isVisible ? 'block' : 'none'; downloadListContainer.style.opacity = isVisible ? '1' : '0'; downloadListContainer.style.maxHeight = isVisible ? '300px' : '0'; } } // 一开始就应用一次设置以匹配初始复选框状态 updateVisibility(); updateDownloadListVisibility(); } window.onload = ()=> { add_download_button(); initializeControlPanel(); window.toggleListVisibility(); console.oldLog = console.log; console.log = (...data) =>{ if (data[0] == 'nodesToData') { course_resources = data[1]; console.oldLog('::', course_resources); parseContent(); return; } console.oldLog(...data); }; // 定义快速筛选的类别选项 window.quickFilters = [ { label: "全部", value: "" }, { label: "文档", value: "doc,docx,pdf,txt,odt,rtf,html,htm,xls,xlsx,ppt,pptx,odp" }, { label: "图片", value: "jpg,jpeg,png,gif,bmp,tiff,svg,webp,tif" }, { label: "压缩包", value: "zip,rar,7z,gz,bz2,tar" }, // 可以继续添加其他类别 ]; window.abortControllers = {}; // 创建一个 MutationObserver 实例 const observer = new MutationObserver(function(mutationsList, observer) { // 在每次发生 DOM 变化时触发这个回调函数 for(let mutation of mutationsList) { if (mutation.type === 'childList' && mutation.target.id === 'download_list') { // 重新添加搜索框和批量下载按钮 window.updateUI(); break; // 处理完毕后退出循环 } } }); // 配置需要观察的目标节点和观察的类型 observer.observe(document.body, { childList: true, subtree: true }); }; // 定义要抓取的后缀名 var extensions = [".doc",".pdf",".docx",".ppt",".pptx",".xls",".xlsx",".txt",".odt",".rtf",".zip",".rar",".7z",".tar",".gz",".bz2",".xz"]; // 创建一个元素,用于显示抓取到的 URL var list = document.createElement("div"); initializeListStyles(list); // 添加CSS动画样式 var style = document.createElement('style'); style.innerHTML = ` @keyframes slideIn { from { opacity: 0; transform: translateX(100%); } to { opacity: 1; transform: translateX(0); } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } `; document.head.appendChild(style); // 监听 xhr 请求,检查响应的 URL 是否符合条件 var open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { var xhr = this; // 强制将所有请求设置为异步 async = true; xhr.addEventListener("load", function() { // 如果 URL 包含指定的后缀名之一 for (var i = 0; i < extensions.length; i++) { if (url.includes(extensions[i])) { // 发送一个新的 xhr 请求,获取真正的下载地址 handleXhrResponse(url); break; } } }); open.call(xhr, method, url, async, user, pass); }; // 初始化列表样式 function initializeListStyles(element) { element.id = "downloadInterface"; element.style.position = "fixed"; element.style.top = "100px"; element.style.right = "0"; // 初始状态:缩成圆形,隐藏内容 element.style.width = "50px"; element.style.height = "50px"; element.style.borderRadius = "50%"; element.style.overflow = "hidden"; element.style.transition = "all 0.3s ease-in-out"; element.style.zIndex = "9997"; element.style.padding = "10px"; // 为元素添加渐变背景色 element.style.background = "linear-gradient(270deg, #ffc700, #ff8c00, #ff6500)"; element.style.backgroundSize = "400% 400%"; // 添加动态背景动画的 CSS 规则 var animStyle = document.createElement("style"); animStyle.textContent = ` @keyframes gradientBgAnimation { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .dynamic-gradient { animation: gradientBgAnimation 15s ease infinite; } `; document.head.appendChild(animStyle); // 为元素添加动态渐变背景的类 element.classList.add("dynamic-gradient"); // 为元素添加阴影效果 element.style.boxShadow = "0 4px 8px 0 rgba(0, 0, 0, 0.2)"; // 为元素添加动画效果,悬停时放大 element.addEventListener("mouseover", function() { element.style.transform = "scale(1.1)"; }); element.addEventListener("mouseout", function() { element.style.transform = "scale(1)"; }); element.innerHTML = "<h3><span style=\"font-family: '微软雅黑', 'Microsoft YaHei', sans-serif; font-weight: bold; font-style: italic; font-size: 16px;\">抓取到的课件</span></h3>"; element.querySelector('h3').style.opacity = 0; // 创建一个容器来存放Lottie动画 var lottieContainer = document.createElement('div'); lottieContainer.style.position = "absolute"; lottieContainer.style.transition = "all 0.3s ease-in-out"; // 添加 transition // Lottie 动画容器的初始位置和大小 lottieContainer.style.width = "200%"; lottieContainer.style.height = "200%"; lottieContainer.style.left = "-60%"; lottieContainer.style.top = "-45%"; lottieContainer.style.overflow = "hidden"; lottieContainer.style.zIndex = "9998"; lottieContainer.style.pointerEvents = "none"; // 创建并配置Lottie播放器 var lottiePlayer = document.createElement('dotlottie-player'); lottiePlayer.setAttribute('src', "https://lottie.host/995b71c8-b7aa-45b0-bb77-94b850da5d5d/dyezqbvtia.json"); lottiePlayer.setAttribute('background', "transparent"); lottiePlayer.setAttribute('speed', "1"); lottiePlayer.setAttribute('style', "width: 125%; height: 100%; position: absolute; left: -12.5%;"); lottiePlayer.setAttribute('loop', ""); lottiePlayer.setAttribute('autoplay', ""); // 将Lottie播放器添加到动画容器中 lottieContainer.appendChild(lottiePlayer); // 将动画容器添加为列表元素的子元素 element.appendChild(lottieContainer); // 点击事件:展开/收缩列表 element.addEventListener("click", function() { if (element.style.width === "50px") { element.style.width = "350px"; element.style.height = "10%"; element.style.borderRadius = "10px"; element.style.overflow = "auto"; lottieContainer.style.width = "70%"; lottieContainer.style.height = "100%"; // 动态计算目标位置 const targetLeft = element.offsetWidth - lottieContainer.offsetWidth + 120; const targetTop = -20; // 使用 transition 过渡 lottieContainer.style.left = targetLeft + "px"; lottieContainer.style.top = targetTop + "px"; element.querySelector('h3').style.opacity = 1; } else { element.style.width = "50px"; element.style.height = "50px"; element.style.borderRadius = "50%"; element.style.overflow = "hidden"; // 缩小 Lottie 动画容器 lottieContainer.style.width = "200%"; lottieContainer.style.height = "200%"; lottieContainer.style.left = "-60%"; lottieContainer.style.top = "-45%"; element.querySelector('h3').style.opacity = 0; } }); document.body.appendChild(element); } // 全局变量,用于存储唯一的预览链接元素 var previewLink; // 全局变量,用于标志是否有异步操作正在进行中 var isDownloading = false; function handleXhrResponse(url) { if (isDownloading) { return; // 如果已经有下载在进行中,则跳过 } isDownloading = true; const token = getCookie("HS-prd-access-token"); var xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.setRequestHeader("Authorization", `Bearer ${token}`); xhr.onload = function () { // 将响应的文本中的 "}}" 和引号替换为空字符串,去掉多余的符号 var text = xhr.responseText.replace("}}", "").replace(/"/g, ""); var match = text.match(/(http|https):\/\/\S+/); var content = document.title.split('|')[0].trim(); if (match) { // 如果预览链接存在,先移除它并带动画效果 if (previewLink) { previewLink.style.animation = "fadeOut 0.5s forwards"; previewLink.addEventListener('animationend', function () { if (previewLink && previewLink.parentNode) { previewLink.parentNode.removeChild(previewLink); previewLink = null; createPreviewLink(match[0], content); } }); } else { createPreviewLink(match[0], content); } } isDownloading = false; } xhr.send(); } // 检查是否已经存在预览链接,如果没有,则创建一个临时的提示预览链接 if (!previewLink || !document.contains(previewLink)) { createPreviewLink("#", "等待课件...( _ _)ノ|"); previewLink.removeAttribute("href"); // 移除 href 属性使其不可点击 previewLink.style.pointerEvents = "none"; // 确保用户无法与之交互 previewLink.style.color = "#DDD"; // 将提示信息的颜色设置为浅灰色 } function createPreviewLink(href, content) { // 清空列表中子元素之外所有元素 while (list.childNodes.length > 2) { list.removeChild(list.lastChild); } // 如果存在旧的 previewLink,则更新其内容 if (previewLink && document.contains(previewLink)) { previewLink.href = href; previewLink.textContent = content; previewLink.style.pointerEvents = ""; // 恢复交互 previewLink.style.color = ""; // 恢复原有颜色 } else { // 创建新预览链接 previewLink = document.createElement("a"); previewLink.style.background = "linear-gradient(to right, #ffffff, #ffecb3)"; previewLink.style.backgroundClip = "text"; previewLink.title = "点击以下载"; previewLink.style.webkitBackgroundClip = "text"; previewLink.style.color = "transparent"; previewLink.style.fontFamily = "'微软雅黑', 'Microsoft YaHei', sans-serif"; previewLink.style.fontWeight = "bold"; previewLink.style.transition = "transform 0.3s, text-shadow 0.3s"; previewLink.style.display = "inline-block"; previewLink.style.position = "relative"; // 为动画设置定位 // 为拖放功能添加拖动数据 previewLink.draggable = true; previewLink.dataset.downloadUrl = href; // 设置下载网址 previewLink.dataset.filename = content; // 设置文件名 // 重新绑定拖动事件监听器 previewLink.addEventListener('dragstart', (event) => { event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', previewLink.dataset.downloadUrl); event.dataTransfer.setData('text/filename', previewLink.dataset.filename); }); // 悬停时的样式变化 previewLink.addEventListener('mouseover', () => { previewLink.style.transform = "scale(1.05)"; // 轻微放大 previewLink.style.textShadow = "0 0 8px rgba(255, 165, 0, 0.7)"; }); previewLink.addEventListener('mouseout', () => { previewLink.style.transform = "scale(1)"; // 恢复原样 previewLink.style.textShadow = "none"; // 移除发光效果 }); // 点击时的样式变化 previewLink.addEventListener('mousedown', () => { previewLink.style.transform = "scale(0.95)"; // 按下效果 }); previewLink.addEventListener('mouseup', () => { previewLink.style.transform = "scale(1.05)"; // 放开后放大效果 }); // 将预览链接添加到列表中 list.appendChild(previewLink); list.appendChild(document.createElement("br")); // 更新预览链接的属性 previewLink.href = href; previewLink.target = "_blank"; previewLink.textContent = content; // 添加点击事件监听器,在点击时进行下载 previewLink.addEventListener("click", function (event) { event.preventDefault(); // 阻止默认的点击行为 // 直接使用 courseDownload 函数进行下载 courseDownload(href, content); }); // 添加动画效果 previewLink.style.animation = "slideIn 0.5s forwards"; } }