// ==UserScript== // @name 网课搜题助手 - 悬浮窗版 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 免费搜题 // @author Assistant // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_notification // @connect tk.swk.tw // ==/UserScript== (function() { 'use strict'; // 防止在 iframe 或嵌套页面中重复创建 if (window.top !== window.self) { // 在 iframe 中不运行,避免重复 return; } // 防止脚本重复初始化 if (window.__tikuAssistantInitialized) { return; } window.__tikuAssistantInitialized = true; // ---------- 配置参数 ---------- const API_URL = 'https://tk.swk.tw/api/search.php'; const DEFAULT_KEY = 'sk-ce698b6e3ff5b787da79bc5e5c23c345759776cc9bb1613d'; const TIKU_URL = 'https://tk.swk.tw/'; // 存储Key let userKey = GM_getValue('tiku_api_key', DEFAULT_KEY); // ---------- 工具函数:HTML转义 ---------- function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // ---------- 全局变量:拖动状态 ---------- let isDragging = false; let dragStartX = 0, dragStartY = 0; let floatingLeft = 0, floatingTop = 0; // ---------- 创建可拖动悬浮窗(一体化) ---------- function createFloatingWindow() { // 检查是否已存在 if (document.getElementById('tiku-floating-window')) { return document.getElementById('tiku-floating-window'); } // 悬浮窗容器 const floatingWin = document.createElement('div'); floatingWin.id = 'tiku-floating-window'; floatingWin.style.cssText = ` position: fixed; width: 460px; max-width: 90vw; background: #ffffff; border-radius: 24px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 0, 0, 0.05); font-family: 'Segoe UI', 'Poppins', system-ui, -apple-system, sans-serif; z-index: 10000; cursor: default; overflow: hidden; backdrop-filter: blur(0); transition: box-shadow 0.2s; right: 20px; top: 80px; display: none; `; // 可拖动头部 const header = document.createElement('div'); header.id = 'tiku-drag-handle'; header.style.cssText = ` background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); padding: 14px 20px; cursor: grab; user-select: none; display: flex; justify-content: space-between; align-items: center; color: white; border-bottom: 1px solid rgba(255,255,255,0.1); `; header.innerHTML = `
📚 题库助手 悬浮窗
`; // 内容区(可折叠) const contentWrapper = document.createElement('div'); contentWrapper.id = 'tiku-content-wrapper'; contentWrapper.style.cssText = ` transition: all 0.3s ease; overflow: hidden; `; const content = document.createElement('div'); content.style.cssText = ` padding: 20px; background: #f9fafb; `; // 卡密配置区域 const keySection = document.createElement('div'); keySection.style.cssText = ` background: white; border-radius: 18px; padding: 14px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #e5e7eb; `; keySection.innerHTML = `
🔑 API 密钥 可选
`; // 搜题区域 const searchSection = document.createElement('div'); searchSection.style.cssText = ` background: white; border-radius: 18px; padding: 14px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #e5e7eb; `; searchSection.innerHTML = `
🔍 智能搜题
`; content.appendChild(keySection); content.appendChild(searchSection); contentWrapper.appendChild(content); floatingWin.appendChild(header); floatingWin.appendChild(contentWrapper); document.body.appendChild(floatingWin); // ----- 拖动逻辑 ----- const dragHandle = document.getElementById('tiku-drag-handle'); dragHandle.addEventListener('mousedown', (e) => { if (e.target.id === 'tiku-minimize-btn' || e.target.id === 'tiku-close-floating') return; isDragging = true; dragStartX = e.clientX; dragStartY = e.clientY; const rect = floatingWin.getBoundingClientRect(); floatingLeft = rect.left; floatingTop = rect.top; floatingWin.style.cursor = 'grabbing'; dragHandle.style.cursor = 'grabbing'; e.preventDefault(); }); window.addEventListener('mousemove', (e) => { if (!isDragging) return; let dx = e.clientX - dragStartX; let dy = e.clientY - dragStartY; let newLeft = floatingLeft + dx; let newTop = floatingTop + dy; // 边界限制 newLeft = Math.min(Math.max(0, newLeft), window.innerWidth - floatingWin.offsetWidth); newTop = Math.min(Math.max(0, newTop), window.innerHeight - floatingWin.offsetHeight); floatingWin.style.left = newLeft + 'px'; floatingWin.style.top = newTop + 'px'; floatingWin.style.right = 'auto'; floatingWin.style.bottom = 'auto'; }); window.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; floatingWin.style.cursor = 'default'; dragHandle.style.cursor = 'grab'; } }); // 最小化/还原功能 let isMinimized = false; const minimizeBtn = document.getElementById('tiku-minimize-btn'); minimizeBtn.addEventListener('click', () => { isMinimized = !isMinimized; if (isMinimized) { contentWrapper.style.height = '0px'; contentWrapper.style.padding = '0'; minimizeBtn.innerHTML = '+'; minimizeBtn.style.transform = 'scale(1.1)'; } else { contentWrapper.style.height = 'auto'; contentWrapper.style.padding = ''; minimizeBtn.innerHTML = '−'; minimizeBtn.style.transform = 'scale(1)'; } }); // 关闭悬浮窗(隐藏) const closeBtn = document.getElementById('tiku-close-floating'); closeBtn.addEventListener('click', () => { floatingWin.style.display = 'none'; // 同时显示悬浮球 const ball = document.getElementById('tiku-float-ball'); if (ball) ball.style.display = 'flex'; }); // 按钮悬停效果 const btns = [minimizeBtn, closeBtn]; btns.forEach(btn => { btn.addEventListener('mouseenter', () => btn.style.background = 'rgba(255,255,255,0.3)'); btn.addEventListener('mouseleave', () => btn.style.background = 'rgba(255,255,255,0.15)'); }); // 保存密钥 const saveKeyBtn = document.getElementById('tiku-save-key'); const keyInput = document.getElementById('tiku-key-input'); const keyStatus = document.getElementById('key-status-msg'); saveKeyBtn.addEventListener('click', () => { const newKey = keyInput.value.trim(); if (newKey === '') { userKey = DEFAULT_KEY; GM_setValue('tiku_api_key', DEFAULT_KEY); showKeyStatus('✅ 已恢复默认密钥', '#10b981'); } else { userKey = newKey; GM_setValue('tiku_api_key', newKey); showKeyStatus('✅ 密钥保存成功', '#10b981'); } setTimeout(() => { keyStatus.style.display = 'none'; }, 2000); }); function showKeyStatus(msg, color) { keyStatus.textContent = msg; keyStatus.style.color = color; keyStatus.style.display = 'block'; } // 题库网站按钮 const websiteBtn = document.getElementById('tiku-goto-website'); websiteBtn.addEventListener('click', () => { window.open(TIKU_URL, '_blank'); }); // 搜题逻辑 const searchBtn = document.getElementById('tiku-search-btn'); const questionInput = document.getElementById('tiku-question-input'); const answerArea = document.getElementById('tiku-answer-area'); function performSearch() { const question = questionInput.value.trim(); if (!question) { showAnswer('⚠️ 请输入要查询的题目内容', '#f97316'); return; } showAnswer('⏳ 正在搜索中,请稍候...', '#3b82f6', true); const currentKey = GM_getValue('tiku_api_key', DEFAULT_KEY); GM_xmlhttpRequest({ method: 'POST', url: API_URL, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, data: `question=${encodeURIComponent(question)}&key=${currentKey}`, onload: function(resp) { try { const json = JSON.parse(resp.responseText); renderAnswer(json, question); } catch(e) { showAnswer('❌ 解析失败: ' + e.message, '#ef4444'); } }, onerror: () => showAnswer('❌ 网络错误,请检查连接', '#ef4444'), ontimeout: () => showAnswer('⏰ 请求超时,请重试', '#ef4444') }); } function renderAnswer(data, question) { if (data.code === 1) { let html = `
✅ 查询成功
📌 题目:
${escapeHtml(question)}
💡 答案:
${escapeHtml(data.data)}
`; if (data.quota_info) { html += `
📊 剩余: ${data.quota_info.remaining} | 今日: ${data.quota_info.free_remaining}/${data.quota_info.daily_quota}
`; } showAnswer(html, '#10b981'); GM_notification({ text: `答案已找到`, title: '题库助手', timeout: 3000 }); } else { let errMsg = `❌ 查询失败 (${data.code})
${escapeHtml(data.msg || '未知错误')}`; if (data.data) errMsg += `
详情: ${escapeHtml(data.data)}`; showAnswer(errMsg, '#ef4444'); } } function showAnswer(content, borderColor, isLoading = false) { answerArea.style.display = 'block'; answerArea.innerHTML = content; answerArea.style.borderLeft = `4px solid ${borderColor}`; answerArea.style.background = isLoading ? '#eff6ff' : '#fffbeb'; } searchBtn.addEventListener('click', performSearch); questionInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') performSearch(); }); // 输入框美化 const styleInput = (inputEl) => { inputEl.addEventListener('focus', () => { inputEl.style.borderColor = '#6366f1'; inputEl.style.boxShadow = '0 0 0 2px rgba(99,102,241,0.1)'; }); inputEl.addEventListener('blur', () => { inputEl.style.borderColor = '#d1d5db'; inputEl.style.boxShadow = 'none'; }); }; styleInput(keyInput); styleInput(questionInput); // 按钮悬停效果 [saveKeyBtn, websiteBtn, searchBtn].forEach(btn => { btn.addEventListener('mouseenter', () => btn.style.transform = 'translateY(-1px)'); btn.addEventListener('mouseleave', () => btn.style.transform = 'translateY(0)'); }); // 填充当前密钥显示 if (userKey === DEFAULT_KEY) { keyInput.value = ''; keyInput.placeholder = '使用默认密钥'; } else { keyInput.value = userKey; keyInput.placeholder = '已自定义密钥'; } return floatingWin; } // ---------- 创建悬浮球(唤起悬浮窗)---------- function createFloatBall() { // 检查是否已存在 if (document.getElementById('tiku-float-ball')) { return document.getElementById('tiku-float-ball'); } const ball = document.createElement('div'); ball.id = 'tiku-float-ball'; ball.innerHTML = '📚'; ball.style.cssText = ` position: fixed; bottom: 30px; right: 30px; width: 52px; height: 52px; background: linear-gradient(135deg, #1e293b, #0f172a); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 24px; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.25); z-index: 9999; transition: all 0.3s ease; font-family: system-ui; border: 1px solid rgba(255,255,255,0.2); `; ball.addEventListener('mouseenter', () => { ball.style.transform = 'scale(1.1)'; ball.style.boxShadow = '0 8px 25px rgba(0,0,0,0.3)'; }); ball.addEventListener('mouseleave', () => { ball.style.transform = 'scale(1)'; }); return ball; } // ---------- 清理函数(防止重复创建)---------- function cleanup() { // 移除可能存在的旧元素 const oldFloating = document.getElementById('tiku-floating-window'); const oldBall = document.getElementById('tiku-float-ball'); if (oldFloating) oldFloating.remove(); if (oldBall) oldBall.remove(); } // ---------- 初始化 ---------- function init() { // 清理旧的残留元素 cleanup(); // 创建新元素 const floatingWin = createFloatingWindow(); const floatBall = createFloatBall(); document.body.appendChild(floatBall); // 确保悬浮窗已添加到body if (!document.body.contains(floatingWin)) { document.body.appendChild(floatingWin); } // 悬浮球点击事件 floatBall.addEventListener('click', () => { floatingWin.style.display = 'block'; floatBall.style.display = 'none'; // 刷新密钥显示 const keyInput = document.getElementById('tiku-key-input'); if (keyInput) { const stored = GM_getValue('tiku_api_key', DEFAULT_KEY); if (stored === DEFAULT_KEY) { keyInput.value = ''; keyInput.placeholder = '使用默认密钥'; } else { keyInput.value = stored; keyInput.placeholder = '已自定义密钥'; } } // 清空答案区域(可选) const answerArea = document.getElementById('tiku-answer-area'); if (answerArea) answerArea.style.display = 'none'; }); // 监听页面动态变化,防止AJAX嵌套页面重复创建 const observer = new MutationObserver((mutations) => { // 检查是否有重复的悬浮球被添加 const balls = document.querySelectorAll('#tiku-float-ball'); if (balls.length > 1) { for (let i = 1; i < balls.length; i++) { balls[i].remove(); } } const windows = document.querySelectorAll('#tiku-floating-window'); if (windows.length > 1) { for (let i = 1; i < windows.length; i++) { windows[i].remove(); } } }); observer.observe(document.body, { childList: true, subtree: true }); console.log('✅ 搜题助手已启动 | 悬浮窗模式 '); } // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }