// ==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) { 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'); }, 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 + ` 元素
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');
}
} 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);
})();