Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
a3032692b2 | |||
40a27eaa09 |
494
AutoHaiTun.js
494
AutoHaiTun.js
@ -1,12 +1,12 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 海豚自动化工具
|
// @name 海豚自动化工具
|
||||||
// @namespace http://tampermonkey.net/
|
// @namespace http://tampermonkey.net/
|
||||||
// @version 1.2
|
// @version 1.3
|
||||||
// @description 自动从代码块复制内容到编辑器,保存并重启笔记本
|
// @description 自动从代码块复制内容到编辑器,保存并重启笔记本
|
||||||
// @author Kakune55 Carryc
|
// @author Kakune55 Carryc
|
||||||
// @match https://labs.wd.dolphin-labs.com/data-analysis/*
|
// @match https://labs.wd.dolphin-labs.com/data-analysis/*
|
||||||
// @grant none
|
// @grant none
|
||||||
// @run-at document-end
|
// @run-at document-idle
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
@ -15,49 +15,30 @@
|
|||||||
// 通知管理器 - 管理多个通知的位置
|
// 通知管理器 - 管理多个通知的位置
|
||||||
const NotificationManager = {
|
const NotificationManager = {
|
||||||
notifications: [],
|
notifications: [],
|
||||||
baseTop: 70, // 初始顶部位置
|
baseTop: 45, // 初始顶部位置
|
||||||
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 +52,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,52 +83,216 @@
|
|||||||
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', 6000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取代码文本
|
// 查找所有目标代码块(包括“代码示例”和练习答案)
|
||||||
function extractCodeText(codeBlock) {
|
function findAllCodeBlocks() {
|
||||||
return codeBlock.innerText ||
|
const textCellWraps = Array.from(document.querySelectorAll('.text-cell-wrap'));
|
||||||
[...codeBlock.querySelectorAll('span')].reduce((text, span) =>
|
|
||||||
text + span.textContent, '');
|
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') {
|
function showNotification(message, type = 'info', duration = 3000) {
|
||||||
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';
|
||||||
} else if (type === 'error') {
|
} else if (type === 'error') {
|
||||||
iconHTML = '<div class="notification-icon">❌</div>';
|
iconHTML = '<div class="notification-icon">❌</div>';
|
||||||
borderColor = '#F44336';
|
borderColor = '#F44336';
|
||||||
|
} else if (type === 'warning') {
|
||||||
|
iconHTML = '<div class="notification-icon">⚠️</div>';
|
||||||
|
borderColor = '#FFA000';
|
||||||
} else {
|
} else {
|
||||||
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;
|
||||||
@ -217,38 +310,24 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transition: top 0.3s ease;
|
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);
|
}, duration); // 使用传递的 duration 参数控制显示时间
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始隐藏倒计时
|
|
||||||
startHideTimeout();
|
startHideTimeout();
|
||||||
|
|
||||||
// 添加点击关闭功能
|
|
||||||
notification.addEventListener('click', () => {
|
notification.addEventListener('click', () => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
notification.classList.add('hiding');
|
notification.classList.add('hiding');
|
||||||
@ -257,156 +336,9 @@
|
|||||||
NotificationManager.remove(notification);
|
NotificationManager.remove(notification);
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
return notification;
|
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() {
|
function createFloatingButton() {
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
@ -414,8 +346,8 @@
|
|||||||
button.innerHTML = '运行自动化';
|
button.innerHTML = '运行自动化';
|
||||||
button.style.cssText = `
|
button.style.cssText = `
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 20px;
|
top: 0px;
|
||||||
right: 300px; /* 距离右侧300像素 */
|
right: 300px;
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
background-color: #2196F3;
|
background-color: #2196F3;
|
||||||
@ -432,14 +364,14 @@
|
|||||||
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,15 +379,14 @@
|
|||||||
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');
|
||||||
const rect = button.getBoundingClientRect();
|
const rect = button.getBoundingClientRect();
|
||||||
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 +399,19 @@
|
|||||||
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,10 +420,9 @@
|
|||||||
function init() {
|
function init() {
|
||||||
addStyles();
|
addStyles();
|
||||||
const button = createFloatingButton();
|
const button = createFloatingButton();
|
||||||
|
|
||||||
// 显示欢迎通知
|
// 显示欢迎通知
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showNotification('海豚自动化工具已加载,点击按钮开始自动化操作', 'info');
|
showNotification('海豚自动化工具 v1.3 已加载,点击按钮开始自动化操作', 'info', 5000); // 通知显示 5 秒
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user