Haitun-Autojs/AutoHaiTun.js

431 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.3
// @description 自动从代码块复制内容到编辑器,保存并重启笔记本
// @author Kakune55 Carryc
// @match https://labs.wd.dolphin-labs.com/data-analysis/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// 通知管理器 - 管理多个通知的位置
const NotificationManager = {
notifications: [],
baseTop: 45, // 初始顶部位置
gap: 10, // 通知间距
add: function(element) {
let topPosition = this.baseTop;
this.notifications.forEach(notification => {
if (notification.isConnected) {
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 { from { transform: rotate(0deg); } to { 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', 6000);
}, 1000);
}
}
}, 200);
}
// 查找所有目标代码块(包括“代码示例”和练习答案)
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 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'); // 修改按钮状态为“失败”
}
}
// 辅助函数:查找保存按钮
function findSaveButton() {
return document.querySelector('.btn-wrap .btn-name') ||
document.querySelector('[title="保存"]') ||
[...document.querySelectorAll('button')].find(btn => btn.textContent.includes('保存'));
}
// 辅助函数:查找重启按钮
function findRestartButton() {
const menuItems = [...document.querySelectorAll('.ivu-dropdown-item, [role="menuitem"], .menu-item')];
const possibleTexts = ['重启 & 运行所有', '重启并运行所有', '重启&运行所有', '重启和运行所有'];
return menuItems.find(item => {
const text = item.textContent.trim();
return possibleTexts.some(pt => text.includes(pt));
});
}
// 设置按钮状态
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';
setTimeout(() => setButtonState(button, 'idle'), 3000);
}
}
// 显示美化的通知
function showNotification(message, type = 'info', duration = 3000) {
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 if (type === 'warning') {
iconHTML = '<div class="notification-icon">⚠️</div>';
borderColor = '#FFA000';
} 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);
}, duration); // 使用传递的 duration 参数控制显示时间
}
startHideTimeout();
notification.addEventListener('click', () => {
clearTimeout(timeoutId);
notification.classList.add('hiding');
setTimeout(() => {
notification.remove();
NotificationManager.remove(notification);
}, 500);
});
return notification;
}
// 创建悬浮按钮
function createFloatingButton() {
const button = document.createElement('button');
button.id = 'dolphin-auto-btn';
button.innerHTML = '运行自动化';
button.style.cssText = `
position: fixed;
top: 0px;
right: 300px;
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('海豚自动化工具 v1.3 已加载,点击按钮开始自动化操作', 'info', 5000); // 通知显示 5 秒
}, 1000);
}
// 页面加载后初始化
setTimeout(init, 1000);
})();