, ...UnwrapRefArray]
216 | : [UnwrapRef]
217 | : [];
218 |
219 | /**
220 | * @description ref
221 | * @param v
222 | * @returns
223 | */
224 | const isRef = (v: Ref | unknown): v is Ref => {
225 | return !!(v && v[ReactiveFlags.IS_REF]);
226 | };
227 |
228 | /**
229 | * @description 浅层 shallow
230 | * @param v
231 | * @returns
232 | */
233 | const isShallow = (v: unknown) => {
234 | return !!(v && v[ReactiveFlags.IS_SHALLOW]);
235 | };
236 |
237 | /**
238 | * @description 创建 ref
239 | * @param v
240 | * @returns
241 | */
242 | const createRef = (rawVal: T, shallow: boolean) => {
243 | return new Ref(rawVal, shallow);
244 | };
245 |
246 | /**
247 | * @description 解除 ref
248 | * @param val
249 | * @returns
250 | */
251 | const unref = (val: T) => {
252 | return >(isRef(val) ? val.value : val);
253 | };
254 |
255 | /**
256 | * @description 顶层 ref
257 | * @param v
258 | * @returns
259 | */
260 | const ref = (value: T): Ref> => {
261 | return isRef>(value)
262 | ? value
263 | : createRef(>value, true);
264 | };
265 |
266 | /**
267 | * @description ref
268 | * @param value
269 | * @returns
270 | */
271 | const shallowRef = (value: T): Ref> => {
272 | return isRef>(value)
273 | ? value
274 | : createRef(>value, false);
275 | };
276 |
277 | /**
278 | * @description 创建处理 reactive
279 | * @param isReadonly
280 | * @param isShallow
281 | * @returns
282 | */
283 | const createReactiveHandlers = (isReadonly: boolean, isShallow: boolean) => {
284 | return {
285 | get: createGetters(isReadonly, isShallow),
286 | set: createSetters(isReadonly, isShallow),
287 | };
288 | };
289 |
290 | /**
291 | * @description getters
292 | * @param isReadonly
293 | * @param isShallow
294 | * @returns
295 | */
296 | const createGetters = (isReadonly: boolean, isShallow: boolean) => {
297 | return function get(target, key, receiver) {
298 | if (key === ReactiveFlags.IS_REACTIVE) {
299 | return !isReadonly;
300 | }
301 | if (key === ReactiveFlags.IS_READONLY) {
302 | return isReadonly;
303 | }
304 | if (key === ReactiveFlags.IS_SHALLOW) {
305 | return isShallow;
306 | }
307 | // 结果
308 | const res = Reflect.get(target, key, receiver);
309 | if (!isReadonly) {
310 | // 收集依赖
311 | track(target, key);
312 | }
313 | if (isShallow) {
314 | return res;
315 | }
316 | if (isRef(res)) {
317 | return res.value;
318 | }
319 | if (res && typeof res === 'object') {
320 | if (res instanceof Element) {
321 | return res;
322 | }
323 | return isReadonly ? readonly(res) : reactive(res);
324 | }
325 | return res;
326 | };
327 | };
328 |
329 | /**
330 | * @description setters
331 | * @param readonly
332 | * @param shallow
333 | * @returns
334 | */
335 | const createSetters = (readonly: boolean, shallow: boolean) => {
336 | return function set(target, key, newVal, receiver) {
337 | // 只读
338 | if (readonly) {
339 | return false;
340 | }
341 | // 旧值
342 | const oldVal = target[key];
343 | if (isReadonly(oldVal) && isRef(oldVal) && !isRef(newVal)) {
344 | return false;
345 | }
346 | if (!shallow) {
347 | if (isRef(oldVal) && !isRef(newVal)) {
348 | oldVal.value = newVal;
349 | return true;
350 | }
351 | }
352 | const res = Reflect.set(target, key, newVal, receiver);
353 | // length
354 | if (Array.isArray(target) && key === 'length') {
355 | // 通知依赖
356 | trigger(target, key, newVal, oldVal);
357 | return res;
358 | }
359 | // 数据变化
360 | if (oldVal !== newVal) {
361 | // 通知依赖
362 | trigger(target, key, newVal, oldVal);
363 | }
364 | return res;
365 | };
366 | };
367 |
368 | // 响应式
369 | type Reactive = {
370 | _isReactive: boolean;
371 | _isReadonly: boolean;
372 | } & T;
373 |
374 | /**
375 | * @description reactive object
376 | */
377 | const createReactiveObj = (
378 | target: T,
379 | isReadonly: boolean,
380 | shallow: boolean
381 | ) => {
382 | // 存在 Proxy
383 | const existingProxy = proxyMap.get(target);
384 | if (existingProxy) {
385 | return >existingProxy;
386 | }
387 | // 新建
388 | const proxy = new Proxy(target, createReactiveHandlers(isReadonly, shallow));
389 | proxyMap.set(target, proxy);
390 | return >proxy;
391 | };
392 |
393 | /**
394 | * @description reactive
395 | * @param val
396 | * @returns
397 | */
398 | const isReactive = (val: unknown): val is boolean => {
399 | return !!(val && val[ReactiveFlags.IS_REACTIVE]);
400 | };
401 |
402 | /**
403 | * @description 创建 reactive
404 | * @param target
405 | * @returns
406 | */
407 | const createReactive = (target: T): Reactive => {
408 | return createReactiveObj(target, false, false);
409 | };
410 |
411 | /**
412 | * @description 顶层 reactive
413 | * @param target
414 | * @returns
415 | */
416 | const shallowReactive = (target: T) => {
417 | return createReactiveObj(target, false, true);
418 | };
419 |
420 | /**
421 | * @description reactive
422 | * @param val
423 | * @returns
424 | */
425 | const isReadonly = (val: unknown): val is object => {
426 | return !!(val && val[ReactiveFlags.IS_READONLY]);
427 | };
428 |
429 | /**
430 | * @description 创建 readonly
431 | * @param target
432 | * @returns
433 | */
434 | const createReadonly = (target: T): T => {
435 | return createReactiveObj(target, true, false);
436 | };
437 |
438 | /**
439 | * @description 顶层 readonly
440 | * @param target
441 | * @returns
442 | */
443 | const shallowReadonly = (target: T) => {
444 | return createReactiveObj(target, true, true);
445 | };
446 |
447 | /**
448 | * @description proxy
449 | * @param val
450 | * @returns
451 | */
452 | const isProxy = (val: unknown): val is Reactive => {
453 | return isReactive(val) || isReadonly(val);
454 | };
455 |
456 | /**
457 | * @description reactive
458 | * @param target
459 | * @returns
460 | */
461 | const reactive = (target: T) => {
462 | return createReactive(target);
463 | };
464 |
465 | /**
466 | * @description readonly
467 | * @param target
468 | * @returns
469 | */
470 | const readonly = (target: T) => {
471 | return createReadonly(target);
472 | };
473 |
474 | /**
475 | * @description 监听数据变化
476 | * @param source
477 | * @param callback
478 | */
479 | const watch = any)>(
480 | source: T,
481 | callback: (
482 | newValue: T extends Ref[]
483 | ? UnwrapRefArray
484 | : T extends () => infer P
485 | ? P extends Ref[]
486 | ? UnwrapRefArray
487 | : P
488 | : UnwrapRef,
489 | oldValue: T extends Ref[]
490 | ? UnwrapRefArray
491 | : T extends () => infer P
492 | ? P extends Ref[]
493 | ? UnwrapRefArray
494 | : P
495 | : UnwrapRef
496 | ) => void,
497 | immediate: boolean = false
498 | ) => {
499 | // 立刻执行
500 | immediate && callback(unref(source), unref(source));
501 | // array
502 | if (Array.isArray(source) && source.every((s) => isRef(s))) {
503 | for (const i in source) {
504 | // Proxy
505 | if (isProxy(source[i])) {
506 | watch(source[i], () => {
507 | const res = source.map((s) => unref(s));
508 | callback(res, res);
509 | });
510 | }
511 | }
512 | watch<() => any>(() => source.map((s) => unref(s)), callback);
513 | return;
514 | }
515 | // function
516 | if (source instanceof Function) {
517 | watch[(watchEffectRef(source), (n, o) => {
518 | callback(unref(n), unref(o));
519 | });
520 | return;
521 | }
522 | // Proxy
523 | if (isProxy(source)) {
524 | for (const key in source) {
525 | currentSub = () => {
526 | callback(source, source);
527 | };
528 | // sub source
529 | const subSource = source[key];
530 | currentSub = undefined;
531 | watch(subSource, () => {
532 | callback(source, source);
533 | });
534 | }
535 | return;
536 | }
537 | // Ref
538 | if (isRef(source)) {
539 | // Ref.value Proxy
540 | if (isProxy(source.value)) {
541 | watch(source.value, () => {
542 | callback(unref(source), unref(source));
543 | });
544 | }
545 | currentSub = callback;
546 | // 收集依赖
547 | trackRef(source);
548 | currentSub = undefined;
549 | return;
550 | }
551 | };
552 |
553 | /**
554 | * @description 监听数据变化影响
555 | * @param callback
556 | * @returns
557 | */
558 | const watchEffect = (callback: () => any) => {
559 | currentSub = callback;
560 | // 收集依赖
561 | callback();
562 | currentSub = undefined;
563 | };
564 |
565 | /**
566 | * @description 监听影响 ref
567 | * @param refVal
568 | * @param callback
569 | * @returns
570 | */
571 | const watchRef = any), P>(
572 | source: T,
573 | callback: () => P
574 | ) => {
575 | // 收集依赖
576 | const effectRes = shallowRef](callback());
577 | // 监听
578 | watch(source, () => (effectRes.value = unref(callback())));
579 | return effectRes;
580 | };
581 |
582 | /**
583 | * @description 监听影响 ref
584 | * @param refVal
585 | * @param callback
586 | * @returns
587 | */
588 | const watchEffectRef = (callback: () => T) => {
589 | // 收集依赖
590 | const effectRes = shallowRef(undefined);
591 | // 监听
592 | watchEffect(() => (effectRes.value = unref(callback())));
593 | return [>>effectRes;
594 | };
595 |
596 | export {
597 | Ref,
598 | ref,
599 | shallowRef,
600 | unref,
601 | isRef,
602 | watch,
603 | watchRef,
604 | watchEffectRef,
605 | watchEffect,
606 | reactive,
607 | shallowReactive,
608 | isReactive,
609 | readonly,
610 | shallowReadonly,
611 | isReadonly,
612 | isProxy,
613 | isShallow,
614 | Reactive,
615 | };
616 |
--------------------------------------------------------------------------------
/src/utils/log.ts:
--------------------------------------------------------------------------------
1 | import { formatDateTime } from './time';
2 |
3 | /**
4 | * @description 打印日志
5 | * @param text
6 | */
7 | function log(...text: any[]) {
8 | printColor('dodgerblue', ...text);
9 | }
10 |
11 | /**
12 | * @description 打印错误
13 | * @param text
14 | */
15 | function error(...text: any[]) {
16 | printColor('red', ...text);
17 | }
18 |
19 | /**
20 | * @description 打印信息
21 | * @param text
22 | */
23 | function info(...text: any[]) {
24 | printColor('yellow', ...text);
25 | }
26 |
27 | /**
28 | * @description 打印颜色
29 | * @param text
30 | * @param color
31 | */
32 | function printColor(color: string, ...text: any[]) {
33 | const textFormatted = text
34 | .map((t) => (typeof t === 'object' ? JSON.stringify(t) : String(t)))
35 | .join(' ');
36 | console.log(
37 | `%c[${formatDateTime()}] %c${textFormatted}`,
38 | '',
39 | `color: ${color}`
40 | );
41 | }
42 |
43 | export { log, error, info };
44 |
--------------------------------------------------------------------------------
/src/utils/push.ts:
--------------------------------------------------------------------------------
1 | import { pushPlus } from '../api/push';
2 | import { formatDateTime } from './time';
3 | /**
4 | * @description 消息模板类型
5 | */
6 | type TemplateType = 'html' | 'txt' | 'json' | 'markdown' | 'cloudMonitor';
7 |
8 | /**
9 | * @description 推送选项
10 | */
11 | type PushOptions = {
12 | title: string;
13 | content: string;
14 | template: TemplateType;
15 | toToken?: string;
16 | fromToken: string;
17 | };
18 |
19 | /**
20 | * @description 模态框
21 | */
22 | type ModalOptions = {
23 | title: string;
24 | subTitle?: string;
25 | content: string | string[];
26 | to?: string;
27 | from?: string;
28 | type: ModalType;
29 | };
30 |
31 | /**
32 | * @description 类型
33 | */
34 | type ModalType = 'info' | 'warn' | 'fail' | 'success';
35 |
36 | /**
37 | * @description html进度条
38 | * @param title
39 | * @param percent
40 | * @returns
41 | */
42 | function getProgressHTML(title: string, current: number, total: number) {
43 | // html
44 | const progressHTML = `]
52 | ${title}
53 | ${getHighlightHTML(`${current}`)} / ${total}
54 |
55 | `;
73 | return progressHTML;
74 | }
75 | /**
76 | * @description html高亮文本
77 | * @param text
78 | * @returns
79 | */
80 | function getHighlightHTML(text: string | number) {
81 | // html
82 | const highlightHTML = `${text}`;
83 | return highlightHTML;
84 | }
85 | /**
86 | * @description 二维码
87 | * @param src
88 | */
89 | function getImgHTML(src: string) {
90 | // 图片
91 | return `
92 |
93 |
103 |

104 |
105 |
106 | `;
107 | }
108 | /**
109 | * @description 创建模态框
110 | * @param options 选项
111 | * @returns
112 | */
113 | function createModal(options: ModalOptions) {
114 | // 配置
115 | const {
116 | title,
117 | subTitle = '',
118 | to = '用户',
119 | content,
120 | type,
121 | from = 'tech-study.js',
122 | } = options;
123 | // 内容文本
124 | let contentText = '';
125 | if (Array.isArray(content)) {
126 | contentText = content.map((ct) => `${ct}
`).join('');
127 | } else {
128 | contentText = content;
129 | }
130 | // 日期
131 | const dateTime = formatDateTime();
132 | // 类型html
133 | let typeHTML = '';
134 | if (type && type.length) {
135 | if (type === 'info') {
136 | typeHTML = `
137 | `;
147 | }
148 | if (type === 'warn') {
149 | typeHTML = `
150 |
160 | `;
161 | }
162 | if (type === 'success') {
163 | typeHTML = `
164 |
174 | `;
175 | }
176 | if (type === 'fail') {
177 | typeHTML = `
178 |
188 | `;
189 | }
190 | }
191 | // 类型
192 | const typeWrap = `
193 |
201 | ${typeHTML}
202 |
203 | `;
204 | // 基础html
205 | const baseHTML = `
206 |
214 |
223 |
231 |
232 | ${typeWrap}
233 | ${title}
234 |
235 |
${subTitle}
236 |
237 |
238 |
239 |
240 |
241 | ${getHighlightHTML(to)}, 你好!
242 |
243 |
${contentText}
244 |
245 |
255 |
${dateTime}
256 |
257 | 来自
258 | ${from}
259 |
260 |
261 |
262 |
263 | `;
264 | return baseHTML;
265 | }
266 |
267 | /**
268 | * @description 推送消息
269 | */
270 | async function pushMessage(options: PushOptions) {
271 | // 选项
272 | const { title, content, template, fromToken, toToken } = options;
273 | // 推送
274 | const res = await pushPlus(fromToken, title, content, template, toToken);
275 | return res;
276 | }
277 | /**
278 | * @description 推送模态框
279 | */
280 | async function pushModal(
281 | options: ModalOptions,
282 | fromToken: string,
283 | toToken?: string
284 | ) {
285 | // html
286 | const html = createModal(options);
287 | // 推送
288 | const res = await pushMessage({
289 | title: '消息提示',
290 | content: html,
291 | fromToken,
292 | toToken,
293 | template: 'html',
294 | });
295 | if (res && res.code === 200) {
296 | return res;
297 | }
298 | return;
299 | }
300 |
301 | export {
302 | createModal,
303 | getHighlightHTML,
304 | getImgHTML,
305 | getProgressHTML,
306 | pushModal,
307 | };
308 |
--------------------------------------------------------------------------------
/src/utils/random.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 点
3 | */
4 | type Point = { x: number; y: number };
5 |
6 | /**
7 | * @description 范围
8 | */
9 | type Bounds = { x: number; y: number; width: number; height: number };
10 | /**
11 | * @description 创建随机点
12 | * @param bounds 范围
13 | * @returns
14 | */
15 | function createRandomPoint(bounds: Bounds): Point {
16 | // 范围
17 | const { x, y, width, height } = bounds;
18 | // 横坐标
19 | const randX = x + Math.random() * width * 0.5 + width * 0.25;
20 | // 纵坐标
21 | const randY = y + Math.random() * height * 0.5 + height * 0.25;
22 | return {
23 | x: randX,
24 | y: randY,
25 | };
26 | }
27 |
28 | /**
29 | * @description 生成随机路径
30 | * @param start
31 | * @param end
32 | * @param steps
33 | * @returns
34 | */
35 | function createRandomPath(start: Point, end: Point, steps: number) {
36 | // 最小水平增量
37 | const minDeltaX = (end.x - start.x) / steps;
38 | // 最大垂直增量
39 | const maxDeltaY = (end.y - start.y) / steps;
40 |
41 | const path: Point[] = [];
42 | // 开始节点
43 | path.push(start);
44 | // 插入点
45 | for (let i = 0; i < steps; i++) {
46 | // 横坐标
47 | const x = path[i].x + Math.random() * 5 + minDeltaX;
48 | // 纵坐标
49 | const y =
50 | path[i].y +
51 | Math.random() * 5 * Math.pow(-1, ~~(Math.random() * 2 + 1)) +
52 | maxDeltaY;
53 | path.push({
54 | x,
55 | y,
56 | });
57 | }
58 | return path;
59 | }
60 | /**
61 | * @description 随机数字
62 | * @returns
63 | */
64 | function generateNumAsChar(): string {
65 | return (~~(Math.random() * 10)).toString();
66 | }
67 | /**
68 | * @description 随机大写字母
69 | * @returns
70 | */
71 | function generateUpperAsChar(): string {
72 | return String.fromCharCode(~~(Math.random() * 26) + 65);
73 | }
74 | /**
75 | * @description 随机小写字母
76 | * @returns
77 | */
78 | function generateLowerAsChar(): string {
79 | return String.fromCharCode(~~(Math.random() * 26) + 97);
80 | }
81 | /**
82 | * @description 随机混合字符
83 | * @param length
84 | * @returns
85 | */
86 | function generateMix(length: number = 6): string {
87 | // 随机字符串
88 | const randomText: string[] = [];
89 | // 生成器
90 | const typeGenerator: (() => string)[] = [
91 | generateNumAsChar,
92 | generateUpperAsChar,
93 | generateLowerAsChar,
94 | ];
95 | if (length) {
96 | for (let i = 0; i < length; i++) {
97 | // 随机位置
98 | const randomIndex = ~~(Math.random() * typeGenerator.length);
99 | randomText.push(typeGenerator[randomIndex]());
100 | }
101 | }
102 | return randomText.join('');
103 | }
104 |
105 | export { createRandomPoint, createRandomPath, generateMix };
106 |
--------------------------------------------------------------------------------
/src/utils/time.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 格式化日期时间数字
3 | * @param num
4 | * @returns
5 | */
6 | function formatDateNum(num: number) {
7 | return num < 10 ? `0${num}` : `${num}`;
8 | }
9 |
10 | /**
11 | * @description 格式化日期时间
12 | * @param time
13 | * @returns
14 | * @example
15 | * formatDateTime() -> "2022-09-01 08:00:00"
16 | * formatDateTime(new Date()) -> "2022-09-01 08:00:00"
17 | * formatDateTime(Date.now()) -> "2022-09-01 08:00:00"
18 | */
19 | function formatDateTime(time: Date | string | number = Date.now()) {
20 | const date = new Date(time);
21 | const s = date.getSeconds();
22 | const min = date.getMinutes();
23 | const h = date.getHours();
24 | const d = date.getDate();
25 | const m = date.getMonth() + 1;
26 | const y = date.getFullYear();
27 | // 日期
28 | const dateText = [y, m, d].map(formatDateNum).join('-');
29 | // 时间
30 | const timeText = [h, min, s].map(formatDateNum).join(':');
31 | // 日期时间
32 | const dateTimeText = `${dateText} ${timeText}`;
33 | return dateTimeText;
34 | }
35 |
36 | /**
37 | * @description 格式化时间
38 | * @param time
39 | * @returns
40 | * @example
41 | * formatTime() -> "08:00:00"
42 | * formatTime(new Date()) -> "08:00:00"
43 | * formatTime(Date.now()) -> "08:00:00"
44 | */
45 | const formatTime = (time: Date | string | number = Date.now()) => {
46 | const date = new Date(time);
47 | const s = date.getSeconds();
48 | const min = date.getMinutes();
49 | const h = date.getHours();
50 | // 时间
51 | const timeText = [h, min, s].map(formatDateNum).join(':');
52 | return timeText;
53 | };
54 |
55 | /**
56 | * @description 时间已过
57 | * @param hour
58 | * @param minute
59 | * @returns
60 | */
61 | function isLate({ hour, minute }: { hour: number; minute: number }) {
62 | const date = new Date();
63 | const h = date.getHours();
64 | const min = date.getMinutes();
65 | return h > hour || (h === hour && min >= minute);
66 | }
67 |
68 | /**
69 | * @description 时间已过
70 | * @param hour
71 | * @param minute
72 | * @returns
73 | */
74 | function isNow({ hour, minute }: { hour: number; minute: number }) {
75 | const date = new Date();
76 | const h = date.getHours();
77 | const min = date.getMinutes();
78 | const s = date.getSeconds();
79 | return h === hour && min === minute && s === 0;
80 | }
81 |
82 | export { formatDateNum, formatDateTime, isLate, isNow };
83 |
--------------------------------------------------------------------------------
/src/utils/utils.ts:
--------------------------------------------------------------------------------
1 | import { log } from './log';
2 |
3 | /* 工具函数 */
4 |
5 | /**
6 | * @description 设置cookie
7 | * @param name
8 | * @param value
9 | * @param expires
10 | */
11 | function setCookie(
12 | name: string,
13 | value: string,
14 | expires: number,
15 | domain: string
16 | ) {
17 | // 当前日期
18 | const date = new Date();
19 | // 过期日期
20 | date.setTime(date.getTime() + expires);
21 | // 设置cookie
22 | document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;domain=${domain}`;
23 | }
24 |
25 | /**
26 | * @description 获取cookie
27 | * @param name
28 | * @returns
29 | */
30 | function getCookie(name: string) {
31 | // 获取当前所有cookie
32 | const strCookies = document.cookie;
33 | // 截取变成cookie数组
34 | const cookieText = strCookies.split(';');
35 | // 循环每个cookie
36 | for (const i in cookieText) {
37 | // 将cookie截取成两部分
38 | const item = cookieText[i].split('=');
39 | // 判断cookie的name 是否相等
40 | if (item[0].trim() === name) {
41 | return item[1].trim();
42 | }
43 | }
44 | return null;
45 | }
46 |
47 | /**
48 | * @description 删除cookie
49 | * @param name
50 | */
51 | function delCookie(name: string, domain: string) {
52 | // 存在cookie
53 | const value = getCookie(name);
54 | if (value !== null) {
55 | setCookie(name, '', -1, domain);
56 | }
57 | }
58 |
59 | /**
60 | * @description 防抖
61 | * @param callback
62 | * @param delay
63 | * @returns
64 | */
65 | function debounce any>(callback: T, delay: number) {
66 | let timer = -1;
67 | return function (this: any, ...args: Parameters) {
68 | if (timer !== -1) {
69 | clearTimeout(timer);
70 | }
71 | timer = setTimeout(() => {
72 | callback.apply(this, args);
73 | }, delay);
74 | };
75 | }
76 |
77 | /**
78 | * @description 判断是否为移动端
79 | * @returns
80 | */
81 | function hasMobile() {
82 | let isMobile = false;
83 | if (
84 | navigator.userAgent.match(
85 | /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
86 | )
87 | ) {
88 | log('移动端');
89 | isMobile = true;
90 | }
91 | if (document.body.clientWidth < 800) {
92 | log('小尺寸设备端');
93 | isMobile = true;
94 | }
95 | return isMobile;
96 | }
97 |
98 | /**
99 | * @description 等待时间
100 | * @param time
101 | * @returns
102 | */
103 | function sleep(time: number) {
104 | // 延时
105 | let timeDelay = Number(time);
106 | if (!Number.isInteger(timeDelay)) {
107 | timeDelay = 1000;
108 | }
109 | timeDelay += Math.random() * 500 - 250;
110 | return new Promise((resolve) => {
111 | setTimeout(() => {
112 | resolve(undefined);
113 | }, timeDelay);
114 | });
115 | }
116 |
117 | /**
118 | * @description 暂停学习锁
119 | */
120 | function studyPauseLock(callback?: (msg: boolean) => void) {
121 | return new Promise((resolve) => {
122 | // 暂停
123 | const pauseStudy = GM_getValue('pauseStudy') || false;
124 | if (pauseStudy) {
125 | const doing = setInterval(() => {
126 | // 暂停
127 | const pauseStudy = GM_getValue('pauseStudy') || false;
128 | if (!pauseStudy) {
129 | // 停止定时器
130 | clearInterval(doing);
131 | log('学习等待结束!');
132 | if (callback && callback instanceof Function) {
133 | callback(true);
134 | }
135 | resolve(true);
136 | return;
137 | }
138 | if (callback && callback instanceof Function) {
139 | callback(false);
140 | }
141 | log('学习等待...');
142 | }, 500);
143 | return;
144 | }
145 | resolve(true);
146 | });
147 | }
148 |
149 | /**
150 | * @description 加载
151 | * @param match
152 | * @param callback
153 | */
154 | function load(
155 | match: string | RegExp | ((href: string) => any) | boolean,
156 | callback: () => void
157 | ) {
158 | // 链接
159 | const { href } = window.location;
160 | window.addEventListener('load', () => {
161 | // 函数
162 | if (match instanceof Function) {
163 | match(href) && callback();
164 | return;
165 | }
166 | // 布尔
167 | if (typeof match === 'boolean') {
168 | match && callback();
169 | return;
170 | }
171 | // 字符正则
172 | if (href.match(match)) {
173 | callback();
174 | return;
175 | }
176 | });
177 | }
178 |
179 | export {
180 | debounce,
181 | delCookie,
182 | getCookie,
183 | hasMobile,
184 | load,
185 | setCookie,
186 | sleep,
187 | studyPauseLock,
188 | };
189 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
15 | "lib": [
16 | "DOM",
17 | "ESNext"
18 | ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
19 | // "jsx": "preserve", /* Specify what JSX code is generated. */
20 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
21 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
22 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
23 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
24 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
25 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
26 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
27 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
28 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
29 |
30 | /* Modules */
31 | "module": "CommonJS" /* Specify what module code is generated. */,
32 | // "rootDir": "./", /* Specify the root folder within your source files. */
33 | "moduleResolution": "Node" /* Specify how TypeScript looks up a file from a given module specifier. */,
34 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
35 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
36 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
37 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
38 | // "types": [] /* Specify type package names to be included without being referenced in a source file. */,
39 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
40 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
41 | // "resolveJsonModule": true, /* Enable importing .json files. */
42 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
43 |
44 | /* JavaScript Support */
45 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
46 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
47 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
48 |
49 | /* Emit */
50 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
51 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
52 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
53 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
54 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
55 | // "outDir": "./", /* Specify an output folder for all emitted files. */
56 | // "removeComments": true, /* Disable emitting comments. */
57 | // "noEmit": true, /* Disable emitting files from a compilation. */
58 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
59 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
60 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
61 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
62 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
63 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
64 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
65 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
66 | // "newLine": "crlf", /* Set the newline character for emitting files. */
67 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
68 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
69 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
70 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
71 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
72 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
73 |
74 | /* Interop Constraints */
75 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
76 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
77 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
78 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
79 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
80 |
81 | /* Type Checking */
82 | "strict": true /* Enable all strict type-checking options. */,
83 | "noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
84 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
85 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
86 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
87 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
88 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
89 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
90 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
91 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
92 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
93 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
94 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
95 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
96 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
97 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
98 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
99 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
100 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
101 |
102 | /* Completeness */
103 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
104 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
105 | },
106 | "include": ["bin/*.ts", "src/**/*", "types/**/*.d.ts"]
107 | }
108 |
--------------------------------------------------------------------------------
/types/global.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | module '*?raw' {
3 | const text: string;
4 | export default text;
5 | }
6 | function md5(value: string): string;
7 | }
8 |
9 | export {};
10 |
--------------------------------------------------------------------------------