// ==UserScript==
// @name 【学习通题库大全】【免费搜题】【完全免费】学习通题库查询助手(悬浮窗)
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 在任意页面添加可拖拽悬浮窗,通过指定接口查询题库答案。API Key 需用户自行填写。
// @author You
// @match *://*/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
if (window.top !== window.self) return;
if (document.getElementById('tiku-float-window')) return;
const API_URL = 'https://so.ucuc.net/prod-api/system/questionBank/search';
GM_addStyle(`
#tiku-float-window {
position: fixed;
top: 100px;
right: 20px;
width: 360px; /* 稍大一点 */
background: white;
border: 2px solid #3b7cff;
border-radius: 12px;
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
z-index: 999999;
font-family: 'Microsoft YaHei', sans-serif;
font-size: 14px;
color: #333;
overflow: hidden;
resize: both;
min-width: 300px;
min-height: 250px;
/* 移除全局 user-select: none,以便可以复制 */
}
#tiku-float-header {
background: #3b7cff;
color: white;
padding: 8px 12px;
cursor: move;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none; /* 头部仍然禁止选中,保持拖动体验 */
}
#tiku-float-header span {
font-size: 16px;
}
#tiku-close-btn {
cursor: pointer;
background: rgba(255,255,255,0.2);
border-radius: 4px;
padding: 0 6px;
font-size: 18px;
line-height: 1;
}
#tiku-close-btn:hover {
background: rgba(255,255,255,0.4);
}
#tiku-content {
padding: 15px;
max-height: 450px; /* 增大内容区域 */
overflow-y: auto;
user-select: text; /* 内容区域允许选中复制 */
}
.tiku-row {
margin-bottom: 12px;
}
.tiku-label {
font-weight: 600;
margin-bottom: 4px;
color: #555;
}
#tiku-question-input {
width: 100%;
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 6px;
box-sizing: border-box;
font-size: 14px;
}
#tiku-apikey-input {
width: 100%;
padding: 6px 8px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 12px;
box-sizing: border-box;
background: #f9f9f9;
}
.tiku-btns {
display: flex;
gap: 8px;
margin-top: 5px;
}
.tiku-btn {
background: #3b7cff;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
flex: 1;
transition: background 0.2s;
}
.tiku-btn:hover {
background: #2a5fcc;
}
.tiku-btn.secondary {
background: #6c757d;
}
.tiku-btn.secondary:hover {
background: #5a6268;
}
#tiku-result {
background: #f5f5f5;
border-radius: 8px;
padding: 12px;
margin-top: 15px;
border-left: 4px solid #3b7cff;
word-break: break-word;
white-space: pre-wrap;
max-height: 250px; /* 稍大 */
overflow-y: auto;
font-size: 13px;
user-select: text; /* 确保结果可复制 */
}
.tiku-result-title {
font-weight: bold;
color: #3b7cff;
margin-bottom: 5px;
}
.tiku-result-item {
margin-bottom: 6px;
border-bottom: 1px dashed #ddd;
padding-bottom: 4px;
}
.tiku-error {
color: #dc3545;
font-weight: 500;
}
.tiku-success {
color: #28a745;
}
.tiku-small {
font-size: 12px;
color: #888;
}
/* 推广文案样式 */
.tiku-promo {
font-size: 11px;
color: #666;
background: #fef8e7;
padding: 6px 8px;
border-radius: 6px;
margin: 8px 0;
border-left: 3px solid #ffc107;
line-height: 1.4;
}
.tiku-promo a {
color: #3b7cff;
text-decoration: none;
font-weight: 500;
}
.tiku-promo a:hover {
text-decoration: underline;
}
`);
function createWindow() {
const win = document.createElement('div');
win.id = 'tiku-float-window';
const header = document.createElement('div');
header.id = 'tiku-float-header';
header.innerHTML = '🔍 学习通题库查询✖';
const content = document.createElement('div');
content.id = 'tiku-content';
// 问题输入
const questionRow = document.createElement('div');
questionRow.className = 'tiku-row';
questionRow.innerHTML = '
📌 问题
';
const questionInput = document.createElement('input');
questionInput.id = 'tiku-question-input';
questionInput.type = 'text';
questionInput.placeholder = '输入要查询的问题,例如:习近平新时代中国特色社会主义思想';
questionInput.value = '习近平新时代中国特色社会主义思想';
questionRow.appendChild(questionInput);
content.appendChild(questionRow);
// API Key 输入
const apiRow = document.createElement('div');
apiRow.className = 'tiku-row';
apiRow.innerHTML = '🔑 API Key (必须填写)
';
const apiInput = document.createElement('input');
apiInput.id = 'tiku-apikey-input';
apiInput.type = 'text';
apiInput.placeholder = '请输入您的API Key';
apiInput.value = GM_getValue('tiku_apikey', '');
apiRow.appendChild(apiInput);
content.appendChild(apiRow);
// 推广文案
const promoDiv = document.createElement('div');
promoDiv.className = 'tiku-promo';
promoDiv.innerHTML = '🔗 https://so.ucuc.net 注册获取key即可享受api调用,邀请好友可享受40%佣金提现,可实时提现到账vx,qq群:1028360747';
content.appendChild(promoDiv);
// 按钮
const btnRow = document.createElement('div');
btnRow.className = 'tiku-row tiku-btns';
const queryBtn = document.createElement('button');
queryBtn.id = 'tiku-query-btn';
queryBtn.className = 'tiku-btn';
queryBtn.textContent = '查询';
const saveApiBtn = document.createElement('button');
saveApiBtn.id = 'tiku-save-api-btn';
saveApiBtn.className = 'tiku-btn secondary';
saveApiBtn.textContent = '保存Key';
btnRow.appendChild(queryBtn);
btnRow.appendChild(saveApiBtn);
content.appendChild(btnRow);
// 结果区域
const resultDiv = document.createElement('div');
resultDiv.id = 'tiku-result';
resultDiv.innerHTML = '📋 查询结果
等待查询...
';
content.appendChild(resultDiv);
win.appendChild(header);
win.appendChild(content);
document.body.appendChild(win);
dragElement(win, header);
document.getElementById('tiku-close-btn').addEventListener('click', () => win.style.display = 'none');
document.getElementById('tiku-save-api-btn').addEventListener('click', () => {
const key = document.getElementById('tiku-apikey-input').value.trim();
if (key) {
GM_setValue('tiku_apikey', key);
alert('API Key 已保存');
} else {
alert('请输入有效的API Key');
}
});
document.getElementById('tiku-query-btn').addEventListener('click', () => {
const question = document.getElementById('tiku-question-input').value.trim();
if (!question) {
showResult('请输入问题', 'error');
return;
}
const apiKey = document.getElementById('tiku-apikey-input').value.trim();
if (!apiKey) {
showResult('请先填写并保存API Key', 'error');
return;
}
queryAnswer(question, apiKey);
});
function showResult(text, type = 'success') {
const resultTextDiv = document.getElementById('tiku-result-text');
resultTextDiv.innerHTML = type === 'error' ? `❌ ${text}` : text;
}
function formatAnswer(data) {
if (!data) return '无返回数据';
let html = '';
if (data.title) html += `题目:${data.title}
`;
if (data.type) html += `类型:${data.type}
`;
if (data.kcname) html += `课程:${data.kcname}
`;
if (data.answer) {
const answers = data.answer.split('##').filter(s => s.trim());
html += '答案:
';
answers.forEach((ans, idx) => {
const cleanAns = ans.replace(/第一空:|第二空:|第三空:/g, '');
html += `${idx+1}. ${cleanAns}
`;
});
}
if (data.remainingCount != null && typeof data.remainingCount !== 'undefined') {
html += `剩余次数:${data.remainingCount}
`;
}
if (typeof data.score === 'number' && !isNaN(data.score)) {
html += `匹配度:${data.score.toFixed(2)}
`;
}
return html;
}
function queryAnswer(question, apiKey) {
showResult('⏳ 查询中...', 'success');
GM_xmlhttpRequest({
method: 'POST',
url: API_URL,
headers: { 'Content-Type': 'application/json' },
data: JSON.stringify({ question, apiKey }),
onload: function(res) {
try {
const resp = JSON.parse(res.responseText);
console.log('题库接口返回:', resp);
if (resp.code === 200 && resp.data) {
const formatted = formatAnswer(resp.data);
showResult(formatted, 'success');
} else {
showResult(`查询失败: ${resp.msg || '未知错误'}`, 'error');
}
} catch (e) {
showResult('解析响应失败: ' + e.message, 'error');
console.error('解析错误', e);
}
},
onerror: () => showResult('网络请求失败', 'error')
});
}
}
function dragElement(elm, dragHandle) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
dragHandle.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elm.style.top = (elm.offsetTop - pos2) + "px";
elm.style.left = (elm.offsetLeft - pos1) + "px";
elm.style.right = 'auto';
elm.style.bottom = 'auto';
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
createWindow();
})();