Haitun-Autojs/AutoHaiTun.js

503 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ==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 = '<div class="notification-icon">✅</div>';
borderColor = '#4CAF50';
} else if (type === 'error') {
iconHTML = '<div class="notification-icon">❌</div>';
borderColor = '#F44336';
} else {
iconHTML = '<div class="notification-icon"></div>';
borderColor = '#2196F3';
}
notification.innerHTML = iconHTML + `<div>${message}</div>`;
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: '<div class="loader"></div> 执行中'
},
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);
})();