// ==UserScript==
// @name 🚀🚀🚀一步搜题|查题|大学|成人本科|继续教育|复制搜题|截图搜题|无侵入,更安全|超全题库,方便快捷,一步到位
// @version 0.1.2
// @description 一步搜题、查题,中学、大学试题,成人本科,继续教育,复制搜题,截图搜题,无侵入,更安全,超全题库,方便快捷,一步到位,超全题库,方便使用,辅助您一步搜题,快速搜答案,并且支持图片搜题,解决您搜题难的问题
// @author You
// @match *://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js
// @require https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.16/vue.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.min.js
// @resource snow https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.snow.min.css
// @grant GM_info
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_addStyle
// @connect appwk.baidu.com
// @connect 39.105.186.109
// @antifeature ads 脚本包含搜索文本时,附带的广告
// @antifeature payment 脚本题目搜索,需对接第三方接口付费
// @namespace https://gf.qytechs.cn/zh-CN/users/1251635-ebsoo
// ==/UserScript==
$(document).ready(function() {
if (window.self !== window.top) {
return;
}
// Tab 组件
Vue.component('eb-tab', {
data: function () {
return {
tabList: [
{ name: '搜题'}
],
currentIndex: 0
}
},
template: `
<div class="eb-tab-container">
<div v-for="(tab, idx) in tabList" class="eb-tab" v-bind:class="{ active: currentIndex == idx }" @click="switchTab(idx)">{{tab.name}}</div>
</div>
`,
methods: {
switchTab(idx) {
this.currentIndex = idx;
this.$emit('switch-tab', idx);
}
}
});
// Header
Vue.component('eb-header', {
template: `
<div id="ebMyHeader" class="eb-header">
<div>ebsoo|一步搜题</div>
<div class="eb-close-icon" @click="closeEv">X</div>
</div>
`,
mounted() {
this.makeDraggable(this.state, document.getElementById(this.draggleId), document.getElementById(this.targetId));
},
data: function() {
return {
draggleId: 'ebMyHeader',
targetId: 'ebsooWrap0104',
state: {
dragging: false,
initX: 0,
initY: 0,
transX: 0,
transY: 0,
moveX: 0,
moveY: 0,
}
}
},
methods: {
closeEv() {
ebToggleOpenClose();
},
makeDraggable(state, el, target) {
function globalMousemove(event) {
const { clientX, clientY } = event;
const { initX, initY, transX, transY } = state;
const moveX = clientX - initX;
const moveY = clientY - initY;
target.style.transform = `translate(${transX + moveX}px, ${transY + moveY}px)`;
state.moveX = moveX;
state.moveY = moveY;
state.dragging = true;
}
function globalMouseup(_event) {
const { transX, transY, moveX, moveY } = state;
state.transX = transX + moveX;
state.transY = transY + moveY;
state.moveX = 0;
state.moveYY = 0;
document.removeEventListener('mousemove', globalMousemove);
document.removeEventListener('mouseup', globalMouseup);
state.dragging = null;
}
function mousedown(event) {
const {clientX, clientY} = event;
state.initX = clientX;
state.initY = clientY;
document.addEventListener('mousemove', globalMousemove);
document.addEventListener('mouseup', globalMouseup);
}
el.addEventListener('mousedown', mousedown);
}
}
});
// 搜索
Vue.component('eb-content-search', {
template: `
<div class="eb-content-search-container">
<div class="eb-editor-outer">
<div id="eb-editor-container"></div>
<div class="eb-my-footer">
<div class="eb-btn primary" @click="submit">搜索</div>
<div class="eb-btn bordered" @click="clear">清空</div>
</div>
</div>
<div id="adkfuasdgku" class="eb-answers">
<div class="fk-item" v-for="(item, index) in dataList">
<div class="eb-question" v-html="item.question">{{index}}</div>
<div class="eb-options-wrap" v-if="item.options.length">
<div class="eb-label">选项:</div>
<div v-for="(option, idx) in item.options">{{idx + 1}}、{{option}}</div>
</div>
<div class="eb-answer-wrap" v-if="item.answer.length">
<div class="eb-label">答案:</div>
<div class="fk-answer" v-for="(answer, idx) in item.answer" v-html="answer">
</div>
</div>
<div class="eb-answer-wrap" v-if="item.analysis">
<div class="eb-label">解析:</div>
<div class="fk-analysis" v-html="item.analysis">
</div>
</div>
</div>
</div>
<eb-loading v-show="showLoading"></eb-loading>
</div>
`,
data: function() {
return {
showLoading: false,
dataList: []
}
},
methods: {
submit() {
let ops = quill.getContents().ops;
let imgNode = ops.find(item => item.insert && item.insert.image);
let txtNode = ops.find(item => item.insert && typeof item.insert == 'string');
let question = '';
if (imgNode) {
let base64 = imgNode.insert.image;
this.imgHandle(base64).then(res => {
this.imgOcr(res).then(val => {
this.fetchAnswer(val);
})
})
} else if (txtNode) {
question = txtNode.insert.replace(/\n|\t|\s/g, '');
this.fetchAnswer(question);
}
},
clear() {
quill.deleteText(0, 99999);
},
fetchAnswer(question) {
if (!question) {
alert('请输入文字,或者复制截图进来');
return;
}
if (question.length < 5) {
alert(`至少输入5个字,当前值: ${question}`);
return;
}
this.showLoading = true;
let prodHost = 'http://39.105.186.109:4000/eb/search';
fkingHttp('POST', prodHost, {
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify({
keyWord: question,
href: location.href || ''
})
}).then(response => {
let dataList = JSON.parse(response.responseText).data.map((item, index) => {
return {
analysis: item.analysis,
answer: this.parseStr(item.answer),
options: this.parseStr(item.options), //1
question: `${index + 1}. <span class="typeStr">【${this.parseType(item.type)}】</span> ${item.question}`
}
});
this.dataList = dataList;
this.showLoading = false;
if (!dataList.length) {
alert('未搜到到结果');
}
}).catch(_ => {
this.showLoading = false;
alert('出错了,请反馈');
})
},
imgHandle(base64) {
return new Promise((resolve, reject) => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const image = new Image();
image.setAttribute("crossOrigin", "Anonymous");
image.src = base64;
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
context.fillStyle = "#fff";
context.fillRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
canvas.toBlob(blob => {
resolve(blob);
});
};
});
},
imgOcr(blob) {
return new Promise((resolve, reject) => {
var fd = new FormData();
fd.append("image", blob, "1.png");
GM_xmlhttpRequest({
url: "https://appwk.baidu.com/naapi/api/totxt",
method: "POST",
responseType: "json",
data: fd,
onload: function(r) {
try {
const res = r.response.words_result.map(item => {
return item.words;
}).join("");
resolve(res);
} catch (err) {
resolve("");
}
}
});
});
},
parseStr(str) {
try {
let res = JSON.parse(str);
if (Array.isArray(res)) {
return res;
}
} catch {
return [str];
}
},
parseType(type) {
const questionTypes = {
0: '单选题', 1: '多选题', 2: '填空题', 3: '判断题', 4: '简答题',
5: '名词解释', 6: '论述题', 7: '计算题', 8: '其它', 9: '分录题',
10: '资料题', 11: '连线题', 13: '排序题', 14: '完型填空', 15: '阅读理解',
17: '程序题', 20: '共用选项题'
};
return questionTypes[Number(type)] || '未知题型'
}
}
});
// 个人中心
Vue.component('eb-content-my', {
data: function() {
return {
}
},
methods: {}
});
// 设置
Vue.component('eb-content-set', {
template: `
<div>eb-content-set</div>
`,
data: function() {
return {
}
},
methods: {}
});
Vue.component('eb-loading', {
template: `
<div class="eb-loading-wrap">
<img class="eb-loading-icon" src="https://i.ibb.co/7jP0Lx5/5d37ba69fc9341179f10b85407c7d34f.gif" />
</div>
`,
data: function() {
},
methods: {
}
});
let httpClient = GM.xmlHttpRequest ? GM.xmlHttpRequest : (GM_xmlhttpRequest || null);
const styleText = GM_getResourceText("snow");
GM_addStyle(styleText);
GM_addStyle(`
#ebsooWrap0104 {
padding-top: 32px;display: flex;flex-direction: column; position: fixed;top: 100px;right: 100px;width: 400px;height: 600px;background-color: #fff;z-index:9999;border: solid #198754 1px;overflow: hidden; transform: translate(-8000px, 0);
}
#ebsooWrap0104 div {
margin: unset;
}
#ebsooWrap0104 #ebMyHeader {
position: absolute;left: 0;top: 0;right: 0;height: 32px;padding: 0 4px 0 12px;text-align: left;background-color: #198754; line-height: 32px;cursor: move;display: flex;justify-content: space-between; align-items: center; font-weight: bold; color: #fff; border-radius: 0px;
}
#ebsooWrap0104 #ebMyHeader .eb-close-icon {
cursor: pointer;font-size: 12px;border: solid #fff 2px;border-radius: 50%;height: 24px;width: 24px;line-height: 22px;text-align: center;
}
#ebsooWrap0104 .router-content {
height: calc(100% - 48px);
}
#ebsooWrap0104 .eb-content-search-container {
height: 100%;display: flex;flex-direction: column;
}
#ebsooWrap0104 .eb-editor-outer {
flex-shrink: 0;padding: 6px;box-shadow: 8px 8px 12px 0 #3c7cfc1f;
}
#ebsooWrap0104 .ql-container {
height: 120px;
}
#ebsooWrap0104 .ql-toolbar {
text-align: left;
}
#ebsooWrap0104 .eb-my-footer {
display: flex; justify-content: center; align-items: center; height: 42px;line-height: 42px;border: 1px solid #ccc;border-top: none;box-sizing: border-box;text-align: center;
}
#ebsooWrap0104 .eb-my-footer .eb-btn {
margin:0 4px; width: 64px; height: 32px; line-height: 31px;text-align: center; border-radius: 6px; cursor: pointer; font-size: 14px; letter-spacing: 2px;
}
#ebsooWrap0104 .eb-my-footer .eb-btn.primary {
background-color: #198754; color: #fff;
}
#ebsooWrap0104 .eb-my-footer .eb-btn.bordered {
border: 1px solid #212529;
}
#ebsooWrap0104 .eb-answers {
flex: 1;text-align: left;overflow-y: scroll;
}
#ebsooWrap0104 .eb-answers .fk-item {
padding: 10px 12px;border-bottom: solid #ccc 1px;
}
#ebsooWrap0104 .eb-answers .fk-item .eb-question,
#ebsooWrap0104 .eb-options-wrap,
#ebsooWrap0104 .eb-answer-wrap {
font-size: 13px; line-height: 17px;
}
#ebsooWrap0104 .fk-answer {
padding: 4px;border: dashed rgb(59, 146, 233) 1px;
}
#ebsooWrap0104 .fk-analysis {
padding: 4px;
}
#ebsooWrap0104 .eb-answers em {
color: red;font-weight: bold;font-style: normal;
}
#ebsooWrap0104 .eb-label {
font-size: 14px;margin: 8px 0;width: fit-content;border-bottom: dashed #ccc 1px;font-weight: bold;
}
#ebsooWrap0104 .eb-panel-container {
position: absolute;left: 0;top: -100%;width: 100%;height: 100%;z-index: 10000;transition: top .3s;background-color: rgba(0,0,0,0.35);
}
#ebsooWrap0104 .eb-panel-container.show {
top: 32px;
}
#ebsooWrap0104 .eb-panel-wrap {
width: 400px;height: 400px;margin: 80px auto auto; background-color: #fff; border-radius: 10px;
}
#ebsooWrap0104 .eb-tab-container {
padding-left: 12px;margin: 6px 0;display: flex;height: 36px;border-bottom: solid #ccc 1px;
}
#ebsooWrap0104 .eb-tab-container .eb-tab {
box-sizing: border-box;position: relative;top: 1px;width: 72px;height: 36px;line-height: 36px;text-align: center;cursor: pointer;border-bottom: solid #ccc 1px;border-top: solid #fff 1px;
}
#ebsooWrap0104 .eb-tab-container .eb-tab.active {
border: solid #198754 1px;border-bottom: solid #fff 1px;border-top-left-radius: 6px;border-top-right-radius: 6px;background-color: #fff;
}
#ebsooWrap0104 .ql-toolbar.ql-snow {
padding: 0;
}
#ebsooWrap0104 .eb-loading-wrap {
position: absolute;left: 0;top: 0;right: 0;bottom: 0;background-color: rgba(0,0,0,0.05);text-align: center;
}
.eb-loading-wrap .eb-loading-icon {
margin-top: 50%;
}
.eb-trigger-btn {
position: fixed; right: 0;bottom:50%; width: 40px; height: 56px; line-height: 56px; background-color: #198754; color: #fff; cursor: pointer;border-top-left-radius:8px; border-bottom-left-radius: 10px; text-align: center; font-size: 20px; user-select: none;z-index: 99999;
}
`);
var $wrap = $(`
<div id="ebsooWrap0104">
<eb-header></eb-header>
<eb-tab @switch-tab="handleSwitchTab"></eb-tab>
<div class="router-content">
<eb-content-search v-show="currentTabIdx == 0"></eb-content-search>
<eb-content-set v-show="currentTabIdx == 1"></eb-content-set>
</div>
</div>
<div id="ebTriggerBtn" class="eb-trigger-btn">开</div>`);
$('body').prepend($wrap);
new Vue({
el: '#ebsooWrap0104',
data: {
currentTabIdx: 0
},
methods: {
handleSwitchTab(idx) {
this.currentTabIdx = idx;
}
}
});
var quill = new Quill('#eb-editor-container', {
modules: {
toolbar: [
['image']
]
},
placeholder: '输入文字,复制截图,或者上传图片进来',
theme: 'snow'
});
function fkingHttp (method, url, config) {
let options = {
timeout: 30000,
}
if (config) {
Object.assign(options, config);
}
return new Promise((resolve, reject) => {
httpClient(Object.assign({
method: method,
url: url.indexOf('http') > -1 ? url : baseUrl + url,
onload: function(response) {
resolve(response);
},
onerror: function (err) {
reject(err);
},
ontimeout: function () {
reject();
}
}, options));
})
};
var show = false;
$('#ebTriggerBtn').on('click', _ => {
ebToggleOpenClose();
})
function ebToggleOpenClose() {
if (show) {
show = false;
$('#ebsooWrap0104').css('transform', 'translate(-8000px, 0)');
$('#ebTriggerBtn').css('background-color', '#198754');
$('#ebTriggerBtn').html('开');
} else {
show = true;
$('#ebsooWrap0104').css('transform', 'translate(0, 0)');
$('#ebTriggerBtn').css('background-color', '#ccc');
$('#ebTriggerBtn').html('关');
}
}
})