From a3032692b2f854a9761e98e90fdc96a4ec49d4b8 Mon Sep 17 00:00:00 2001 From: Carrycyebo <1730452337@qq.com> Date: Thu, 27 Mar 2025 18:38:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E4=BA=86=E9=83=A8?= =?UTF-8?q?=E5=88=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AutoHaiTun.js | 417 +++++++++++++++++++++++++++----------------------- 1 file changed, 224 insertions(+), 193 deletions(-) diff --git a/AutoHaiTun.js b/AutoHaiTun.js index cbe85ab..da2018d 100644 --- a/AutoHaiTun.js +++ b/AutoHaiTun.js @@ -1,20 +1,21 @@ // ==UserScript== // @name 海豚自动化工具 // @namespace http://tampermonkey.net/ -// @version 1.2 +// @version 1.3 // @description 自动从代码块复制内容到编辑器,保存并重启笔记本 // @author Kakune55 Carryc // @match https://labs.wd.dolphin-labs.com/data-analysis/* // @grant none -// @run-at document-end +// @run-at document-idle // ==/UserScript== + (function() { 'use strict'; // 通知管理器 - 管理多个通知的位置 const NotificationManager = { notifications: [], - baseTop: 70, // 初始顶部位置 + baseTop: 45, // 初始顶部位置 gap: 10, // 通知间距 add: function(element) { let topPosition = this.baseTop; @@ -82,75 +83,172 @@ console.log('等待重启确认模态框...'); const checkModal = setInterval(() => { const modals = [...document.querySelectorAll('.ivu-modal')]; - const targetModal = modals.find(modal => modal.textContent.includes('重新启动内核并重新运行整个笔记本')); + const targetModal = modals.find(modal => + modal.textContent.includes('重新启动内核并重新运行整个笔记本') + ); + if (targetModal) { const confirmButton = targetModal.querySelector('.ivu-btn-primary'); if (confirmButton) { confirmButton.click(); console.log('已点击"确定"按钮'); clearInterval(checkModal); + // 执行完成后通知用户 setTimeout(() => { - showNotification('自动化执行完成:代码已复制、保存并重启', 'success'); + showNotification('自动化执行完成:代码已复制、保存并重启!若无警告,等待所有代码块执行完成后自行保存退出!', 'success', 6000); }, 1000); } } }, 200); } - // 提取代码文本 - function extractCodeText(codeBlock) { - return codeBlock.innerText || [...codeBlock.querySelectorAll('span')].reduce((text, span) => text + span.textContent, ''); + // 查找所有目标代码块(包括“代码示例”和练习答案) + function findAllCodeBlocks() { + const textCellWraps = Array.from(document.querySelectorAll('.text-cell-wrap')); + + const allCodeBlocks = []; + function findCodeBlock(container) { + if (!container) return null; + const codeBlock = container.querySelector('pre code'); + return codeBlock || findCodeBlock(container.nextElementSibling); + } + + textCellWraps.forEach((cellWrap, index) => { + console.log(`处理容器 ${index + 1}:`, cellWrap); + + // 优先查找“代码示例”代码块 + const exampleMarker = cellWrap.querySelector('.alert.alert-success.alertsuccess'); + if (exampleMarker && exampleMarker.textContent.includes('代码示例')) { + const codeBlock = findCodeBlock(cellWrap); + if (codeBlock) { + allCodeBlocks.push(codeBlock); + console.log(`找到“代码示例”代码块内容:`, codeBlock.textContent.trim()); + } else { + console.warn(`未找到“代码示例”代码块!`); + } + } + + // 然后查找练习答案代码块 + const collapseDiv = cellWrap.querySelector('.collapse'); + if (collapseDiv) { + const codeBlocks = collapseDiv.querySelectorAll('pre code'); + if (codeBlocks.length > 0) { + codeBlocks.forEach((codeBlock) => { + allCodeBlocks.push(codeBlock); + console.log(`找到练习答案代码块内容:`, codeBlock.textContent.trim()); + }); + } else { + console.warn(`容器 ${index + 1} 中未找到练习答案代码块!`); + } + } + }); + + return allCodeBlocks; } - // 显示美化的通知 - function showNotification(message, type = 'info') { - const notification = document.createElement('div'); - notification.className = 'dolphin-notification'; - let iconHTML = ''; - let borderColor = ''; - if (type === 'success') { - iconHTML = '
'; - borderColor = '#4CAF50'; - } else if (type === 'error') { - iconHTML = ' '; - borderColor = '#F44336'; - } else { - iconHTML = ' '; - borderColor = '#2196F3'; + // 主函数:自动化流程 + function runAutomation() { + const button = document.getElementById('dolphin-auto-btn'); + setButtonState(button, 'running'); // 修改按钮状态为“执行中” + + try { + // 1. 查找所有目标代码块 + const allCodeBlocks = findAllCodeBlocks(); + console.log(`共找到 ${allCodeBlocks.length} 个有效代码块:`); + allCodeBlocks.forEach((codeBlock, index) => { + console.log(`代码块 ${index + 1}:`, codeBlock.textContent.trim()); + }); + + // 2. 获取所有 CodeMirror 编辑器实例 + const codeMirrors = Array.from(document.querySelectorAll('.CodeMirror')) + .map(cmElement => cmElement.CodeMirror) + .filter(cmInstance => !!cmInstance); + + console.log(`找到 ${codeMirrors.length} 个 CodeMirror 实例:`); + + if (allCodeBlocks.length === 0 || codeMirrors.length === 0) { + throw new Error("未找到足够的代码块或编辑器!"); + } + + // 3. 将代码块内容填充到 CodeMirror 编辑器 + showNotification(`正在填充 ${Math.min(allCodeBlocks.length, codeMirrors.length)} 个代码块...`, 'info'); + const minCount = Math.min(allCodeBlocks.length, codeMirrors.length); + for (let i = 0; i < minCount; i++) { + const codeText = allCodeBlocks[i].textContent.trim(); + const cmInstance = codeMirrors[i]; + if (cmInstance) { + cmInstance.setValue(codeText); // 使用 CodeMirror API 填充内容 + console.log(`已填充代码块 ${i + 1} 到 CodeMirror ${i + 1}`); + } else { + try { + const textarea = codeMirrors[i].querySelector('textarea'); + if (textarea) { + textarea.value = codeText; // 使用替代方法填充内容 + console.log(`使用替代方法填充代码块 ${i + 1}`); + } + } catch (e) { + console.error(`无法填充代码块 ${i + 1}:`, e); + } + } + } + + // 4. 检查多余的 CodeMirror 实例并通知用户 + if (codeMirrors.length > allCodeBlocks.length) { + const extraCount = codeMirrors.length - allCodeBlocks.length; + console.warn(`多余的 CodeMirror 实例未被填充: 多出 ${extraCount} 个实例`); + showNotification( + `多余的 CodeMirror 实例未被填充: 多出 ${extraCount} 个实例,请检查最后是否有练习,手动填充`, + 'warning', + 10000 // 通知停留时间设置为 10 秒 + ); + } + + // 5. 点击保存按钮 + console.log('准备点击保存按钮...'); + showNotification('正在保存笔记本...', 'info'); + const saveButton = findSaveButton(); + if (!saveButton) { + throw new Error("未找到保存按钮!"); + } + saveButton.click(); + console.log('已点击保存按钮'); + + // 6. 等待保存成功后重启 + waitForMessage(() => { + console.log('准备点击重启按钮...'); + showNotification('正在重启笔记本...', 'info'); + const restartButton = findRestartButton(); + if (!restartButton) { + throw new Error("未找到重启按钮!"); + } + restartButton.click(); + console.log('找到重启按钮,点击中...'); + waitForRestartModal(); // 等待模态框确认 + setButtonState(button, 'completed'); // 修改按钮状态为“已完成” + }); + + } catch (error) { + console.error(error.message); + showNotification(`自动化执行失败:${error.message}`, 'error'); // 显示错误通知 + setButtonState(button, 'failed'); // 修改按钮状态为“失败” } - notification.innerHTML = iconHTML + ` 元素
- const exampleCodeBlocks = [];
-
- // 辅助函数:递归查找
- function findCodeBlock(container) {
- if (!container) return null;
- const codeBlock = container.querySelector('pre code');
- return codeBlock || findCodeBlock(container.nextElementSibling);
- }
-
- textCellWraps.forEach((cellWrap, index) => {
- console.log(`处理代码示例 ${index + 1} 的父容器:`, cellWrap);
- const codeBlock = findCodeBlock(cellWrap);
- if (codeBlock) {
- exampleCodeBlocks.push(codeBlock);
- console.log(`找到“代码示例”代码块内容:`, codeBlock.textContent.trim());
- } else {
- console.warn(`未找到“代码示例”代码块!`);
- }
- });
-
- // 找到所有练习答案区域中的
- const answerCodeBlocks = [];
- const collapseDivs = Array.from(document.querySelectorAll('.collapse'));
- console.log(`找到 ${collapseDivs.length} 个练习答案区域:`);
- collapseDivs.forEach((collapseDiv, index) => {
- console.log(`检查练习答案区域 ${index + 1}:`, collapseDiv);
- const codeBlocks = collapseDiv.querySelectorAll('pre code');
- if (codeBlocks.length > 0) {
- codeBlocks.forEach((codeBlock) => {
- answerCodeBlocks.push(codeBlock);
- console.log(`找到练习答案代码块内容:`, codeBlock.textContent.trim());
- });
- } else {
- console.warn(`练习答案区域 ${index + 1} 中未找到 !`);
- }
- });
-
- // 合并所有代码块
- return [...exampleCodeBlocks, ...answerCodeBlocks];
- }
-
- // 主函数
- function runAutomation() {
- const button = document.getElementById('dolphin-auto-btn');
- setButtonState(button, 'running');
-
- // 查找所有目标代码块
- const allCodeBlocks = findAllCodeBlocks();
- console.log(`共找到 ${allCodeBlocks.length} 个有效代码块:`);
- allCodeBlocks.forEach((codeBlock, index) => {
- console.log(`代码块 ${index + 1}:`, codeBlock.textContent.trim());
- });
-
- // 查找所有 CodeMirror 编辑器实例
- const codeMirrors = Array.from(document.querySelectorAll('.CodeMirror'))
- .map(cmElement => cmElement.CodeMirror)
- .filter(cmInstance => !!cmInstance);
-
- console.log(`找到 ${codeMirrors.length} 个 CodeMirror 实例:`);
-
- if (allCodeBlocks.length > 0 && codeMirrors.length > 0) {
- showNotification(`正在填充 ${Math.min(allCodeBlocks.length, codeMirrors.length)} 个代码块...`, 'info');
-
- // 将代码块内容复制到编辑器
- allCodeBlocks.forEach((codeBlock, index) => {
- if (index < codeMirrors.length) {
- const codeText = extractCodeText(codeBlock);
- const cm = codeMirrors[index];
- if (cm) {
- cm.setValue(codeText);
- console.log(`已填充代码块 ${index + 1}`);
- } else {
- try {
- const textarea = codeMirrors[index].querySelector('textarea');
- if (textarea) {
- textarea.value = codeText;
- console.log(`使用替代方法填充代码块 ${index + 1}`);
- }
- } catch (e) {
- console.error(`无法填充代码块 ${index + 1}:`, e);
- }
- }
- }
- });
-
- // 点击保存按钮
- console.log('准备点击保存按钮...');
- showNotification('正在保存笔记本...', 'info');
- const saveButton = document.querySelector('.btn-wrap .btn-name') ||
- document.querySelector('[title="保存"]') ||
- [...document.querySelectorAll('button')].find(btn =>
- btn.textContent.includes('保存'));
- if (saveButton) {
- saveButton.click();
- console.log('已点击保存按钮');
- // 等待保存成功后重启
- waitForMessage(() => {
- console.log('准备点击重启按钮...');
- showNotification('正在重启笔记本...', 'info');
- const menuItems = [...document.querySelectorAll('.ivu-dropdown-item, [role="menuitem"], .menu-item')];
- const possibleTexts = ['重启 & 运行所有', '重启并运行所有', '重启&运行所有', '重启和运行所有'];
- const restartButton = menuItems.find(item => {
- const text = item.textContent.trim();
- return possibleTexts.some(pt => text.includes(pt));
- });
- if (restartButton) {
- console.log('找到重启按钮,点击中...');
- restartButton.click();
- waitForRestartModal();
- setButtonState(button, 'completed');
- } else {
- console.error('未找到重启按钮!');
- console.log('可用菜单项:');
- menuItems.forEach((item, i) => console.log(`${i+1}. "${item.textContent.trim()}"`));
- // 执行遇到错误,通知用户
- showNotification('自动化执行失败:未找到重启按钮!', 'error');
- setButtonState(button, 'failed');
- }
- });
- } else {
- console.error('未找到保存按钮!');
- // 执行遇到错误,通知用户
- showNotification('自动化执行失败:未找到保存按钮!', 'error');
- setButtonState(button, 'failed');
- }
+ if (type === 'success') {
+ iconHTML = '${message}`;
+ notification.style.cssText = `
+ position: fixed;
+ right: 300px;
+ padding: 15px 20px;
+ background: white;
+ color: #333;
+ border-radius: 6px;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.15);
+ z-index: 10000;
+ max-width: 350px;
+ font-size: 14px;
+ border-left-color: ${borderColor};
+ display: flex;
+ align-items: center;
+ transition: top 0.3s ease;
+ `;
+ document.body.appendChild(notification);
+ NotificationManager.add(notification);
+
+ let timeoutId;
+ notification.addEventListener('mouseenter', () => clearTimeout(timeoutId));
+ notification.addEventListener('mouseleave', () => startHideTimeout());
+
+ function startHideTimeout() {
+ timeoutId = setTimeout(() => {
+ notification.classList.add('hiding');
+ setTimeout(() => {
+ notification.remove();
+ NotificationManager.remove(notification);
+ }, 500);
+ }, duration); // 使用传递的 duration 参数控制显示时间
+ }
+ startHideTimeout();
+
+ notification.addEventListener('click', () => {
+ clearTimeout(timeoutId);
+ notification.classList.add('hiding');
+ setTimeout(() => {
+ notification.remove();
+ NotificationManager.remove(notification);
+ }, 500);
+ });
+ return notification;
}
// 创建悬浮按钮
@@ -320,8 +346,8 @@
button.innerHTML = '运行自动化';
button.style.cssText = `
position: fixed;
- top: 20px;
- right: 300px; /* 距离右侧300像素 */
+ top: 0px;
+ right: 300px;
z-index: 10000;
padding: 12px 20px;
background-color: #2196F3;
@@ -338,12 +364,14 @@
min-width: 140px;
font-weight: 600;
`;
- // 鼠标悬停效果,缓慢放大并加深颜色
+
+ // 鼠标悬停效果
button.addEventListener('mouseover', () => {
button.style.transform = 'translateY(-2px)';
button.style.boxShadow = '0 5px 15px rgba(33, 150, 243, 0.4)';
button.style.backgroundColor = '#1976D2';
});
+
button.addEventListener('mouseout', () => {
button.style.transform = '';
button.style.boxShadow = '0 3px 10px rgba(33, 150, 243, 0.3)';
@@ -351,7 +379,8 @@
button.style.backgroundColor = '#2196F3';
}
});
- // 添加点击波纹效果
+
+ // 点击波纹效果
button.addEventListener('mousedown', (e) => {
const ripple = document.createElement('span');
const rect = button.getBoundingClientRect();
@@ -375,12 +404,14 @@
ripple.remove();
}, 600);
});
+
// 点击事件
button.addEventListener('click', () => {
if (!button.disabled) {
runAutomation();
}
});
+
document.body.appendChild(button);
return button;
}
@@ -391,7 +422,7 @@
const button = createFloatingButton();
// 显示欢迎通知
setTimeout(() => {
- showNotification('海豚自动化工具已加载,点击按钮开始自动化操作', 'info');
+ showNotification('海豚自动化工具 v1.3 已加载,点击按钮开始自动化操作', 'info', 5000); // 通知显示 5 秒
}, 1000);
}
';
+ borderColor = '#4CAF50';
+ } else if (type === 'error') {
+ iconHTML = ' ';
+ borderColor = '#F44336';
+ } else if (type === 'warning') {
+ iconHTML = ' ';
+ borderColor = '#FFA000';
} else {
- console.error("未找到足够的代码块或编辑器!");
- // 执行遇到错误,通知用户
- showNotification('自动化执行失败:未找到足够的代码块或编辑器!', 'error');
- setButtonState(button, 'failed');
+ iconHTML = ' ';
+ borderColor = '#2196F3';
}
+
+ notification.innerHTML = iconHTML + `