// ==UserScript==
// @name 智学网阅卷助手 - 快捷键版
// @namespace http://tampermonkey.net/
// @version 1.3
// @description 智学网辅助阅卷工具,支持完全自定义快捷键、悬浮窗控制和快速回评功能
// @author YourName
// @match *://*.zhixue.com/*
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// @license Elegy挽歌
// ==/UserScript==
(function() {
'use strict';
// 焦点控制标志
let isFocusLocked = false;
// 默认配置
const defaultConfig = {
scoreShortcuts: {
'1': 0, // 数字键1对应0分
'2': 1, // 数字键2对应1分
'3': 2, // 数字键3对应2分
'4': 3, // 数字键4对应3分
'5': 4, // 数字键5对应4分
'6': 5, // 数字键6对应5分
'0': 0, // 0键对应0分
'q': 1, // Q键对应1分
'w': 2, // W键对应2分
'e': 3, // E键对应3分
'r': 4, // R键对应4分
't': 5, // T键对应5分
},
prevShortcut: 'Backspace', // 回评上一份试卷的快捷键
autoCheck: true, // 是否自动勾选
theme: 'dark', // 默认主题
focusLock: true // 焦点锁定
};
// 加载用户配置
let config = Object.assign({}, defaultConfig, GM_getValue('config', {}));
// 添加全局样式
GM_addStyle(`
#assistant-container {
position: fixed;
top: 20px;
right: 20px;
width: 320px;
background: ${config.theme === 'dark' ? '#2d3748' : '#ffffff'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#2d3748'};
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
z-index: 99999;
overflow: hidden;
transition: all 0.3s ease;
font-family: 'Segoe UI', system-ui, sans-serif;
border: 1px solid ${config.theme === 'dark' ? '#4a5568' : '#e2e8f0'};
}
#assistant-header {
background: ${config.theme === 'dark' ? '#1a202c' : '#f7fafc'};
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
border-bottom: 1px solid ${config.theme === 'dark' ? '#4a5568' : '#e2e8f0'};
}
#assistant-title {
font-weight: 600;
font-size: 16px;
display: flex;
align-items: center;
gap: 8px;
}
#assistant-title svg {
width: 20px;
height: 20px;
fill: ${config.theme === 'dark' ? '#63b3ed' : '#4299e1'};
}
#assistant-controls {
display: flex;
gap: 10px;
}
.assistant-btn {
background: none;
border: none;
width: 28px;
height: 28px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: ${config.theme === 'dark' ? '#cbd5e0' : '#718096'};
transition: all 0.2s;
}
.assistant-btn:hover {
background: ${config.theme === 'dark' ? '#4a5568' : '#edf2f7'};
color: ${config.theme === 'dark' ? '#ffffff' : '#2d3748'};
}
#assistant-content {
padding: 15px;
max-height: 500px;
overflow-y: auto;
}
.section {
margin-bottom: 20px;
}
.section-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 6px;
}
.section-title svg {
width: 16px;
height: 16px;
fill: ${config.theme === 'dark' ? '#63b3ed' : '#4299e1'};
}
.shortcut-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-bottom: 15px;
}
.shortcut-item {
background: ${config.theme === 'dark' ? '#4a5568' : '#edf2f7'};
border-radius: 8px;
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.2s;
position: relative;
}
.shortcut-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.shortcut-key {
width: 36px;
height: 36px;
background: ${config.theme === 'dark' ? '#63b3ed' : '#4299e1'};
color: white;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 16px;
margin-bottom: 8px;
}
.shortcut-value {
font-size: 14px;
}
.shortcut-delete {
position: absolute;
top: -6px;
right: -6px;
width: 20px;
height: 20px;
background: #e53e3e;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s;
}
.shortcut-item:hover .shortcut-delete {
opacity: 1;
}
.divider {
height: 1px;
background: ${config.theme === 'dark' ? '#4a5568' : '#e2e8f0'};
margin: 15px 0;
}
.action-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: ${config.theme === 'dark' ? '#4a5568' : '#edf2f7'};
border-radius: 8px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s;
}
.action-item:hover {
background: ${config.theme === 'dark' ? '#5a6578' : '#e2e8f0'};
}
.action-label {
display: flex;
align-items: center;
gap: 8px;
}
.action-key {
background: ${config.theme === 'dark' ? '#2d3748' : '#cbd5e0'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#2d3748'};
padding: 3px 8px;
border-radius: 4px;
font-size: 12px;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 40px;
height: 22px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #4299e1;
}
input:checked + .slider:before {
transform: translateX(18px);
}
.status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-left: 8px;
background-color: #48bb78;
}
.status-indicator.inactive {
background-color: #e53e3e;
}
.theme-toggle {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
}
.theme-label {
display: flex;
align-items: center;
gap: 8px;
}
.focus-notification {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: #e53e3e;
color: white;
padding: 10px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 100000;
animation: fadeInOut 2.5s forwards;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
}
@keyframes fadeInOut {
0% { opacity: 0; bottom: 10px; }
10% { opacity: 1; bottom: 20px; }
90% { opacity: 1; bottom: 20px; }
100% { opacity: 0; bottom: 30px; }
}
.custom-shortcut-form {
display: flex;
gap: 10px;
margin-top: 15px;
}
.key-input {
flex: 1;
padding: 8px 12px;
border-radius: 8px;
border: 1px solid ${config.theme === 'dark' ? '#4a5568' : '#cbd5e0'};
background: ${config.theme === 'dark' ? '#1a202c' : '#f7fafc'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#2d3748'};
outline: none;
}
.score-input {
width: 60px;
padding: 8px 12px;
border-radius: 8px;
border: 1px solid ${config.theme === 'dark' ? '#4a5568' : '#cbd5e0'};
background: ${config.theme === 'dark' ? '#1a202c' : '#f7fafc'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#2d3748'};
outline: none;
}
.add-btn {
padding: 8px 15px;
background: #4299e1;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
}
.add-btn:hover {
background: #3182ce;
}
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 99998;
display: flex;
align-items: center;
justify-content: center;
}
.key-modal {
background: ${config.theme === 'dark' ? '#2d3748' : '#ffffff'};
border-radius: 12px;
padding: 20px;
width: 300px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
}
.modal-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 15px;
text-align: center;
}
.key-input-large {
width: 100%;
padding: 12px;
border-radius: 8px;
border: 1px solid ${config.theme === 'dark' ? '#4a5568' : '#cbd5e0'};
background: ${config.theme === 'dark' ? '#1a202c' : '#f7fafc'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#2d3748'};
outline: none;
font-size: 16px;
text-align: center;
margin-bottom: 15px;
}
.modal-buttons {
display: flex;
gap: 10px;
}
.modal-btn {
flex: 1;
padding: 10px;
border-radius: 8px;
border: none;
cursor: pointer;
font-weight: 500;
}
.confirm-btn {
background: #4299e1;
color: white;
}
.cancel-btn {
background: ${config.theme === 'dark' ? '#4a5568' : '#e2e8f0'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#4a5568'};
}
.shortcut-help {
font-size: 12px;
color: ${config.theme === 'dark' ? '#a0aec0' : '#718096'};
margin-top: 5px;
text-align: center;
}
`);
// 创建悬浮窗容器
const container = document.createElement('div');
container.id = 'assistant-container';
container.innerHTML = `
<div id="assistant-header">
<div id="assistant-title">
<svg viewBox="0 0 24 24">
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/>
<path d="M7 10h10v2H7zm0 4h5v2H7z"/>
</svg>
智学网阅卷助手By.挽歌
<span class="status-indicator"></span>
</div>
<div id="assistant-controls">
<button class="assistant-btn" id="minimize-btn">
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M19 13H5v-2h14v2z"/>
</svg>
</button>
<button class="assistant-btn" id="close-btn">
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
</svg>
</button>
</div>
</div>
<div id="assistant-content">
<div class="section">
<div class="section-title">
<svg viewBox="0 0 24 24">
<path d="M3 17v2h6v-2H3zM3 5v2h10V5H3zm10 16v-2h8v-2h-8v-2h-2v6h2zM7 9v2H3v2h4v2h2V9H7zm14 4v-2H11v2h10zm-6-4h2V7h4V5h-4V3h-2v6z"/>
</svg>
评分快捷键配置
</div>
<div class="shortcut-grid" id="shortcut-grid"></div>
<div class="custom-shortcut-form">
<input type="text" class="key-input" id="new-key" placeholder="输入按键" readonly>
<input type="number" class="score-input" id="new-score" min="0" max="100" placeholder="分数">
<button class="add-btn" id="add-shortcut-btn">添加</button>
</div>
<div class="shortcut-help">点击输入框后按任意键设置按键</div>
</div>
<div class="divider"></div>
<div class="section">
<div class="section-title">
<svg viewBox="0 0 24 24">
<path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"/>
</svg>
功能快捷键
</div>
<div class="action-item" id="prev-paper-action">
<div class="action-label">
<svg viewBox="0 0 24 24" width="18" height="18">
<path d="M14 7l-5 5 5 5V7z"/>
</svg>
回评上一份试卷
</div>
<div class="action-key" id="prev-key">${config.prevShortcut}</div>
</div>
<div class="action-item" id="set-prev-key">
<div class="action-label">
<svg viewBox="0 0 24 24" width="18" height="18">
<path d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"/>
</svg>
设置回评快捷键
</div>
<div class="action-key">点击设置</div>
</div>
</div>
<div class="divider"></div>
<div class="section">
<div class="section-title">
<svg viewBox="0 0 24 24">
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/>
</svg>
焦点与显示设置
</div>
<div class="action-item">
<div class="action-label">
<svg viewBox="0 0 24 24" width="18" height="18">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
</svg>
焦点锁定功能
</div>
<label class="toggle-switch">
<input type="checkbox" id="focus-lock-toggle" ${config.focusLock ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
<div class="action-item">
<div class="action-label">
<svg viewBox="0 0 24 24" width="18" height="18">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
自动勾选阅卷辅助
</div>
<label class="toggle-switch">
<input type="checkbox" id="auto-check-toggle" ${config.autoCheck ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
<div class="theme-toggle">
<div class="theme-label">
<svg viewBox="0 0 24 24" width="18" height="18">
<path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/>
</svg>
界面主题
</div>
<div>
<button class="theme-btn ${config.theme === 'light' ? 'active' : ''}" data-theme="light">浅色</button>
<button class="theme-btn ${config.theme === 'dark' ? 'active' : ''}" data-theme="dark">深色</button>
</div>
</div>
</div>
<div class="divider"></div>
<div class="section">
<div class="section-title">
<svg viewBox="0 0 24 24">
<path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/>
</svg>
使用说明
</div>
<div style="font-size:13px; line-height:1.5; color: ${config.theme === 'dark' ? '#a0aec0' : '#718096'}">
<p>1. 使用自定义快捷键快速打分</p>
<p>2. 按<span class="action-key">${config.prevShortcut}</span>回评上一份试卷</p>
<p>3. 打分后自动进入下一份试卷</p>
<p>4. 拖动顶部栏可移动悬浮窗</p>
<p>5. 启用"焦点锁定"防止输入框抢占焦点</p>
</div>
</div>
</div>
`;
document.body.appendChild(container);
// 渲染快捷键网格
function renderShortcuts() {
const shortcutGrid = container.querySelector('#shortcut-grid');
shortcutGrid.innerHTML = '';
for (let key in config.scoreShortcuts) {
const score = config.scoreShortcuts[key];
const shortcutItem = document.createElement('div');
shortcutItem.className = 'shortcut-item';
shortcutItem.innerHTML = `
<div class="shortcut-key">${key}</div>
<div class="shortcut-value">${score} 分</div>
<div class="shortcut-delete" data-key="${key}">×</div>
`;
shortcutGrid.appendChild(shortcutItem);
// 添加删除事件
shortcutItem.querySelector('.shortcut-delete').addEventListener('click', (e) => {
e.stopPropagation();
delete config.scoreShortcuts[e.target.dataset.key];
saveConfig();
renderShortcuts();
showNotification(`已删除快捷键: ${e.target.dataset.key}`);
});
}
}
// 初始渲染快捷键
renderShortcuts();
// 添加主题按钮样式
GM_addStyle(`
.theme-btn {
padding: 5px 12px;
border: none;
border-radius: 6px;
background: ${config.theme === 'dark' ? '#4a5568' : '#e2e8f0'};
color: ${config.theme === 'dark' ? '#e2e8f0' : '#4a5568'};
cursor: pointer;
font-size: 13px;
margin-left: 8px;
transition: all 0.2s;
}
.theme-btn.active {
background: ${config.theme === 'dark' ? '#63b3ed' : '#4299e1'};
color: white;
}
.theme-btn:hover {
opacity: 0.9;
}
`);
// 拖拽功能
let isDragging = false;
let offsetX, offsetY;
const header = container.querySelector('#assistant-header');
header.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - container.getBoundingClientRect().left;
offsetY = e.clientY - container.getBoundingClientRect().top;
container.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
// 边界检查
const maxX = window.innerWidth - container.offsetWidth;
const maxY = window.innerHeight - container.offsetHeight;
container.style.left = Math.min(Math.max(x, 10), maxX - 10) + 'px';
container.style.top = Math.min(Math.max(y, 10), maxY - 10) + 'px';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
container.style.cursor = 'default';
});
// 折叠功能
let isMinimized = false;
const minimizeBtn = container.querySelector('#minimize-btn');
const content = container.querySelector('#assistant-content');
minimizeBtn.addEventListener('click', () => {
isMinimized = !isMinimized;
if (isMinimized) {
content.style.display = 'none';
minimizeBtn.innerHTML = `
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/>
</svg>
`;
container.style.height = 'auto';
} else {
content.style.display = 'block';
minimizeBtn.innerHTML = `
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/>
</svg>
`;
}
});
// 关闭按钮
const closeBtn = container.querySelector('#close-btn');
closeBtn.addEventListener('click', () => {
container.style.display = 'none';
});
// 自动勾选功能
const autoCheckToggle = container.querySelector('#auto-check-toggle');
autoCheckToggle.addEventListener('change', () => {
config.autoCheck = autoCheckToggle.checked;
saveConfig();
if (config.autoCheck) {
checkElement();
}
});
// 焦点锁定功能
const focusLockToggle = container.querySelector('#focus-lock-toggle');
focusLockToggle.addEventListener('change', () => {
config.focusLock = focusLockToggle.checked;
saveConfig();
if (config.focusLock) {
showNotification('焦点锁定已启用');
lockFocus();
} else {
showNotification('焦点锁定已禁用');
}
});
// 主题切换
const themeButtons = container.querySelectorAll('.theme-btn');
themeButtons.forEach(btn => {
btn.addEventListener('click', () => {
const theme = btn.dataset.theme;
config.theme = theme;
saveConfig();
location.reload(); // 重新加载以应用新主题
});
});
// 保存配置
function saveConfig() {
GM_setValue('config', config);
}
// 自动勾选目标元素
function checkElement() {
const checkInterval = setInterval(() => {
const targetElement = document.evaluate(
'//*[@id="CalcPlattitle"]/span[2]/a',
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
if (targetElement) {
targetElement.click();
clearInterval(checkInterval);
}
}, 500);
}
// 显示通知
function showNotification(message) {
const existingNote = document.querySelector('.focus-notification');
if (existingNote) existingNote.remove();
const notification = document.createElement('div');
notification.className = 'focus-notification';
notification.innerHTML = `
<svg viewBox="0 0 24 24" width="18" height="18" fill="white">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
${message}
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 2500);
}
// 焦点锁定功能
function lockFocus() {
// 监听焦点变化
document.addEventListener('focusin', (e) => {
if (!config.focusLock) return;
const input = document.getElementById('txt_marking_2');
if (input && e.target === input) {
// 如果焦点被转移到输入框,立即移回body
e.preventDefault();
document.body.focus();
showNotification('焦点已锁定到页面');
}
});
// 初始设置焦点
document.body.focus();
}
// 添加快捷键功能
const newKeyInput = container.querySelector('#new-key');
const newScoreInput = container.querySelector('#new-score');
const addShortcutBtn = container.querySelector('#add-shortcut-btn');
// 按键捕获功能
newKeyInput.addEventListener('focus', () => {
newKeyInput.value = '按下按键...';
isFocusLocked = false; // 临时禁用焦点锁定
});
newKeyInput.addEventListener('keydown', (e) => {
e.preventDefault();
// 忽略功能键
if (['Shift', 'Control', 'Alt', 'Meta', 'CapsLock'].includes(e.key)) {
return;
}
const key = e.key.length === 1 ? e.key.toUpperCase() : e.key;
newKeyInput.value = key;
newScoreInput.focus();
isFocusLocked = config.focusLock; // 恢复焦点锁定状态
});
// 添加新快捷键
addShortcutBtn.addEventListener('click', () => {
const key = newKeyInput.value;
const score = parseInt(newScoreInput.value);
if (!key || key === '按下按键...') {
showNotification('请先设置按键');
return;
}
if (isNaN(score) || score < 0 || score > 100) {
showNotification('请输入0-100之间的分数');
return;
}
// 添加或更新快捷键
config.scoreShortcuts[key] = score;
saveConfig();
renderShortcuts();
showNotification(`已添加快捷键: ${key} → ${score}分`);
// 清空表单
newKeyInput.value = '';
newScoreInput.value = '';
});
// 设置回评快捷键
const setPrevKeyBtn = container.querySelector('#set-prev-key');
let keyModal = null;
setPrevKeyBtn.addEventListener('click', () => {
// 创建模态框
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop';
backdrop.innerHTML = `
<div class="key-modal">
<div class="modal-title">设置回评快捷键</div>
<input type="text" class="key-input-large" id="modal-key-input" placeholder="按下按键..." readonly>
<div class="shortcut-help">按下您想设置的快捷键</div>
<div class="modal-buttons">
<button class="modal-btn cancel-btn" id="cancel-btn">取消</button>
<button class="modal-btn confirm-btn" id="confirm-btn">确认</button>
</div>
</div>
`;
document.body.appendChild(backdrop);
keyModal = backdrop;
const keyInput = backdrop.querySelector('#modal-key-input');
keyInput.focus();
// 按键捕获
keyInput.addEventListener('keydown', (e) => {
e.preventDefault();
// 忽略功能键
if (['Shift', 'Control', 'Alt', 'Meta', 'CapsLock'].includes(e.key)) {
return;
}
const key = e.key.length === 1 ? e.key.toUpperCase() : e.key;
keyInput.value = key;
});
// 确认按钮
backdrop.querySelector('#confirm-btn').addEventListener('click', () => {
const key = keyInput.value;
if (!key || key === '按下按键...') {
showNotification('请先设置按键');
return;
}
config.prevShortcut = key;
saveConfig();
container.querySelector('#prev-key').textContent = key;
showNotification(`回评快捷键已设置为: ${key}`);
backdrop.remove();
});
// 取消按钮
backdrop.querySelector('#cancel-btn').addEventListener('click', () => {
backdrop.remove();
});
});
// 初始化功能
if (config.autoCheck) {
setTimeout(checkElement, 1000);
}
if (config.focusLock) {
lockFocus();
}
// 快捷键功能
document.addEventListener('keydown', function(e) {
// 忽略在输入框中的按键(如果焦点锁定已禁用)
if (!config.focusLock && (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA')) {
return;
}
const key = e.key;
// 检查是否为评分快捷键
if (config.scoreShortcuts.hasOwnProperty(key)) {
const score = config.scoreShortcuts[key];
const scoreElement = document.evaluate(
`//*[@id="platContent"]/a[${score + 1}]`,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
if (scoreElement) {
scoreElement.click();
// 保持焦点在页面上
setTimeout(() => {
document.body.focus();
}, 100);
}
return;
}
// 检查是否为回评快捷键
if (key === config.prevShortcut) {
const prevElement = document.evaluate(
'//*[@id="app"]/div/div[1]/div/div[2]/div/div[1]/div[1]/div[1]/div[3]/span[9]/a',
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
if (prevElement) {
prevElement.click();
// 保持焦点在页面上
setTimeout(() => {
document.body.focus();
}, 100);
}
}
});
// 状态指示器
const statusIndicator = container.querySelector('.status-indicator');
setInterval(() => {
statusIndicator.classList.toggle('inactive');
setTimeout(() => {
statusIndicator.classList.toggle('inactive');
}, 500);
}, 2000);
// 回评按钮点击事件
const prevPaperAction = container.querySelector('#prev-paper-action');
prevPaperAction.addEventListener('click', () => {
const prevElement = document.evaluate(
'//*[@id="app"]/div/div[1]/div/div[2]/div/div[1]/div[1]/div[1]/div[3]/span[9]/a',
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
if (prevElement) {
prevElement.click();
}
});
// 初始显示状态
container.style.display = 'block';
// 添加菜单命令
GM_registerMenuCommand("显示/隐藏悬浮窗", function() {
if (container.style.display === 'none') {
container.style.display = 'block';
showNotification('悬浮窗已显示');
} else {
container.style.display = 'none';
showNotification('悬浮窗已隐藏');
}
});
GM_registerMenuCommand("重新加载配置", function() {
config = Object.assign({}, defaultConfig, GM_getValue('config', {}));
location.reload();
});
GM_registerMenuCommand("重置为默认配置", function() {
config = defaultConfig;
saveConfig();
location.reload();
});
})();