fix:修复了部分bug

This commit is contained in:
Carrycyebo 2025-03-27 16:18:37 +08:00
parent 4869f626c9
commit 40a27eaa09

View File

@ -8,7 +8,6 @@
// @grant none // @grant none
// @run-at document-end // @run-at document-end
// ==/UserScript== // ==/UserScript==
(function() { (function() {
'use strict'; 'use strict';
@ -17,47 +16,28 @@
notifications: [], notifications: [],
baseTop: 70, // 初始顶部位置 baseTop: 70, // 初始顶部位置
gap: 10, // 通知间距 gap: 10, // 通知间距
// 添加通知到管理器
add: function(element) { add: function(element) {
// 计算新通知的顶部位置
let topPosition = this.baseTop; let topPosition = this.baseTop;
// 为已有的每个通知增加高度和间隔
this.notifications.forEach(notification => { this.notifications.forEach(notification => {
if (notification.isConnected) { // 确保通知仍在DOM中 if (notification.isConnected) {
const height = notification.offsetHeight; const height = notification.offsetHeight;
topPosition += height + this.gap; topPosition += height + this.gap;
} }
}); });
// 过滤掉已移除的通知
this.notifications = this.notifications.filter(n => n.isConnected); this.notifications = this.notifications.filter(n => n.isConnected);
// 设置新通知位置
element.style.top = topPosition + 'px'; element.style.top = topPosition + 'px';
// 添加到管理器
this.notifications.push(element); this.notifications.push(element);
return element; return element;
}, },
// 从管理器移除通知
remove: function(element) { remove: function(element) {
const index = this.notifications.indexOf(element); const index = this.notifications.indexOf(element);
if (index > -1) { if (index > -1) {
this.notifications.splice(index, 1); this.notifications.splice(index, 1);
} }
// 重新计算剩余通知的位置
this.updatePositions(); this.updatePositions();
}, },
// 更新所有通知的位置
updatePositions: function() { updatePositions: function() {
let topPosition = this.baseTop; let topPosition = this.baseTop;
this.notifications.forEach(notification => { this.notifications.forEach(notification => {
if (notification.isConnected) { if (notification.isConnected) {
notification.style.top = topPosition + 'px'; notification.style.top = topPosition + 'px';
@ -71,69 +51,17 @@
function addStyles() { function addStyles() {
const styleSheet = document.createElement('style'); const styleSheet = document.createElement('style');
styleSheet.textContent = ` styleSheet.textContent = `
@keyframes slideInRight { @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
from { transform: translateX(100%); opacity: 0; } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } }
to { transform: translateX(0); opacity: 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 pulse { @keyframes ripple { to { transform: scale(3); opacity: 0; } }
0% { transform: scale(1); } #dolphin-auto-btn { animation: slideInRight 0.5s ease-out; }
50% { transform: scale(1.05); } .dolphin-notification { animation: fadeIn 0.3s ease-out; display: flex; align-items: center; border-left: 4px solid; }
100% { transform: scale(1); } .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; }
@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); document.head.appendChild(styleSheet);
} }
@ -154,17 +82,13 @@
console.log('等待重启确认模态框...'); console.log('等待重启确认模态框...');
const checkModal = setInterval(() => { const checkModal = setInterval(() => {
const modals = [...document.querySelectorAll('.ivu-modal')]; const modals = [...document.querySelectorAll('.ivu-modal')];
const targetModal = modals.find(modal => const targetModal = modals.find(modal => modal.textContent.includes('重新启动内核并重新运行整个笔记本'));
modal.textContent.includes('重新启动内核并重新运行整个笔记本'));
if (targetModal) { if (targetModal) {
const confirmButton = targetModal.querySelector('.ivu-btn-primary'); const confirmButton = targetModal.querySelector('.ivu-btn-primary');
if (confirmButton) { if (confirmButton) {
confirmButton.click(); confirmButton.click();
console.log('已点击"确定"按钮'); console.log('已点击"确定"按钮');
clearInterval(checkModal); clearInterval(checkModal);
// 执行完成后通知用户
setTimeout(() => { setTimeout(() => {
showNotification('自动化执行完成:代码已复制、保存并重启', 'success'); showNotification('自动化执行完成:代码已复制、保存并重启', 'success');
}, 1000); }, 1000);
@ -175,20 +99,15 @@
// 提取代码文本 // 提取代码文本
function extractCodeText(codeBlock) { function extractCodeText(codeBlock) {
return codeBlock.innerText || return codeBlock.innerText || [...codeBlock.querySelectorAll('span')].reduce((text, span) => text + span.textContent, '');
[...codeBlock.querySelectorAll('span')].reduce((text, span) =>
text + span.textContent, '');
} }
// 显示美化的通知 // 显示美化的通知
function showNotification(message, type = 'info') { function showNotification(message, type = 'info') {
const notification = document.createElement('div'); const notification = document.createElement('div');
notification.className = 'dolphin-notification'; notification.className = 'dolphin-notification';
let iconHTML = ''; let iconHTML = '';
let borderColor = ''; let borderColor = '';
// 根据类型设置不同的图标和颜色
if (type === 'success') { if (type === 'success') {
iconHTML = '<div class="notification-icon">✅</div>'; iconHTML = '<div class="notification-icon">✅</div>';
borderColor = '#4CAF50'; borderColor = '#4CAF50';
@ -199,56 +118,30 @@
iconHTML = '<div class="notification-icon"></div>'; iconHTML = '<div class="notification-icon"></div>';
borderColor = '#2196F3'; borderColor = '#2196F3';
} }
notification.innerHTML = iconHTML + `<div>${message}</div>`; notification.innerHTML = iconHTML + `<div>${message}</div>`;
notification.style.cssText = ` notification.style.cssText = `
position: fixed; position: fixed; right: 300px; padding: 15px 20px; background: white; color: #333; border-radius: 6px;
right: 300px; box-shadow: 0 4px 15px rgba(0,0,0,0.15); z-index: 10000; max-width: 350px; font-size: 14px; border-left-color: ${borderColor};
padding: 15px 20px; display: flex; align-items: center; transition: top 0.3s ease;
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); document.body.appendChild(notification);
// 通过通知管理器添加并定位通知
NotificationManager.add(notification); NotificationManager.add(notification);
// 鼠标悬停时停止自动隐藏
let timeoutId; let timeoutId;
notification.addEventListener('mouseenter', () => clearTimeout(timeoutId));
notification.addEventListener('mouseenter', () => { notification.addEventListener('mouseleave', () => startHideTimeout());
clearTimeout(timeoutId);
});
notification.addEventListener('mouseleave', () => {
startHideTimeout();
});
function startHideTimeout() { function startHideTimeout() {
timeoutId = setTimeout(() => { timeoutId = setTimeout(() => {
notification.classList.add('hiding'); notification.classList.add('hiding');
setTimeout(() => { setTimeout(() => {
notification.remove(); notification.remove();
// 从管理器中移除并重新排列其他通知
NotificationManager.remove(notification); NotificationManager.remove(notification);
}, 500); }, 500);
}, 3000); }, 3000);
} }
// 开始隐藏倒计时
startHideTimeout(); startHideTimeout();
// 添加点击关闭功能
notification.addEventListener('click', () => { notification.addEventListener('click', () => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
notification.classList.add('hiding'); notification.classList.add('hiding');
@ -257,84 +150,104 @@
NotificationManager.remove(notification); NotificationManager.remove(notification);
}, 500); }, 500);
}); });
return notification; return notification;
} }
// 设置按钮状态 // 设置按钮状态
function setButtonState(button, state) { function setButtonState(button, state) {
if (!button) return; if (!button) return;
// 定义不同状态的样式
const states = { const states = {
idle: { idle: { text: '运行自动化', backgroundColor: '#2196F3', disabled: false, html: '运行自动化' },
text: '运行自动化', running: { text: '执行中', backgroundColor: '#FFA000', disabled: true, html: '<div class="loader"></div> 执行中' },
backgroundColor: '#2196F3', completed: { text: '已完成', backgroundColor: '#4CAF50', disabled: true, html: '✅ 已完成' },
disabled: false, failed: { text: '执行失败', backgroundColor: '#F44336', disabled: false, html: '❌ 重试' }
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]; const newState = states[state];
// 应用过渡效果
button.style.transition = 'all 0.3s ease'; button.style.transition = 'all 0.3s ease';
button.disabled = newState.disabled; button.disabled = newState.disabled;
button.innerHTML = newState.html; button.innerHTML = newState.html;
button.style.backgroundColor = newState.backgroundColor; button.style.backgroundColor = newState.backgroundColor;
// 如果是完成状态,添加脉冲动画
if (state === 'completed') { if (state === 'completed') {
button.style.animation = 'pulse 0.5s ease-in-out'; button.style.animation = 'pulse 0.5s ease-in-out';
// 3秒后恢复到初始状态
setTimeout(() => setButtonState(button, 'idle'), 3000); setTimeout(() => setButtonState(button, 'idle'), 3000);
} }
} }
// 查找所有目标代码块(包括“代码示例”和练习答案)
function findAllCodeBlocks() {
// 找到所有包含“代码示例”的父容器
const textCellWraps = Array.from(document.querySelectorAll('.text-cell-wrap'))
.filter(cellWrap => cellWrap.querySelector('.alert.alert-success.alertsuccess')?.textContent.includes('代码示例'));
// 收集紧跟在“代码示例”后面的 <pre><code> 元素
const exampleCodeBlocks = [];
// 辅助函数:递归查找 <pre><code>
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(`未找到“代码示例”代码块!`);
}
});
// 找到所有练习答案区域中的 <pre><code>
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} 中未找到 <pre><code>`);
}
});
// 合并所有代码块
return [...exampleCodeBlocks, ...answerCodeBlocks];
}
// 主函数 // 主函数
function runAutomation() { function runAutomation() {
// 修改按钮状态
const button = document.getElementById('dolphin-auto-btn'); const button = document.getElementById('dolphin-auto-btn');
setButtonState(button, 'running'); setButtonState(button, 'running');
// 1. 查找代码块 // 查找所有目标代码块
let codeBlocks = document.querySelectorAll('pre > code.cm-s-ipython[class*="language-"]') || const allCodeBlocks = findAllCodeBlocks();
document.querySelectorAll('pre > code[class*="language-"]') || console.log(`共找到 ${allCodeBlocks.length} 个有效代码块:`);
document.querySelectorAll('pre > code'); allCodeBlocks.forEach((codeBlock, index) => {
console.log(`代码块 ${index + 1}:`, codeBlock.textContent.trim());
});
console.log(`找到 ${codeBlocks.length} 个代码块`); // 查找所有 CodeMirror 编辑器实例
const codeMirrors = Array.from(document.querySelectorAll('.CodeMirror'))
.map(cmElement => cmElement.CodeMirror)
.filter(cmInstance => !!cmInstance);
// 2. 查找编辑器 console.log(`找到 ${codeMirrors.length} 个 CodeMirror 实例:`);
const codeMirrors = document.querySelectorAll('.CodeMirror');
console.log(`找到 ${codeMirrors.length} 个编辑器`);
if (codeBlocks.length > 0 && codeMirrors.length > 0) { if (allCodeBlocks.length > 0 && codeMirrors.length > 0) {
showNotification(`正在填充 ${Math.min(codeBlocks.length, codeMirrors.length)} 个代码块...`, 'info'); showNotification(`正在填充 ${Math.min(allCodeBlocks.length, codeMirrors.length)} 个代码块...`, 'info');
// 3. 将代码块内容复制到编辑器 // 将代码块内容复制到编辑器
codeBlocks.forEach((codeBlock, index) => { allCodeBlocks.forEach((codeBlock, index) => {
if (index < codeMirrors.length) { if (index < codeMirrors.length) {
const codeText = extractCodeText(codeBlock); const codeText = extractCodeText(codeBlock);
const cm = codeMirrors[index].CodeMirror; const cm = codeMirrors[index];
if (cm) { if (cm) {
cm.setValue(codeText); cm.setValue(codeText);
console.log(`已填充代码块 ${index + 1}`); console.log(`已填充代码块 ${index + 1}`);
@ -352,32 +265,26 @@
} }
}); });
// 4. 点击保存按钮 // 点击保存按钮
console.log('准备点击保存按钮...'); console.log('准备点击保存按钮...');
showNotification('正在保存笔记本...', 'info'); showNotification('正在保存笔记本...', 'info');
const saveButton = document.querySelector('.btn-wrap .btn-name') || const saveButton = document.querySelector('.btn-wrap .btn-name') ||
document.querySelector('[title="保存"]') || document.querySelector('[title="保存"]') ||
[...document.querySelectorAll('button')].find(btn => [...document.querySelectorAll('button')].find(btn =>
btn.textContent.includes('保存')); btn.textContent.includes('保存'));
if (saveButton) { if (saveButton) {
saveButton.click(); saveButton.click();
console.log('已点击保存按钮'); console.log('已点击保存按钮');
// 等待保存成功后重启
// 5. 等待保存成功后重启
waitForMessage(() => { waitForMessage(() => {
console.log('准备点击重启按钮...'); console.log('准备点击重启按钮...');
showNotification('正在重启笔记本...', 'info'); showNotification('正在重启笔记本...', 'info');
const menuItems = [...document.querySelectorAll('.ivu-dropdown-item, [role="menuitem"], .menu-item')]; const menuItems = [...document.querySelectorAll('.ivu-dropdown-item, [role="menuitem"], .menu-item')];
const possibleTexts = ['重启 & 运行所有', '重启并运行所有', '重启&运行所有', '重启和运行所有']; const possibleTexts = ['重启 & 运行所有', '重启并运行所有', '重启&运行所有', '重启和运行所有'];
const restartButton = menuItems.find(item => { const restartButton = menuItems.find(item => {
const text = item.textContent.trim(); const text = item.textContent.trim();
return possibleTexts.some(pt => text.includes(pt)); return possibleTexts.some(pt => text.includes(pt));
}); });
if (restartButton) { if (restartButton) {
console.log('找到重启按钮,点击中...'); console.log('找到重启按钮,点击中...');
restartButton.click(); restartButton.click();
@ -387,7 +294,6 @@
console.error('未找到重启按钮!'); console.error('未找到重启按钮!');
console.log('可用菜单项:'); console.log('可用菜单项:');
menuItems.forEach((item, i) => console.log(`${i+1}. "${item.textContent.trim()}"`)); menuItems.forEach((item, i) => console.log(`${i+1}. "${item.textContent.trim()}"`));
// 执行遇到错误,通知用户 // 执行遇到错误,通知用户
showNotification('自动化执行失败:未找到重启按钮!', 'error'); showNotification('自动化执行失败:未找到重启按钮!', 'error');
setButtonState(button, 'failed'); setButtonState(button, 'failed');
@ -432,14 +338,12 @@
min-width: 140px; min-width: 140px;
font-weight: 600; font-weight: 600;
`; `;
// 鼠标悬停效果,缓慢放大并加深颜色 // 鼠标悬停效果,缓慢放大并加深颜色
button.addEventListener('mouseover', () => { button.addEventListener('mouseover', () => {
button.style.transform = 'translateY(-2px)'; button.style.transform = 'translateY(-2px)';
button.style.boxShadow = '0 5px 15px rgba(33, 150, 243, 0.4)'; button.style.boxShadow = '0 5px 15px rgba(33, 150, 243, 0.4)';
button.style.backgroundColor = '#1976D2'; button.style.backgroundColor = '#1976D2';
}); });
button.addEventListener('mouseout', () => { button.addEventListener('mouseout', () => {
button.style.transform = ''; button.style.transform = '';
button.style.boxShadow = '0 3px 10px rgba(33, 150, 243, 0.3)'; button.style.boxShadow = '0 3px 10px rgba(33, 150, 243, 0.3)';
@ -447,7 +351,6 @@
button.style.backgroundColor = '#2196F3'; button.style.backgroundColor = '#2196F3';
} }
}); });
// 添加点击波纹效果 // 添加点击波纹效果
button.addEventListener('mousedown', (e) => { button.addEventListener('mousedown', (e) => {
const ripple = document.createElement('span'); const ripple = document.createElement('span');
@ -455,7 +358,6 @@
const size = Math.max(rect.width, rect.height); const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2; const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2; const y = e.clientY - rect.top - size / 2;
ripple.style.cssText = ` ripple.style.cssText = `
position: absolute; position: absolute;
top: ${y}px; top: ${y}px;
@ -468,21 +370,17 @@
animation: ripple 0.6s linear; animation: ripple 0.6s linear;
pointer-events: none; pointer-events: none;
`; `;
button.appendChild(ripple); button.appendChild(ripple);
setTimeout(() => { setTimeout(() => {
ripple.remove(); ripple.remove();
}, 600); }, 600);
}); });
// 点击事件 // 点击事件
button.addEventListener('click', () => { button.addEventListener('click', () => {
if (!button.disabled) { if (!button.disabled) {
runAutomation(); runAutomation();
} }
}); });
document.body.appendChild(button); document.body.appendChild(button);
return button; return button;
} }
@ -491,7 +389,6 @@
function init() { function init() {
addStyles(); addStyles();
const button = createFloatingButton(); const button = createFloatingButton();
// 显示欢迎通知 // 显示欢迎通知
setTimeout(() => { setTimeout(() => {
showNotification('海豚自动化工具已加载,点击按钮开始自动化操作', 'info'); showNotification('海豚自动化工具已加载,点击按钮开始自动化操作', 'info');