commit 4869f626c9a74f5d6340238263997ae59bda6e10 Author: kaku Date: Thu Mar 27 12:09:24 2025 +0800 feat:添加文件AutoHaitun.js diff --git a/AutoHaiTun.js b/AutoHaiTun.js new file mode 100644 index 0000000..73f0ef4 --- /dev/null +++ b/AutoHaiTun.js @@ -0,0 +1,503 @@ +// ==UserScript== +// @name 海豚自动化工具 +// @namespace http://tampermonkey.net/ +// @version 1.2 +// @description 自动从代码块复制内容到编辑器,保存并重启笔记本 +// @author Kakune55 Carryc +// @match https://labs.wd.dolphin-labs.com/data-analysis/* +// @grant none +// @run-at document-end +// ==/UserScript== + +(function() { + 'use strict'; + + // 通知管理器 - 管理多个通知的位置 + const NotificationManager = { + notifications: [], + baseTop: 70, // 初始顶部位置 + gap: 10, // 通知间距 + + // 添加通知到管理器 + add: function(element) { + // 计算新通知的顶部位置 + let topPosition = this.baseTop; + + // 为已有的每个通知增加高度和间隔 + this.notifications.forEach(notification => { + if (notification.isConnected) { // 确保通知仍在DOM中 + const height = notification.offsetHeight; + topPosition += height + this.gap; + } + }); + + // 过滤掉已移除的通知 + this.notifications = this.notifications.filter(n => n.isConnected); + + // 设置新通知位置 + element.style.top = topPosition + 'px'; + + // 添加到管理器 + this.notifications.push(element); + + return element; + }, + + // 从管理器移除通知 + remove: function(element) { + const index = this.notifications.indexOf(element); + if (index > -1) { + this.notifications.splice(index, 1); + } + + // 重新计算剩余通知的位置 + this.updatePositions(); + }, + + // 更新所有通知的位置 + updatePositions: function() { + let topPosition = this.baseTop; + + this.notifications.forEach(notification => { + if (notification.isConnected) { + notification.style.top = topPosition + 'px'; + topPosition += notification.offsetHeight + this.gap; + } + }); + } + }; + + // 添加样式到页面 + function addStyles() { + const styleSheet = document.createElement('style'); + styleSheet.textContent = ` + @keyframes slideInRight { + from { transform: translateX(100%); opacity: 0; } + to { transform: translateX(0); opacity: 1; } + } + + @keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } + } + + @keyframes fadeIn { + from { opacity: 0; transform: translateY(-20px); } + to { opacity: 1; transform: translateY(0); } + } + + @keyframes fadeOut { + from { opacity: 1; transform: translateY(0); } + to { opacity: 0; transform: translateY(-20px); } + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + @keyframes ripple { + to { + transform: scale(3); + opacity: 0; + } + } + + #dolphin-auto-btn { + animation: slideInRight 0.5s ease-out; + } + + .dolphin-notification { + animation: fadeIn 0.3s ease-out; + display: flex; + align-items: center; + border-left: 4px solid; + } + + .dolphin-notification.hiding { + animation: fadeOut 0.5s ease-in forwards; + } + + .notification-icon { + margin-right: 12px; + font-size: 20px; + } + + .loader { + display: inline-block; + width: 15px; + height: 15px; + border: 2px solid rgba(255,255,255,0.3); + border-radius: 50%; + border-top-color: #fff; + animation: spin 0.8s linear infinite; + margin-right: 8px; + } + `; + document.head.appendChild(styleSheet); + } + + // 等待保存成功消息 + function waitForMessage(callback) { + const messageDiv = document.querySelector('.ivu-message'); + if (messageDiv && messageDiv.textContent.includes('保存成功')) { + console.log('保存成功'); + callback(); + } else { + setTimeout(() => waitForMessage(callback), 100); + } + } + + // 等待模态框并点击确认 + function waitForRestartModal() { + console.log('等待重启确认模态框...'); + const checkModal = setInterval(() => { + const modals = [...document.querySelectorAll('.ivu-modal')]; + 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'); + }, 1000); + } + } + }, 200); + } + + // 提取代码文本 + function extractCodeText(codeBlock) { + return codeBlock.innerText || + [...codeBlock.querySelectorAll('span')].reduce((text, span) => + text + span.textContent, ''); + } + + // 显示美化的通知 + 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'; + } + + notification.innerHTML = 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); + }, 3000); + } + + // 开始隐藏倒计时 + startHideTimeout(); + + // 添加点击关闭功能 + notification.addEventListener('click', () => { + clearTimeout(timeoutId); + notification.classList.add('hiding'); + setTimeout(() => { + notification.remove(); + NotificationManager.remove(notification); + }, 500); + }); + + return notification; + } + + // 设置按钮状态 + function setButtonState(button, state) { + if (!button) return; + + // 定义不同状态的样式 + const states = { + idle: { + text: '运行自动化', + backgroundColor: '#2196F3', + disabled: false, + html: '运行自动化' + }, + running: { + text: '执行中', + backgroundColor: '#FFA000', + disabled: true, + html: '
执行中' + }, + completed: { + text: '已完成', + backgroundColor: '#4CAF50', + disabled: true, + html: '✅ 已完成' + }, + failed: { + text: '执行失败', + backgroundColor: '#F44336', + disabled: false, + html: '❌ 重试' + } + }; + + const newState = states[state]; + + // 应用过渡效果 + button.style.transition = 'all 0.3s ease'; + button.disabled = newState.disabled; + button.innerHTML = newState.html; + button.style.backgroundColor = newState.backgroundColor; + + // 如果是完成状态,添加脉冲动画 + if (state === 'completed') { + button.style.animation = 'pulse 0.5s ease-in-out'; + // 3秒后恢复到初始状态 + setTimeout(() => setButtonState(button, 'idle'), 3000); + } + } + + // 主函数 + function runAutomation() { + // 修改按钮状态 + const button = document.getElementById('dolphin-auto-btn'); + setButtonState(button, 'running'); + + // 1. 查找代码块 + let codeBlocks = document.querySelectorAll('pre > code.cm-s-ipython[class*="language-"]') || + document.querySelectorAll('pre > code[class*="language-"]') || + document.querySelectorAll('pre > code'); + + console.log(`找到 ${codeBlocks.length} 个代码块`); + + // 2. 查找编辑器 + const codeMirrors = document.querySelectorAll('.CodeMirror'); + console.log(`找到 ${codeMirrors.length} 个编辑器`); + + if (codeBlocks.length > 0 && codeMirrors.length > 0) { + showNotification(`正在填充 ${Math.min(codeBlocks.length, codeMirrors.length)} 个代码块...`, 'info'); + + // 3. 将代码块内容复制到编辑器 + codeBlocks.forEach((codeBlock, index) => { + if (index < codeMirrors.length) { + const codeText = extractCodeText(codeBlock); + const cm = codeMirrors[index].CodeMirror; + + 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); + } + } + } + }); + + // 4. 点击保存按钮 + 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('已点击保存按钮'); + + // 5. 等待保存成功后重启 + 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'); + } + } else { + console.error("未找到足够的代码块或编辑器!"); + // 执行遇到错误,通知用户 + showNotification('自动化执行失败:未找到足够的代码块或编辑器!', 'error'); + setButtonState(button, 'failed'); + } + } + + // 创建悬浮按钮 + function createFloatingButton() { + const button = document.createElement('button'); + button.id = 'dolphin-auto-btn'; + button.innerHTML = '运行自动化'; + button.style.cssText = ` + position: fixed; + top: 20px; + right: 300px; /* 距离右侧300像素 */ + z-index: 10000; + padding: 12px 20px; + background-color: #2196F3; + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + box-shadow: 0 3px 10px rgba(33, 150, 243, 0.3); + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + 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)'; + if (!button.disabled) { + button.style.backgroundColor = '#2196F3'; + } + }); + + // 添加点击波纹效果 + button.addEventListener('mousedown', (e) => { + const ripple = document.createElement('span'); + const rect = button.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const x = e.clientX - rect.left - size / 2; + const y = e.clientY - rect.top - size / 2; + + ripple.style.cssText = ` + position: absolute; + top: ${y}px; + left: ${x}px; + width: ${size}px; + height: ${size}px; + background-color: rgba(255, 255, 255, 0.7); + border-radius: 50%; + transform: scale(0); + animation: ripple 0.6s linear; + pointer-events: none; + `; + + button.appendChild(ripple); + + setTimeout(() => { + ripple.remove(); + }, 600); + }); + + // 点击事件 + button.addEventListener('click', () => { + if (!button.disabled) { + runAutomation(); + } + }); + + document.body.appendChild(button); + return button; + } + + // 初始化函数 + function init() { + addStyles(); + const button = createFloatingButton(); + + // 显示欢迎通知 + setTimeout(() => { + showNotification('海豚自动化工具已加载,点击按钮开始自动化操作', 'info'); + }, 1000); + } + + // 页面加载后初始化 + setTimeout(init, 1000); +})(); \ No newline at end of file