Skip to content

Commit

Permalink
feat(core): 完成智慧树登录脚本,学习脚本,修复BUG
Browse files Browse the repository at this point in the history
  • Loading branch information
enncy committed Nov 10, 2022
1 parent eaa6ae5 commit f07dd0f
Show file tree
Hide file tree
Showing 28 changed files with 2,095 additions and 457 deletions.
619 changes: 312 additions & 307 deletions packages/core/assets/css/style.css

Large diffs are not rendered by default.

56 changes: 34 additions & 22 deletions packages/core/assets/less/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ container-element {
position: fixed;
top: 10%;
left: 10%;
z-index: 99;
z-index: 99999;
text-align: left;
min-width: 300px;
-webkit-font-smoothing: antialiased;
Expand Down Expand Up @@ -147,12 +147,12 @@ scrip-panel-element + scrip-panel-element {
.card {
border-radius: 2px;
padding: 0px 4px;
width: -webkit-fill-available;
}

.notes {
background: #0099ff0e;
border-left: 4px solid #0099ff65;
width: -webkit-fill-available;
}

.tooltip {
Expand All @@ -171,6 +171,7 @@ scrip-panel-element + scrip-panel-element {
.configs {
display: table;
background: #e1e1e107;
width: -webkit-fill-available;

.configs-body {
display: table-row-group;
Expand Down Expand Up @@ -225,6 +226,10 @@ scrip-panel-element + scrip-panel-element {
.base-style-input();
.base-style-active-form-control();
}
input[type='range'] {
// 取消左右间隔,否则滚动条不能滚到头尾
padding: 0px;
}

input[type='button'] {
.base-style-button();
Expand Down Expand Up @@ -328,26 +333,30 @@ scrip-panel-element + scrip-panel-element {
}

&.error {
background-color: #ff000699;
background-color: #ff6b6ded;
color: #fff;
font-weight: bold;
border: 1px solid #f36c70;
}

&.info {
background-color: #2196f3a3;
background-color: #2195f3ef;
color: white;
font-weight: bold;
border: 1px solid #1890ff;
}

&.success {
background-color: #84d346d1;
color: #fff;
background-color: #67e53ccf;
color: #48752e;
font-weight: bold;
border: 1px solid #6fd91d;
}

&.warn {
background-color: #fbbf30e6;
color: #725206;
font-weight: bold;
border: 1px solid #ffc107;
}
}
Expand All @@ -365,7 +374,7 @@ model-element {
transform: translate(-50%, -50%);
padding: 12px 18px 18px 18px;
font-family: @base-font-family;
z-index: 99999;
z-index: 99999999999;

.model-profile {
zoom: 0.8;
Expand Down Expand Up @@ -402,14 +411,13 @@ model-element {

.model-cancel-button {
.base-style-button();
min-width: 60px;

color: gray;
background-color: white;
border: 1px solid #dcdcdc;
}
.model-confirm-button {
.base-style-button();
min-width: 60px;
}
}

Expand Down Expand Up @@ -469,21 +477,21 @@ model-element.confirm {
align-items: center;
text-align: center;
padding: 4px 0px;
}

.separator::before,
.separator::after {
content: '';
flex: 1;
border-bottom: 1px solid #63636346;
}
&::before,
&::after {
content: '';
flex: 1;
border-bottom: 1px solid #63636346;
}

.separator:not(:empty)::before {
margin-right: 0.25em;
}
&:not(:empty)::before {
margin-right: 0.25em;
}

.separator:not(:empty)::after {
margin-left: 0.25em;
&:not(:empty)::after {
margin-left: 0.25em;
}
}

.minimize {
Expand All @@ -495,9 +503,13 @@ model-element.confirm {
}
}

ul {
padding-left: 16px;
margin: 0px;
}

.user-guide {
max-width: 400px;
padding-left: 16px;

& > li {
padding: 4px 0px;
Expand Down
65 changes: 65 additions & 0 deletions packages/core/src/core/utils/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { AnswererWrapper } from '../worker/answer.wrapper.handler';
import { request } from './request';

/**
* 解析题库配置, 有误会抛出异常
*/
export async function parseAnswererWrappers(value: string): Promise<AnswererWrapper[]> {
let raw = value.toString();
let aw: AnswererWrapper[] = [];

// 解析 http 链接
if (value.startsWith('http')) {
raw = await request(value, {
contentType: 'text',
method: 'get',
type: 'fetch'
});
}

try {
aw = JSON.parse(raw);
} catch {
throw new Error(`格式错误,必须为:json字符串 或 题库配置链接`);
}

if (aw && Array.isArray(aw)) {
if (aw.length) {
for (let i = 0; i < aw.length; i++) {
const item = aw[i];
if (typeof item.name !== 'string') {
throw new Error(`第 ${i + 1} 个题库的 名字(name) 为空`);
}
if (typeof item.url !== 'string') {
throw new Error(`第 ${i + 1} 个题库的 接口地址(url) 为空`);
}
if (typeof item.name !== 'string') {
throw new Error(`第 ${i + 1} 个题库的 解析器(handler) 为空`);
}
if (item.headers && typeof item.headers !== 'object') {
throw new Error(`第 ${i + 1} 个题库的 头部信息(header) 应为 对象 格式`);
}
if (item.data && typeof item.data !== 'object') {
throw new Error(`第 ${i + 1} 个题库的 提交数据(data) 应为 对象 格式`);
}
const contentTypes = ['json', 'text'] as AnswererWrapper['contentType'][];
if (item.contentType && contentTypes.every((i) => i !== item.contentType)) {
throw new Error(`第 ${i + 1} 个题库的 contentType 必须为以下选项中的一个 ${contentTypes.join(', ')}`);
}
const methods = ['post', 'get'] as AnswererWrapper['method'][];
if (item.method && methods.every((i) => i !== item.method)) {
throw new Error(`第 ${i + 1} 个题库的 method 必须为以下选项中的一个 ${methods.join(', ')}`);
}
const types = ['fetch', 'GM_xmlhttpRequest'] as AnswererWrapper['type'][];
if (item.type && types.every((i) => i !== item.type)) {
throw new Error(`第 ${i + 1} 个题库的 type 必须为以下选项中的一个 ${types.join(', ')}`);
}
}
return aw;
} else {
throw new Error('题库为空!');
}
} else {
throw new Error('题库配置格式错误!');
}
}
45 changes: 45 additions & 0 deletions packages/core/src/core/utils/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { RawElements, SearchedElements } from '../worker/interface';

/**
* 与 {@link domSearchAll } 相同,区别是这个只返回单个元素,而不是一个元素数组
* @param root
* @param wrapper
* @returns
*/
export function domSearch<E extends RawElements>(
/** 搜索构造器 */
wrapper: E,
root: HTMLElement | Document = window.document
): SearchedElements<E, HTMLElement | null> {
const obj = Object.create({});
Reflect.ownKeys(wrapper).forEach((key) => {
Reflect.set(obj, key, root.querySelector(wrapper[key.toString()]));
});
return obj;
}

/**
* 元素搜索
*
* @example
*
* const { title , btn , arr } = domSearch(document.body,{
* title: '.title'
* btn: ()=> '.btn',
* arr: ()=> Array.from(document.body.querySelectorAll('.function-arr'))
* })
*
* console.log(title) // 等价于 Array.from(document.body.querySelectorAll('.title'))
* console.log(btn)// 等价于 Array.from(document.body.querySelectorAll('.btn'))
*/
export function domSearchAll<E extends RawElements>(
/** 搜索构造器 */
wrapper: E,
root: HTMLElement | Document = window.document
): SearchedElements<E, HTMLElement[]> {
const obj = Object.create({});
Reflect.ownKeys(wrapper).forEach((key) => {
Reflect.set(obj, key, Array.from(root.querySelectorAll(wrapper[key.toString()])));
});
return obj;
}
75 changes: 75 additions & 0 deletions packages/core/src/core/utils/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { isInBrowser } from '../../utils/common';

/**
* 发起请求
* @param url 请求地址
* @param opts 请求参数
*/
export function request<T extends 'json' | 'text'>(
url: string,
opts: {
type: 'fetch' | 'GM_xmlhttpRequest';
method?: 'get' | 'post';
contentType?: T;
headers?: Record<string, string>;
data?: Record<string, string>;
}
): Promise<T extends 'json' ? Record<string, any> : string> {
return new Promise((resolve, reject) => {
try {
/** 默认参数 */
const { contentType = 'json', method = 'get', type = 'fetch', data = {}, headers = {} } = opts || {};
/** 环境变量 */
const env = isInBrowser() ? 'browser' : 'node';

/** 如果是跨域模式并且是浏览器环境 */
if (type === 'GM_xmlhttpRequest' && env === 'browser') {
if (typeof GM_xmlhttpRequest !== 'undefined') {
// eslint-disable-next-line no-undef
GM_xmlhttpRequest({
url,
method: method === 'get' ? 'GET' : 'POST',
data: new URLSearchParams(data).toString(),
headers: headers,
responseType: 'json',
onload: (response) => {
if (response.status === 200) {
if (contentType === 'json') {
try {
resolve(JSON.parse(response.responseText));
} catch (error) {
reject(error);
}
} else {
resolve(response.responseText as any);
}
} else {
reject(response.responseText);
}
},
onerror: reject
});
} else {
reject(new Error('GM_xmlhttpRequest is not defined'));
}
} else {
const fet: (...args: any[]) => Promise<Response> = env === 'node' ? require('node-fetch').default : fetch;

fet(url, { contentType, body: method === 'post' ? data : undefined, method, headers })
.then((response) => {
if (contentType === 'json') {
response.json().then(resolve).catch(reject);
} else {
// @ts-ignore
response.text().then(resolve).catch(reject);
}
})
.catch((error) => {
reject(new Error(error));
});
}
} catch (error) {
reject(error);
}
});
}
52 changes: 52 additions & 0 deletions packages/core/src/core/utils/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { findBestMatch, Rating } from 'string-similarity';

/**
* 删除特殊字符, 并且全部转小写,只保留英文,数字,中文
* @param str
* @returns
*/
export function clearString(str: string, ...exclude: string[]) {
return str
.trim()
.toLocaleLowerCase()
.replace(RegExp(`[^\\u4e00-\\u9fa5A-Za-z0-9${exclude.join('')}]*`, 'g'), '');
}

/**
* 答案相似度匹配 , 返回相似度对象列表 Array<{@link Rating}>
*
* 相似度计算算法 : https://www.npmjs.com/package/string-similarity
*
* @param answers 答案列表
* @param options 选项列表
*
*
* @example
*
* ```js
*
* answerSimilar( ['3'], ['1+2','3','4','错误的例子'] ) // [0, 1, 0, 0]
*
* answerSimilar( ['hello world','console.log("hello world")'], ['console.log("hello world")','hello world','1','错误的例子'] ) // [1, 1, 0, 0]
*
* ```
*
*/
export function answerSimilar(answers: string[], options: string[]): Rating[] {
answers = answers.map(removeRedundant);
options = options.map(removeRedundant);

const similar =
answers.length !== 0
? options.map((option) => findBestMatch(option, answers).bestMatch)
: options.map((opt) => ({ rating: 0, target: '' } as Rating));

return similar;
}

/**
* 删除题目选项中开头的冗余字符串
*/
export function removeRedundant(str: string) {
return str?.trim().replace(/[A-Z]{1}[^A-Za-z0-9\u4e00-\u9fa5]+([A-Za-z0-9\u4e00-\u9fa5]+)/, '$1') || '';
}
Loading

0 comments on commit f07dd0f

Please sign in to comment.