Full Code of Xu22Web/tech-study-js for AI

master a1da2057696a cached
56 files
861.2 KB
243.7k tokens
429 symbols
1 requests
Download .txt
Showing preview only (942K chars total). Download the full file or copy to clipboard to get everything.
Repository: Xu22Web/tech-study-js
Branch: master
Commit: a1da2057696a
Files: 56
Total size: 861.2 KB

Directory structure:
gitextract_aws1pt39/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── bin/
│   └── index.ts
├── cache.json
├── package.json
├── src/
│   ├── api/
│   │   ├── answer.ts
│   │   ├── data.ts
│   │   ├── login.ts
│   │   ├── push.ts
│   │   └── user.ts
│   ├── component/
│   │   ├── ExamBtn.ts
│   │   ├── Frame.ts
│   │   ├── Hr.ts
│   │   ├── InfoItem.ts
│   │   ├── LoginItem.ts
│   │   ├── NoramlItem.ts
│   │   ├── Panel.ts
│   │   ├── ScheduleList.ts
│   │   ├── ScoreItem.ts
│   │   ├── Select.ts
│   │   ├── SettingsPanel.ts
│   │   ├── TaskBtn.ts
│   │   ├── TaskItem.ts
│   │   ├── TaskList.ts
│   │   ├── TimeInput.ts
│   │   └── Tip.ts
│   ├── config/
│   │   ├── api.ts
│   │   ├── compile.ts
│   │   ├── script.ts
│   │   ├── task.ts
│   │   ├── url.ts
│   │   └── version.ts
│   ├── controller/
│   │   ├── exam.ts
│   │   ├── frame.ts
│   │   ├── login.ts
│   │   ├── readAndWatch.ts
│   │   ├── schedule.ts
│   │   ├── tip.ts
│   │   └── user.ts
│   ├── css/
│   │   └── index.css
│   ├── index.js
│   ├── index.ts
│   ├── shared/
│   │   └── index.ts
│   ├── types/
│   │   └── index.ts
│   └── utils/
│       ├── composition.ts
│       ├── element.ts
│       ├── log.ts
│       ├── push.ts
│       ├── random.ts
│       ├── time.ts
│       └── utils.ts
├── tech-study.js
├── tsconfig.json
└── types/
    └── global.d.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms



================================================
FILE: .gitignore
================================================
node_modules

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 Xu22Web

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# tech-study-js

### 原仓库

> https://github.com/TechXueXi/techxuexi-js

基于原作者的原仓库,自行改进和完善

### 描述 Description

- 灵活且貌似轻量的 `学习强国` 油猴脚本。

- 与此同时,提供更加便捷的版本选择

  - [Node.js 版](https://github.com/Xu22Web/tech-study-node 'Node.js 版')

  - [Docker 版](https://github.com/Xu22Web/tech-study-docker 'Docker 版')

### 交流群 Telegram Group

- 链接: [tech-study 互动群](https://t.me/+IJ_YzNc-Iew0MGRl)

- 二维码:

  <img src="./images/group.png" style="width:200px;" alt="Telegram邀请二维码">

注:介于脚本国内敏感,暂时不提供其他交流互动方式。

### 用法 Usage

1. 装个浏览器插件`Tampermonkey`

   1. Microsoft Edge: [插件安装](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd?hl=zh-CN)

   2. Google Chrome: [插件安装](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=zh)

2. 点击插件里添加按钮,去掉编辑框里原来的代码,复制 [tech-study.js](https://raw.githubusercontent.com/Xu22Web/tech-study-js/master/tech-study.js) 脚本,粘贴进编辑框保存。

3. 开启这个脚本,然后进入网页强国 https://www.xuexi.cn 。

### 优化 Promote

1. 优化整体交互设计,新增一体式扫码登录

2. 新增用户信息显示,包括昵称、头像、总分以及当天分数

3. 新增任务进度以及任务分数详情显示,任务情况清晰明了

4. 优化答题逻辑,新增滑动验证,远离验证烦恼

5. 新增同屏任务以及静默运行,仅需一个页面即可静默运行任务

6. 兼容桌面端以及移动端,手机电脑均可运行(设备均需支持油猴脚本,此外,移动端需要开启同屏任务)

7. 新增定时任务以及远程推送,定时刷新页面,远程微信推送登录二维码

### 使用流程 Process

1. 用户`登录`

   <img src="./images/login.png" style="width:200px;" alt="登录">

2. 点击 `开始学习`,等待完成任务运行

   - `桌面端`运行

     <img src="./images/pc.png" style="width:1000px;" alt="桌面端运行">

   - `移动端`运行

      <img src="./images/mobile.png" style="width:320px;" alt="移动端运行">

3. `完成学习`任务

   <img src="./images/done.png" style="width:300px;" alt="完成学习">

### 更新与维护 Update and Maintenance

1. 修复有声视频播放后,页面倒计时不继续的问题(注:不同标签页的有声视频需要用户交互才能播放,手动播放后会展示倒计时)

2. 与此同时,提供更加便捷的版本选择

   - [Node.js 版](https://github.com/Xu22Web/tech-study-node 'Node.js 版')

   - [Docker 版](https://github.com/Xu22Web/tech-study-docker 'Docker 版')

### 关于开发 Development

- 脚本配置

  1. 版本配置 `src/config/version.ts`

  2. 脚本配置 `src/config/script.ts`

  3. 编译配置 `src/config/compile.ts`

  4. 接口配置 `src/config/api.ts`

  5. 链接配置 `src/config/url.ts`

  6. 任务配置 `src/config/task.ts`

- CSS 文件

  `src/css/index.css`

  - 根据功能特性(i)

    ```js
    // 将文件'./css/index.css'文本内容赋值到'css'
    import css from './css/index.css?raw';
    ```

  - 根据 Tampermonkey API 函数

    ```js
    // 嵌入样式
    GM_addStyle(css);
    ```

- 脚本内容

  `src/tech-study.ts`

- 编译

  ```
  # 编译生成 'tech-study.js'
  pnpm build
  ```

  即

  ```
   ✔ 完成编译: index.ts -> index.js
   ✔ 已生成 用户脚本配置 注释!
   ✔ 完成编译: ./config/api.ts -> api.js
   ✔ 完成编译: ./config/url.ts -> url.js
   ✔ 完成编译: ./config/task.ts -> task.js

   ... ...

   ✔ 导出整合的脚本文件: tech-study.js
  ```

- 功能特性(基于`TypeScript Compiler API`)

  1. 包含`?raw`结尾的`import`语句

     ```
     import var from 'file?raw';
     ```

     1. 文件`file`文本内容赋值到`var`

     2. 此类型`import`语句不会被编译到结果

  2. 普通的`import`语句

     ```
     import { funName } from 'file';
     ```

     1. 文件`file`文本插入到主文件一起导出,相当于合并多个`*.ts`文件导出为一个`*.js`文件

     2. 此类型`import`语句不会被编译到结果

- 类似组合式接口(类似 `Composition API`)

  模拟 `ref`,`watch`,`watchEffect` 等 API。


================================================
FILE: bin/index.ts
================================================
import chalk from 'chalk';
import fs from 'fs';
import ora from 'ora';
import path from 'path';
import rollup from 'rollup';
import ts from 'typescript';
import COMPILE_CONFIG from '../src/config/compile';
import SCRIPT_CONFIG from '../src/config/script';
// 根目录 文件名
const { input, output, rollupConfig, compress } = COMPILE_CONFIG;
// 输入文件路径
const inputFilePath = input.file;
// 输入目录名
const inputDirPath = path.dirname(input.file);
// 输出文件路径
const outputFilePath = output.file;
// 输出目录名
const outputDirPath = path.dirname(output.file);
// 导入路径
const importFilePaths: string[] = [];
// 缓存
const cache: {
  [key: string]: { timeStamp: string; data: string; from: string; to: string };
} = JSON.parse(fs.readFileSync('cache.json', { encoding: 'utf-8' })) || {};

/**
 * @description 编译
 * @param filePath
 * @returns
 */
const handleCompile = async (
  filePath: string
): Promise<
  | {
      data: string;
      compileName: string;
      originName: string;
      originPath: string;
      originRelativePath: string;
    }
  | undefined
> => {
  // 全路径
  const fullFilePath = path.resolve(filePath);
  // 文件名
  const fileName = getFileName(filePath);
  // 创建配置
  const options = createOptions(fullFilePath);
  // 创建项目
  const program = ts.createIncrementalProgram(options);
  // 根据项目配置获取源文件
  const sourceFile = program.getSourceFile(fullFilePath);
  // 自定义处理流程
  const customTransformers: ts.CustomTransformers = {
    before: [transformerFactory],
  };
  return new Promise((resolve) => {
    // 编译生成
    program.emit(
      sourceFile,
      (name, text) => {
        // 编译后的文件数据
        const data = text.replace(/export (\{.*\}|default .*);/g, '');
        // 编译后的文件名
        const compileName = getFileName(name);

        resolve({
          data,
          compileName,
          originName: fileName,
          originPath: fullFilePath,
          originRelativePath: filePath,
        });
      },
      undefined,
      false,
      customTransformers
    );
  });
};
/**
 * @description 解析模块
 * @param filePath
 * @returns
 */
const resoveModule = async (
  rawModulePath: string
): Promise<{
  modulePath: string;
  time: string | undefined;
  stats: boolean;
}> => {
  // 文件路径
  const modulePath = path.join(rawModulePath);
  // 后缀
  const ext = path.extname(modulePath);
  // 文件名
  const fileName = getFileName(modulePath);
  // 路径存在状态
  const { stats, time } = handleFileStatus(modulePath);
  // 文件存在
  if (stats) {
    return { modulePath, time, stats };
  }
  // 存在扩展
  if (!ext.length) {
    // 加后缀
    const res = await resoveModule(`${modulePath}.ts`);
    if (res.stats) {
      return res;
    }
    if (fileName !== 'index.ts') {
      const res = await resoveModule(`${modulePath}/index.ts`);
      if (res.stats) {
        return res;
      }
    }
  }
  return { modulePath, time, stats };
};
/**
 * @description 获取文件名
 * @param filePath
 * @returns
 */
const getFileName = (filePath) => {
  return filePath.substring(filePath.lastIndexOf('/') + 1);
};
/**
 * @description 文件存在
 * @param filePath
 * @returns
 */
const handleFileStatus = (filePath: string) => {
  // 路径存在状态
  const exists = fs.existsSync(filePath);
  // 路径存在
  if (exists) {
    // 文件信息
    const fileInfo = fs.statSync(filePath);
    // 是文件
    if (fileInfo.isFile()) {
      const { mtime } = fileInfo;
      return { stats: true, time: mtime.toJSON() };
    }
  }
  return { stats: false };
};
/**
 * @description 生成配置
 * @param filePath
 * @returns
 */
const createOptions = (filePath: string): ts.CreateProgramOptions => {
  const { target, module } = COMPILE_CONFIG;
  // 项目配置
  const programOptions = {
    rootNames: [filePath],
    options: {
      target,
      module,
      outputDirPath,
    },
  };
  return programOptions;
};
/**
 * @description 创建用户脚本注释配置
 * @returns
 */
const createConfigComment = () => {
  // 脚本数据
  const data: string[] = [];
  data.push('// ==UserScript==');
  for (const key in SCRIPT_CONFIG) {
    if (typeof SCRIPT_CONFIG[key] === 'string') {
      data.push(`// @${key}   ${SCRIPT_CONFIG[key]}`);
    }
    if (Array.isArray(SCRIPT_CONFIG[key])) {
      for (const i in SCRIPT_CONFIG[key]) {
        data.push(`// @${key}   ${SCRIPT_CONFIG[key][i]}`);
      }
    }
  }
  data.push('// ==/UserScript==');
  return data.join('\r\n');
};
// 处理流程工厂
const transformerFactory: ts.TransformerFactory<ts.SourceFile> = (context) => {
  return (node) => {
    // 访问
    const visitor: ts.Visitor = (rootNode) => {
      // 节点
      const node = ts.visitEachChild(rootNode, visitor, context);
      // 是导入声明
      if (ts.isImportDeclaration(node)) {
        // 获取变量名
        const identifierText = node.importClause?.getText();
        // 获取导入模块名
        const moduleText = node.moduleSpecifier.getText();
        // 检查是否满足路径
        const rawSpecificPath = moduleText.match(
          /(?<=(["'`]))(?:\.{0,2}(?:\/|(?:\\{1,2}))[-_.a-zA-Z]*)+(?=\?raw\1)/
        );
        if (identifierText && rawSpecificPath) {
          // 提取模块相对路径
          const [relativefilePath] = rawSpecificPath;
          // 获取实际路径
          const filePath = path.resolve(inputDirPath, relativefilePath);
          // 获取文本信息
          const content = fs
            .readFileSync(filePath, {
              encoding: 'utf8',
            })
            .replace(/\n|\\n/g, '');
          // 创建变量标识符
          const name = ts.factory.createIdentifier(identifierText);
          // 创建字符串
          const value = ts.factory.createStringLiteral(content, true);
          // 创建变量声明
          const declaration = ts.factory.createVariableDeclaration(
            name,
            ts.factory.createToken(ts.SyntaxKind.ExclamationToken),
            ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
            value
          );
          // 创建声明列表 指定为 const
          const declaratioList = ts.factory.createVariableDeclarationList(
            [declaration],
            ts.NodeFlags.Const
          );
          // 创建变量声明
          return ts.factory.createVariableStatement(undefined, declaratioList);
        }
        // 检查是否满足路径
        const resPath = moduleText.match(
          /(?<=(["'`]))(?:\.{0,2}(?:\/|(?:\\{1,2}))[-_.a-zA-Z]*)+(?=\1)/
        );
        // 检查是否满足路径
        if (resPath) {
          // 相对路径
          const relativefilePath = resPath[0];
          importFilePaths.push(relativefilePath);
          return ts.factory.createNotEmittedStatement(node);
        }
        return node;
      }
      // 导出声明
      if (ts.isExportDeclaration(node)) {
        return ts.factory.createNotEmittedStatement(node);
      }
      return node;
    };
    // 调用 visitor
    return ts.visitNode(node, visitor);
  };
};

// 主函数
const main = async () => {
  // 开始编译
  const progress = ora('准备编译生成脚本文件...');
  // 数据
  const fullData: string[] = [];

  // 注释
  progress.start(`正在生成 ${chalk.blueBright('用户脚本配置')} 注释...`);
  // 用户脚本注释配置
  const config = createConfigComment();
  fullData.push(config);
  // 注释
  progress.succeed(`已生成 ${chalk.blueBright('用户脚本配置')} 注释!`);

  // 解析模块
  const { modulePath } = await resoveModule(inputFilePath);
  progress.start(`正在编译... ${chalk.blueBright(modulePath)}`);
  // 编译
  const res = await handleCompile(modulePath);
  if (res) {
    // 编译信息
    const { compileName, data } = res;
    // 脚本内容
    const content: string[] = [];
    content.push(data);
    progress.succeed(
      `完成编译: ${chalk.blueBright(modulePath)} -> ${chalk.blueBright(
        compileName
      )}`
    );
    // 编译相对路径
    const compileRelativePath = path.join(inputDirPath, compileName);

    // 源文件名 编译文件名 数据
    for (const i in importFilePaths) {
      // 相对路径
      const relativefilePath = importFilePaths[i];
      // 文件路径
      const filePath = path.join(inputDirPath, relativefilePath);
      // 解析模块
      const { time, modulePath, stats } = await resoveModule(filePath);

      progress.start(`正在编译... ${chalk.blueBright(modulePath)}`);
      // 存在模块
      if (stats) {
        // 缓存
        if (cache[modulePath]) {
          const { timeStamp, from, to } = cache[modulePath];
          if (time === timeStamp) {
            // 缓存取数据
            content.push(cache[modulePath].data);
            progress.succeed(
              `缓存编译: ${chalk.blueBright(from)} -> ${chalk.blueBright(to)}`
            );
            continue;
          }
        }

        // 编译
        const res = await handleCompile(modulePath);
        if (res) {
          // 编译信息
          const { originRelativePath, compileName, data } = res;
          content.push(data);
          // 缓存
          cache[modulePath] = {
            timeStamp: <string>time,
            data,
            from: originRelativePath,
            to: compileName,
          };
          progress.succeed(
            `直接编译: ${chalk.blueBright(
              originRelativePath
            )} -> ${chalk.blueBright(compileName)}`
          );
          continue;
        }
      }
      progress.fail(`编译失败: ${chalk.red(filePath)}, 请检查导入文件路径!`);
      break;
    }

    progress.start(`正在生成js文件...`);
    // 生成js文件
    fs.writeFileSync(compileRelativePath, content.join('\r\n'));
    progress.succeed(`生成js文件成功!`);

    if (compress) {
      progress.start(`正在压缩js文件...`);
      // rollup 压缩
      const bundle = await rollup.rollup(rollupConfig.inputOptions);
      const { output } = await bundle.write(rollupConfig.outputOptions);
      fullData.push(output[0].code);
      progress.succeed(`压缩js文件成功!`);
    } else {
      fullData.push(content.join('\r\n').replace(/(\r\n){2,}/g, '\r\n'));
    }

    progress.start(
      `正在导出整合的脚本文件... ${chalk.blueBright(outputFilePath)}`
    );
    // 导出文件
    fs.writeFileSync('cache.json', JSON.stringify(cache));
    // 导出文件
    fs.writeFileSync(outputFilePath, fullData.join('\r\n'));
    progress.succeed(`导出整合的脚本文件: ${chalk.blueBright(outputFilePath)}`);
    return;
  }
  progress.fail(`编译失败,请检查文件路径!`);
};

main();


================================================
FILE: cache.json
================================================
{"src\\api\\answer.ts":{"timeStamp":"2023-03-07T02:25:45.829Z","data":"/* 答案 API */\r\n/**\r\n * @description 获取答案\r\n */\r\nasync function getAnswer(question) {\r\n    // 数据\r\n    const data = {\r\n        txt_name: md5(question),\r\n        password: '',\r\n    };\r\n    try {\r\n        const params = new URLSearchParams(data);\r\n        // 请求\r\n        const res = await fetch(API_CONFIG.answerSearch, {\r\n            method: 'POST',\r\n            mode: 'cors',\r\n            headers: {\r\n                'Content-Type': 'application/x-www-form-urlencoded',\r\n            },\r\n            body: params.toString(),\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const result = await res.json();\r\n            const { data, status } = result;\r\n            if (status !== 0) {\r\n                // 答案列表\r\n                const answerList = JSON.parse(data.txt_content);\r\n                // 答案\r\n                const answers = answerList[0].content.split(/[;\\s]/);\r\n                return answers;\r\n            }\r\n        }\r\n    }\r\n    catch (error) { }\r\n    return [];\r\n}\r\n/**\r\n * @description 保存答案\r\n */\r\nasync function saveAnswer(question, answer) {\r\n    try {\r\n        // 内容\r\n        const content = JSON.stringify([{ title: md5(question), content: answer }]);\r\n        // 数据\r\n        const data = {\r\n            txt_name: md5(question),\r\n            txt_content: content,\r\n            password: '',\r\n            v_id: '',\r\n        };\r\n        const params = new URLSearchParams(data);\r\n        // 请求\r\n        const res = await fetch(API_CONFIG.answerSave, {\r\n            method: 'POST',\r\n            mode: 'cors',\r\n            headers: {\r\n                'Content-Type': 'application/x-www-form-urlencoded',\r\n            },\r\n            body: params.toString(),\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            return data;\r\n        }\r\n    }\r\n    catch (error) { }\r\n}\r\n\r\n","from":"src\\api\\answer.ts","to":"answer.js"},"src\\api\\data.ts":{"timeStamp":"2023-06-02T06:24:50.525Z","data":"/* 数据 API */\r\n/**\r\n * @description 获取新闻数据\r\n */\r\nasync function getNewsList() {\r\n    // 随机\r\n    const randNum = ~~(Math.random() * API_CONFIG.todayNews.length);\r\n    try {\r\n        // 获取重要新闻\r\n        const res = await fetch(API_CONFIG.todayNews[randNum], {\r\n            method: 'GET',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            return data;\r\n        }\r\n    }\r\n    catch (err) { }\r\n}\r\n/**\r\n * @description 获取视频数据\r\n */\r\nasync function getVideoList() {\r\n    // 随机\r\n    const randNum = ~~(Math.random() * API_CONFIG.todayVideos.length);\r\n    try {\r\n        // 获取重要新闻\r\n        const res = await fetch(API_CONFIG.todayVideos[randNum], {\r\n            method: 'GET',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            return data;\r\n        }\r\n    }\r\n    catch (err) { }\r\n}\r\n/**\r\n * @description 专项练习数据\r\n */\r\nasync function getExamPaper(pageNo) {\r\n    // 链接\r\n    const url = `${API_CONFIG.paperList}?pageSize=50&pageNo=${pageNo}`;\r\n    try {\r\n        // 获取专项练习\r\n        const res = await fetch(url, {\r\n            method: 'GET',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            const paperJson = decodeURIComponent(escape(window.atob(data.data_str.replace(/-/g, '+').replace(/_/g, '/'))));\r\n            // JSON格式化\r\n            const paper = JSON.parse(paperJson);\r\n            return paper;\r\n        }\r\n    }\r\n    catch (err) {\r\n        return [];\r\n    }\r\n    return [];\r\n}\r\n\r\n","from":"src\\api\\data.ts","to":"data.js"},"src\\api\\login.ts":{"timeStamp":"2023-02-11T12:24:53.231Z","data":"/**\r\n * @description 生成二维码\r\n */\r\nasync function generateQRCode() {\r\n    try {\r\n        // 推送\r\n        const res = await fetch(API_CONFIG.generateQRCode, {\r\n            method: 'GET',\r\n            mode: 'cors',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            if (data.success) {\r\n                return data.result;\r\n            }\r\n        }\r\n    }\r\n    catch (error) { }\r\n}\r\n/**\r\n * @description 用二维码登录\r\n */\r\nasync function loginWithQRCode(qrCode) {\r\n    try {\r\n        const params = new URLSearchParams({\r\n            qrCode,\r\n            goto: 'https://oa.xuexi.cn',\r\n            pdmToken: '',\r\n        });\r\n        // 推送\r\n        const res = await fetch(API_CONFIG.loginWithQRCode, {\r\n            method: 'POST',\r\n            mode: 'cors',\r\n            credentials: 'include',\r\n            headers: {\r\n                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\r\n            },\r\n            body: params.toString(),\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            return data;\r\n        }\r\n    }\r\n    catch (error) { }\r\n}\r\n/**\r\n * @description 签名\r\n */\r\nasync function getSign() {\r\n    try {\r\n        // 推送\r\n        const res = await fetch(API_CONFIG.sign, {\r\n            method: 'GET',\r\n            mode: 'cors',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            if (data.ok) {\r\n                return data.data.sign;\r\n            }\r\n        }\r\n    }\r\n    catch (error) { }\r\n}\r\n/**\r\n * @description 安全检查\r\n * @param data\r\n */\r\nasync function secureCheck(data) {\r\n    try {\r\n        const params = new URLSearchParams(data);\r\n        const url = `${API_CONFIG.secureCheck}?${params}`;\r\n        // 推送\r\n        const res = await fetch(url, {\r\n            method: 'GET',\r\n            mode: 'cors',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            return data.success;\r\n        }\r\n    }\r\n    catch (error) { }\r\n    return false;\r\n}\r\n\r\n","from":"src\\api\\login.ts","to":"login.js"},"src\\api\\push.ts":{"timeStamp":"2023-02-11T12:24:42.072Z","data":"/* 推送 API */\r\n/**\r\n * @description 推送\r\n */\r\nasync function pushPlus(token, title, content, template, toToken) {\r\n    try {\r\n        // 参数体\r\n        const body = {\r\n            token,\r\n            title,\r\n            content,\r\n            template,\r\n        };\r\n        // 好友令牌\r\n        if (toToken) {\r\n            body.to = toToken;\r\n        }\r\n        // 推送\r\n        const res = await fetch(API_CONFIG.push, {\r\n            method: 'POST',\r\n            mode: 'cors',\r\n            headers: {\r\n                'Content-Type': 'application/json',\r\n            },\r\n            body: JSON.stringify(body),\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const data = await res.json();\r\n            return data;\r\n        }\r\n    }\r\n    catch (error) { }\r\n}\r\n\r\n","from":"src\\api\\push.ts","to":"push.js"},"src\\api\\user.ts":{"timeStamp":"2023-02-12T08:26:02.766Z","data":"/* 用户 API */\r\n/**\r\n * @description 获取用户信息\r\n */\r\nasync function getUserInfo() {\r\n    try {\r\n        const res = await fetch(API_CONFIG.userInfo, {\r\n            method: 'GET',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const { data } = await res.json();\r\n            return data;\r\n        }\r\n    }\r\n    catch (err) { }\r\n}\r\n/**\r\n * @description 获取总积分\r\n */\r\nasync function getTotalScore() {\r\n    try {\r\n        const res = await fetch(API_CONFIG.totalScore, {\r\n            method: 'GET',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const { data } = await res.json();\r\n            // 总分\r\n            const { score } = data;\r\n            return score;\r\n        }\r\n    }\r\n    catch (err) { }\r\n}\r\n/**\r\n * @description 获取当天总积分\r\n */\r\nasync function getTodayScore() {\r\n    try {\r\n        const res = await fetch(API_CONFIG.todayScore, {\r\n            method: 'GET',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const { data } = await res.json();\r\n            // 当天总分\r\n            const { score } = data;\r\n            return score;\r\n        }\r\n    }\r\n    catch (err) { }\r\n}\r\n/**\r\n * @description 获取任务列表\r\n */\r\nasync function getTaskList() {\r\n    try {\r\n        const res = await fetch(API_CONFIG.taskList, {\r\n            method: 'GET',\r\n            credentials: 'include',\r\n        });\r\n        // 请求成功\r\n        if (res.ok) {\r\n            const { data } = await res.json();\r\n            // 进度和当天总分\r\n            const { taskProgress } = data;\r\n            return taskProgress;\r\n        }\r\n    }\r\n    catch (err) { }\r\n}\r\n\r\n","from":"src\\api\\user.ts","to":"user.js"},"src\\config\\task.ts":{"timeStamp":"2023-07-12T05:19:25.996Z","data":"/* task·配置 */\r\n/**\r\n * @description 单次最大新闻数\r\n */\r\nconst maxNewsNum = 6;\r\n/**\r\n * @description 单次最大视频数\r\n */\r\nconst maxVideoNum = 6;\r\n/**\r\n * @description 二维码最大刷新次数\r\n */\r\nconst maxRefreshCount = 10;\r\n/**\r\n * @description 二维码自动刷新间隔\r\n */\r\nconst autoRefreshQRCodeInterval = 100000;\r\n\r\n","from":"src\\config\\task.ts","to":"task.js"},"src\\config\\url.ts":{"timeStamp":"2023-01-11T12:31:08.253Z","data":"/**\r\n * @description url配置\r\n */\r\nconst URL_CONFIG = {\r\n    // 主页正则\r\n    home: /^https\\:\\/\\/www\\.xuexi\\.cn(\\/(index\\.html)?)?$/,\r\n    // 主页\r\n    homeOrigin: 'https://www.xuexi.cn',\r\n    // 每日答题页面\r\n    examPractice: 'https://pc.xuexi.cn/points/exam-practice.html',\r\n    // 专项练习页面\r\n    examPaper: 'https://pc.xuexi.cn/points/exam-paper-detail.html',\r\n};\r\n\r\n","from":"src\\config\\url.ts","to":"url.js"},"src\\config\\api.ts":{"timeStamp":"2023-03-21T07:23:02.891Z","data":"/**\r\n * @description api配置\r\n */\r\nconst API_CONFIG = {\r\n    // 用户信息\r\n    userInfo: 'https://pc-api.xuexi.cn/open/api/user/info',\r\n    // 总分\r\n    totalScore: 'https://pc-proxy-api.xuexi.cn/delegate/score/get',\r\n    // 当天分数\r\n    todayScore: 'https://pc-proxy-api.xuexi.cn/delegate/score/today/query',\r\n    // 任务列表\r\n    taskList: 'https://pc-proxy-api.xuexi.cn/delegate/score/days/listScoreProgress?sence=score&deviceType=2',\r\n    // 新闻数据\r\n    todayNews: [\r\n        'https://www.xuexi.cn/lgdata/35il6fpn0ohq.json',\r\n        'https://www.xuexi.cn/lgdata/1ap1igfgdn2.json',\r\n        'https://www.xuexi.cn/lgdata/vdppiu92n1.json',\r\n        'https://www.xuexi.cn/lgdata/152mdtl3qn1.json',\r\n    ],\r\n    // 视频数据\r\n    todayVideos: [\r\n        'https://www.xuexi.cn/lgdata/525pi8vcj24p.json',\r\n        'https://www.xuexi.cn/lgdata/11vku6vt6rgom.json',\r\n        'https://www.xuexi.cn/lgdata/2qfjjjrprmdh.json',\r\n        'https://www.xuexi.cn/lgdata/3o3ufqgl8rsn.json',\r\n        'https://www.xuexi.cn/lgdata/591ht3bc22pi.json',\r\n        'https://www.xuexi.cn/lgdata/1742g60067k.json',\r\n        'https://www.xuexi.cn/lgdata/1novbsbi47k.json',\r\n    ],\r\n    // 专项练习列表\r\n    paperList: 'https://pc-proxy-api.xuexi.cn/api/exam/service/paper/pc/list',\r\n    // 文本服务器保存答案\r\n    answerSave: 'https://a6.qikekeji.com/txt/data/save',\r\n    // 文本服务器获取答案\r\n    answerSearch: 'https://a6.qikekeji.com/txt/data/detail',\r\n    // 推送\r\n    push: 'https://www.pushplus.plus/send',\r\n    // 生成二维码\r\n    generateQRCode: 'https://login.xuexi.cn/user/qrcode/generate',\r\n    //二维码登录\r\n    loginWithQRCode: 'https://login.xuexi.cn/login/login_with_qr',\r\n    // 签名\r\n    sign: 'https://pc-api.xuexi.cn/open/api/sns/sign',\r\n    // 安全检查\r\n    secureCheck: 'https://pc-api.xuexi.cn/login/secure_check',\r\n    // 二维码\r\n    qrcode: 'https://api.qrserver.com/v1/create-qr-code',\r\n};\r\n\r\n","from":"src\\config\\api.ts","to":"api.js"},"src\\config\\version.ts":{"timeStamp":"2023-07-13T13:35:55.841Z","data":"/**\r\n * @description 版本号\r\n */\r\nconst version = '1.7.5';\r\n\r\n","from":"src\\config\\version.ts","to":"version.js"},"src\\types\\index.ts":{"timeStamp":"2023-07-12T05:59:43.153Z","data":"/**\r\n * @description 任务类型\r\n */\r\nvar TaskType;\r\n(function (TaskType) {\r\n    TaskType[TaskType[\"LOGIN\"] = 0] = \"LOGIN\";\r\n    TaskType[TaskType[\"READ\"] = 1] = \"READ\";\r\n    TaskType[TaskType[\"WATCH\"] = 2] = \"WATCH\";\r\n    TaskType[TaskType[\"PRACTICE\"] = 3] = \"PRACTICE\";\r\n})(TaskType || (TaskType = {}));\r\n/**\r\n * @description 设置类型\r\n */\r\nvar SettingType;\r\n(function (SettingType) {\r\n    SettingType[SettingType[\"AUTO_START\"] = 0] = \"AUTO_START\";\r\n    SettingType[SettingType[\"SAME_TAB\"] = 1] = \"SAME_TAB\";\r\n    SettingType[SettingType[\"SILENT_RUN\"] = 2] = \"SILENT_RUN\";\r\n    SettingType[SettingType[\"SCHEDULE_RUN\"] = 3] = \"SCHEDULE_RUN\";\r\n    SettingType[SettingType[\"VIDEO_MUTED\"] = 4] = \"VIDEO_MUTED\";\r\n    SettingType[SettingType[\"RANDOM_EXAM\"] = 5] = \"RANDOM_EXAM\";\r\n    SettingType[SettingType[\"AUTO_ANSWER\"] = 6] = \"AUTO_ANSWER\";\r\n    SettingType[SettingType[\"REMOTE_PUSH\"] = 7] = \"REMOTE_PUSH\";\r\n})(SettingType || (SettingType = {}));\r\n/**\r\n * @description 进度类型\r\n */\r\nvar TaskStatusType;\r\n(function (TaskStatusType) {\r\n    TaskStatusType[TaskStatusType[\"LOADING\"] = 0] = \"LOADING\";\r\n    TaskStatusType[TaskStatusType[\"LOADED\"] = 1] = \"LOADED\";\r\n    TaskStatusType[TaskStatusType[\"START\"] = 2] = \"START\";\r\n    TaskStatusType[TaskStatusType[\"PAUSE\"] = 3] = \"PAUSE\";\r\n    TaskStatusType[TaskStatusType[\"FINISH\"] = 4] = \"FINISH\";\r\n})(TaskStatusType || (TaskStatusType = {}));\r\n\r\n","from":"src\\types\\index.ts","to":"index.js"},"src\\utils\\composition.ts":{"timeStamp":"2023-03-07T10:10:48.749Z","data":"// 当前订阅\r\nlet currentSub;\r\n// 订阅\r\nconst subscription = new WeakMap();\r\n/**\r\n * @description Proxy Map\r\n */\r\nconst proxyMap = new WeakMap();\r\n/**\r\n * @description 收集 Ref 依赖\r\n * @param target\r\n * @param key\r\n */\r\nconst trackRef = (target) => {\r\n    // 当前订阅\r\n    if (!currentSub) {\r\n        return;\r\n    }\r\n    // target 订阅列表\r\n    let subList = subscription.get(target);\r\n    // 不存在订阅列表\r\n    if (!subList) {\r\n        subList = new Map();\r\n        // 键订阅\r\n        const subkeyList = new Set();\r\n        // 添加订阅\r\n        subkeyList.add(currentSub);\r\n        subList.set('value', subkeyList);\r\n        subscription.set(target, subList);\r\n        return;\r\n    }\r\n    // 键订阅\r\n    let subkeyList = subList.get('value');\r\n    if (!subkeyList) {\r\n        // 键订阅\r\n        subkeyList = new Set();\r\n        // 添加订阅\r\n        subkeyList.add(currentSub);\r\n        subList.set('value', subkeyList);\r\n        subscription.set(target, subList);\r\n        return;\r\n    }\r\n    // 添加订阅\r\n    subkeyList.add(currentSub);\r\n};\r\n/**\r\n * @description 通知 Ref 订阅\r\n * @param terget\r\n * @param key\r\n * @returns\r\n */\r\nfunction triggerRef(target, newVal, oldVal) {\r\n    // target 订阅列表\r\n    const subList = subscription.get(target);\r\n    if (!subList) {\r\n        return;\r\n    }\r\n    // 键订阅\r\n    let subkeyList = subList.get('value');\r\n    if (!subkeyList) {\r\n        return;\r\n    }\r\n    // 通知订阅\r\n    for (const fn of subkeyList) {\r\n        if (fn instanceof Function) {\r\n            fn(newVal, oldVal);\r\n        }\r\n    }\r\n}\r\n/**\r\n * @description 收集依赖\r\n * @param target\r\n * @param key\r\n */\r\nconst track = (target, key) => {\r\n    // 当前订阅\r\n    if (!currentSub) {\r\n        return;\r\n    }\r\n    // proxy\r\n    const proxyTarget = proxyMap.get(target);\r\n    if (!proxyTarget) {\r\n        return;\r\n    }\r\n    // target 订阅列表\r\n    let subList = subscription.get(target);\r\n    // 不存在订阅列表\r\n    if (!subList) {\r\n        subList = new Map();\r\n        // 键订阅\r\n        const subkeyList = new Set();\r\n        // 添加订阅\r\n        subkeyList.add(currentSub);\r\n        subList.set(key, subkeyList);\r\n        subscription.set(target, subList);\r\n        return;\r\n    }\r\n    // 键订阅\r\n    let subkeyList = subList.get(key);\r\n    if (!subkeyList) {\r\n        // 键订阅\r\n        subkeyList = new Set();\r\n        // 添加订阅\r\n        subkeyList.add(currentSub);\r\n        subList.set(key, subkeyList);\r\n        subscription.set(target, subList);\r\n        return;\r\n    }\r\n    // 添加订阅\r\n    subkeyList.add(currentSub);\r\n};\r\n/**\r\n * @description 通知订阅\r\n * @param terget\r\n * @param key\r\n * @returns\r\n */\r\nfunction trigger(target, key, newVal, oldVal) {\r\n    // proxy\r\n    const proxyTarget = proxyMap.get(target);\r\n    if (!proxyTarget) {\r\n        return;\r\n    }\r\n    // proxyTarget 订阅列表\r\n    const subList = subscription.get(target);\r\n    if (!subList) {\r\n        return;\r\n    }\r\n    // 键订阅\r\n    let subkeyList = subList.get(key);\r\n    if (!subkeyList) {\r\n        return;\r\n    }\r\n    // 通知订阅\r\n    for (const fn of subkeyList) {\r\n        fn(newVal, oldVal);\r\n    }\r\n}\r\n/**\r\n * @description 只读键\r\n */\r\nvar ReactiveFlags;\r\n(function (ReactiveFlags) {\r\n    ReactiveFlags[\"IS_REF\"] = \"_isRef\";\r\n    ReactiveFlags[\"IS_SHALLOW\"] = \"_isShallow\";\r\n    ReactiveFlags[\"IS_REACTIVE\"] = \"_isReactive\";\r\n    ReactiveFlags[\"IS_READONLY\"] = \"_isReadonly\";\r\n})(ReactiveFlags || (ReactiveFlags = {}));\r\n/**\r\n * @description Ref\r\n */\r\nclass Ref {\r\n    _isShallow = false;\r\n    _isRef = true;\r\n    _value;\r\n    value;\r\n    constructor(val, shallow = false) {\r\n        const _this = this;\r\n        this._isShallow = shallow;\r\n        if (val && typeof val === 'object' && shallow) {\r\n            const reactiveVal = reactive(val);\r\n            this._value = reactiveVal;\r\n            this.value = reactiveVal;\r\n        }\r\n        else {\r\n            this._value = val;\r\n            this.value = val;\r\n        }\r\n        // 定义属性\r\n        Object.defineProperty(this, 'value', {\r\n            get() {\r\n                // 收集依赖\r\n                trackRef(this);\r\n                return _this._value;\r\n            },\r\n            set(newVal) {\r\n                // 旧数据\r\n                const oldVal = this._value;\r\n                // 数据变化\r\n                if (oldVal !== newVal) {\r\n                    // 设置新数据值\r\n                    _this._value = newVal;\r\n                    // 通知依赖\r\n                    triggerRef(this, newVal, oldVal);\r\n                }\r\n            },\r\n        });\r\n    }\r\n    toJSON() {\r\n        return this._value;\r\n    }\r\n}\r\n/**\r\n * @description ref\r\n * @param v\r\n * @returns\r\n */\r\nconst isRef = (v) => {\r\n    return !!(v && v[ReactiveFlags.IS_REF]);\r\n};\r\n/**\r\n * @description 浅层 shallow\r\n * @param v\r\n * @returns\r\n */\r\nconst isShallow = (v) => {\r\n    return !!(v && v[ReactiveFlags.IS_SHALLOW]);\r\n};\r\n/**\r\n * @description 创建 ref\r\n * @param v\r\n * @returns\r\n */\r\nconst createRef = (rawVal, shallow) => {\r\n    return new Ref(rawVal, shallow);\r\n};\r\n/**\r\n * @description 解除 ref\r\n * @param val\r\n * @returns\r\n */\r\nconst unref = (val) => {\r\n    return (isRef(val) ? val.value : val);\r\n};\r\n/**\r\n * @description 顶层 ref\r\n * @param v\r\n * @returns\r\n */\r\nconst ref = (value) => {\r\n    return isRef(value)\r\n        ? value\r\n        : createRef(value, true);\r\n};\r\n/**\r\n * @description ref\r\n * @param value\r\n * @returns\r\n */\r\nconst shallowRef = (value) => {\r\n    return isRef(value)\r\n        ? value\r\n        : createRef(value, false);\r\n};\r\n/**\r\n * @description 创建处理 reactive\r\n * @param isReadonly\r\n * @param isShallow\r\n * @returns\r\n */\r\nconst createReactiveHandlers = (isReadonly, isShallow) => {\r\n    return {\r\n        get: createGetters(isReadonly, isShallow),\r\n        set: createSetters(isReadonly, isShallow),\r\n    };\r\n};\r\n/**\r\n * @description getters\r\n * @param isReadonly\r\n * @param isShallow\r\n * @returns\r\n */\r\nconst createGetters = (isReadonly, isShallow) => {\r\n    return function get(target, key, receiver) {\r\n        if (key === ReactiveFlags.IS_REACTIVE) {\r\n            return !isReadonly;\r\n        }\r\n        if (key === ReactiveFlags.IS_READONLY) {\r\n            return isReadonly;\r\n        }\r\n        if (key === ReactiveFlags.IS_SHALLOW) {\r\n            return isShallow;\r\n        }\r\n        // 结果\r\n        const res = Reflect.get(target, key, receiver);\r\n        if (!isReadonly) {\r\n            // 收集依赖\r\n            track(target, key);\r\n        }\r\n        if (isShallow) {\r\n            return res;\r\n        }\r\n        if (isRef(res)) {\r\n            return res.value;\r\n        }\r\n        if (res && typeof res === 'object') {\r\n            if (res instanceof Element) {\r\n                return res;\r\n            }\r\n            return isReadonly ? readonly(res) : reactive(res);\r\n        }\r\n        return res;\r\n    };\r\n};\r\n/**\r\n * @description setters\r\n * @param readonly\r\n * @param shallow\r\n * @returns\r\n */\r\nconst createSetters = (readonly, shallow) => {\r\n    return function set(target, key, newVal, receiver) {\r\n        // 只读\r\n        if (readonly) {\r\n            return false;\r\n        }\r\n        // 旧值\r\n        const oldVal = target[key];\r\n        if (isReadonly(oldVal) && isRef(oldVal) && !isRef(newVal)) {\r\n            return false;\r\n        }\r\n        if (!shallow) {\r\n            if (isRef(oldVal) && !isRef(newVal)) {\r\n                oldVal.value = newVal;\r\n                return true;\r\n            }\r\n        }\r\n        const res = Reflect.set(target, key, newVal, receiver);\r\n        // length\r\n        if (Array.isArray(target) && key === 'length') {\r\n            // 通知依赖\r\n            trigger(target, key, newVal, oldVal);\r\n            return res;\r\n        }\r\n        // 数据变化\r\n        if (oldVal !== newVal) {\r\n            // 通知依赖\r\n            trigger(target, key, newVal, oldVal);\r\n        }\r\n        return res;\r\n    };\r\n};\r\n/**\r\n * @description reactive object\r\n */\r\nconst createReactiveObj = (target, isReadonly, shallow) => {\r\n    // 存在 Proxy\r\n    const existingProxy = proxyMap.get(target);\r\n    if (existingProxy) {\r\n        return existingProxy;\r\n    }\r\n    // 新建\r\n    const proxy = new Proxy(target, createReactiveHandlers(isReadonly, shallow));\r\n    proxyMap.set(target, proxy);\r\n    return proxy;\r\n};\r\n/**\r\n * @description reactive\r\n * @param val\r\n * @returns\r\n */\r\nconst isReactive = (val) => {\r\n    return !!(val && val[ReactiveFlags.IS_REACTIVE]);\r\n};\r\n/**\r\n * @description 创建 reactive\r\n * @param target\r\n * @returns\r\n */\r\nconst createReactive = (target) => {\r\n    return createReactiveObj(target, false, false);\r\n};\r\n/**\r\n * @description 顶层 reactive\r\n * @param target\r\n * @returns\r\n */\r\nconst shallowReactive = (target) => {\r\n    return createReactiveObj(target, false, true);\r\n};\r\n/**\r\n * @description reactive\r\n * @param val\r\n * @returns\r\n */\r\nconst isReadonly = (val) => {\r\n    return !!(val && val[ReactiveFlags.IS_READONLY]);\r\n};\r\n/**\r\n * @description 创建 readonly\r\n * @param target\r\n * @returns\r\n */\r\nconst createReadonly = (target) => {\r\n    return createReactiveObj(target, true, false);\r\n};\r\n/**\r\n * @description 顶层 readonly\r\n * @param target\r\n * @returns\r\n */\r\nconst shallowReadonly = (target) => {\r\n    return createReactiveObj(target, true, true);\r\n};\r\n/**\r\n * @description proxy\r\n * @param val\r\n * @returns\r\n */\r\nconst isProxy = (val) => {\r\n    return isReactive(val) || isReadonly(val);\r\n};\r\n/**\r\n * @description reactive\r\n * @param target\r\n * @returns\r\n */\r\nconst reactive = (target) => {\r\n    return createReactive(target);\r\n};\r\n/**\r\n * @description readonly\r\n * @param target\r\n * @returns\r\n */\r\nconst readonly = (target) => {\r\n    return createReadonly(target);\r\n};\r\n/**\r\n * @description 监听数据变化\r\n * @param source\r\n * @param callback\r\n */\r\nconst watch = (source, callback, immediate = false) => {\r\n    // 立刻执行\r\n    immediate && callback(unref(source), unref(source));\r\n    // array\r\n    if (Array.isArray(source) && source.every((s) => isRef(s))) {\r\n        for (const i in source) {\r\n            // Proxy\r\n            if (isProxy(source[i])) {\r\n                watch(source[i], () => {\r\n                    const res = source.map((s) => unref(s));\r\n                    callback(res, res);\r\n                });\r\n            }\r\n        }\r\n        watch(() => source.map((s) => unref(s)), callback);\r\n        return;\r\n    }\r\n    // function\r\n    if (source instanceof Function) {\r\n        watch(watchEffectRef(source), (n, o) => {\r\n            callback(unref(n), unref(o));\r\n        });\r\n        return;\r\n    }\r\n    // Proxy\r\n    if (isProxy(source)) {\r\n        for (const key in source) {\r\n            currentSub = () => {\r\n                callback(source, source);\r\n            };\r\n            // sub source\r\n            const subSource = source[key];\r\n            currentSub = undefined;\r\n            watch(subSource, () => {\r\n                callback(source, source);\r\n            });\r\n        }\r\n        return;\r\n    }\r\n    // Ref\r\n    if (isRef(source)) {\r\n        // Ref.value Proxy\r\n        if (isProxy(source.value)) {\r\n            watch(source.value, () => {\r\n                callback(unref(source), unref(source));\r\n            });\r\n        }\r\n        currentSub = callback;\r\n        // 收集依赖\r\n        trackRef(source);\r\n        currentSub = undefined;\r\n        return;\r\n    }\r\n};\r\n/**\r\n * @description 监听数据变化影响\r\n * @param callback\r\n * @returns\r\n */\r\nconst watchEffect = (callback) => {\r\n    currentSub = callback;\r\n    // 收集依赖\r\n    callback();\r\n    currentSub = undefined;\r\n};\r\n/**\r\n * @description 监听影响 ref\r\n * @param refVal\r\n * @param callback\r\n * @returns\r\n */\r\nconst watchRef = (source, callback) => {\r\n    // 收集依赖\r\n    const effectRes = shallowRef(callback());\r\n    // 监听\r\n    watch(source, () => (effectRes.value = unref(callback())));\r\n    return effectRes;\r\n};\r\n/**\r\n * @description 监听影响 ref\r\n * @param refVal\r\n * @param callback\r\n * @returns\r\n */\r\nconst watchEffectRef = (callback) => {\r\n    // 收集依赖\r\n    const effectRes = shallowRef(undefined);\r\n    // 监听\r\n    watchEffect(() => (effectRes.value = unref(callback())));\r\n    return effectRes;\r\n};\r\n\r\n","from":"src\\utils\\composition.ts","to":"composition.js"},"src\\utils\\element.ts":{"timeStamp":"2023-03-07T11:28:14.066Z","data":"/**\r\n * @description 创建元素节点\r\n * @param eleName\r\n * @param props\r\n * @param attrs\r\n * @param children\r\n * @returns\r\n */\r\nfunction createElementNode(tagName, props, attrs, children, options) {\r\n    // 挂载状态\r\n    let beforemount = ref(false);\r\n    // 挂载状态\r\n    let mounted = ref(false);\r\n    const { onCreated, beforeCreat, onMounted, beforeMount } = options || {};\r\n    // 订阅\r\n    const subscribe = (e) => {\r\n        const { onMounted, beforeMount } = e;\r\n        if (beforeMount) {\r\n            watch(beforemount, () => {\r\n                if (beforemount.value) {\r\n                    beforeMount();\r\n                    return;\r\n                }\r\n            }, true);\r\n        }\r\n        if (onMounted) {\r\n            watch(mounted, () => {\r\n                if (mounted.value) {\r\n                    onMounted();\r\n                    return;\r\n                }\r\n            }, true);\r\n        }\r\n    };\r\n    // 取消订阅\r\n    const unsubscribe = (e) => {\r\n        //懒得写\r\n    };\r\n    // 创建元素前\r\n    beforeCreat && beforeCreat();\r\n    // 创建普通元素\r\n    const ele = document.createElement(tagName);\r\n    // 处理属性\r\n    handleProps(ele, props);\r\n    // 处理属性\r\n    handleAttributes(ele, attrs, subscribe, unsubscribe);\r\n    // 处理子元素\r\n    handleChildren(ele, children, subscribe, unsubscribe);\r\n    // 收集挂载前\r\n    const collectBeforeMount = () => {\r\n        beforemount.value = true;\r\n        beforeMount && beforeMount();\r\n    };\r\n    // 收集挂载\r\n    const collectOnMounted = () => {\r\n        mounted.value = true;\r\n        onMounted && onMounted();\r\n    };\r\n    // 创建元素后\r\n    onCreated && onCreated();\r\n    return { ele, beforeMount: collectBeforeMount, onMounted: collectOnMounted };\r\n}\r\n/**\r\n * @description 创建svg元素\r\n * @param tagName\r\n * @param props\r\n * @param attrs\r\n * @param children\r\n * @returns\r\n */\r\nfunction createNSElementNode(tagName, props, attrs, children, options) {\r\n    // 挂载状态\r\n    let beforemount = ref(false);\r\n    // 挂载状态\r\n    let mounted = ref(false);\r\n    const { onCreated, beforeCreat, onMounted, beforeMount } = options || {};\r\n    // 订阅\r\n    const subscribe = (e) => {\r\n        const { onMounted, beforeMount } = e;\r\n        if (beforeMount) {\r\n            watch(beforemount, () => {\r\n                if (beforemount.value) {\r\n                    beforeMount();\r\n                    return;\r\n                }\r\n            }, true);\r\n        }\r\n        if (onMounted) {\r\n            watch(mounted, () => {\r\n                if (mounted.value) {\r\n                    onMounted();\r\n                    return;\r\n                }\r\n            }, true);\r\n        }\r\n    };\r\n    // 取消订阅\r\n    const unsubscribe = (e) => {\r\n        //懒得写\r\n    };\r\n    // 创建元素前\r\n    beforeCreat && beforeCreat();\r\n    // svg元素命名空间\r\n    const ns = 'http://www.w3.org/2000/svg';\r\n    // 创建svg元素\r\n    const ele = document.createElementNS(ns, tagName);\r\n    // 处理属性\r\n    handleProps(ele, props);\r\n    // 处理属性\r\n    handleAttributes(ele, attrs, subscribe, unsubscribe);\r\n    // 处理子元素\r\n    handleChildren(ele, children, subscribe, unsubscribe);\r\n    // 收集挂载前\r\n    const collectBeforeMount = () => {\r\n        beforemount.value = true;\r\n        beforeMount && beforeMount();\r\n    };\r\n    // 收集挂载\r\n    const collectOnMounted = () => {\r\n        mounted.value = true;\r\n        onMounted && onMounted();\r\n    };\r\n    // 创建元素后\r\n    onCreated && onCreated();\r\n    return { ele, beforeMount: collectBeforeMount, onMounted: collectOnMounted };\r\n}\r\n/**\r\n * @description 处理属性\r\n * @param ele\r\n * @param props\r\n */\r\nfunction handleProps(ele, props) {\r\n    // props属性设置\r\n    for (const key in props) {\r\n        // Ref 属性\r\n        if (isRef(props[key])) {\r\n            const refVal = props[key];\r\n            watchEffect(() => (ele[key] = refVal.value));\r\n            continue;\r\n        }\r\n        ele[key] = props[key];\r\n    }\r\n}\r\n/**\r\n * @description 处理svg属性\r\n * @param ele\r\n * @param attrs\r\n */\r\nfunction handleAttributes(ele, attrs, subscribe, unsubscribe) {\r\n    // 属性存在\r\n    if (attrs) {\r\n        // attrs属性设置\r\n        for (const key in attrs) {\r\n            // 处理普通属性\r\n            handleAttribute(ele, key, attrs[key], subscribe, unsubscribe);\r\n        }\r\n    }\r\n}\r\n/**\r\n * @description 处理事件选项\r\n */\r\nfunction handleEventOptions(option) {\r\n    if (option.length) {\r\n        const options = {\r\n            capture: option.includes('capture'),\r\n            once: option.includes('once'),\r\n            passive: option.includes('passive'),\r\n        };\r\n        return options;\r\n    }\r\n}\r\n/**\r\n * @description 处理属性\r\n * @param ele\r\n * @param key\r\n * @param value\r\n */\r\nfunction handleAttribute(ele, key, value, subscribe, unsubscribe) {\r\n    // 处理完的key\r\n    const formatKey = key.toLowerCase();\r\n    // 事件绑定\r\n    if (formatKey.startsWith('on')) {\r\n        // 事件监听\r\n        const [event] = formatKey.match(/(?<=on).*/);\r\n        // 事件类型\r\n        if (event) {\r\n            const [eventType, ...option] = event.split('_');\r\n            const options = handleEventOptions(option);\r\n            // Ref 函数\r\n            if (isRef(value)) {\r\n                const refVal = value;\r\n                const refListener = watchRef(refVal, () => refVal.value\r\n                    ? (e) => {\r\n                        option.includes('prevent') && e.preventDefault();\r\n                        option.includes('stop') && e.stopPropagation();\r\n                        const callback = refVal.value;\r\n                        callback(e);\r\n                    }\r\n                    : undefined);\r\n                // 设置事件监听\r\n                refListener.value &&\r\n                    ele.addEventListener(eventType, refListener.value, options);\r\n                // 监听事件变化\r\n                watch(refListener, (newVal, oldVal) => {\r\n                    // 移除旧事件监听\r\n                    oldVal && ele.removeEventListener(eventType, oldVal);\r\n                    // 设置新事件监听\r\n                    newVal && ele.addEventListener(eventType, newVal, options);\r\n                });\r\n                return;\r\n            }\r\n            // 普通函数\r\n            if (value instanceof Function) {\r\n                // 设置事件监听\r\n                ele.addEventListener(eventType, value, options);\r\n            }\r\n        }\r\n        return;\r\n    }\r\n    // 特殊属性\r\n    const specificAttrs = ['checked', 'selected', 'disabled', 'enabled'];\r\n    // 特殊 key\r\n    if (specificAttrs.includes(formatKey)) {\r\n        // Ref\r\n        if (isRef(value)) {\r\n            const refVal = value;\r\n            watchEffect(() => {\r\n                if (refVal.value) {\r\n                    ele.setAttribute(formatKey, '');\r\n                }\r\n                else {\r\n                    ele.removeAttribute(formatKey);\r\n                }\r\n            });\r\n            return;\r\n        }\r\n        // 普通属性值\r\n        if (value) {\r\n            ele.setAttribute(formatKey, '');\r\n        }\r\n        else {\r\n            ele.removeAttribute(formatKey);\r\n        }\r\n        return;\r\n    }\r\n    // ref 属性名\r\n    if (key === 'ref') {\r\n        // Ref\r\n        if (isRef(value)) {\r\n            const refVal = value;\r\n            subscribe &&\r\n                subscribe({\r\n                    onMounted() {\r\n                        refVal.value = ele;\r\n                    },\r\n                });\r\n            return;\r\n        }\r\n        // Ref 函数\r\n        if (value instanceof Function) {\r\n            const refFn = value;\r\n            subscribe &&\r\n                subscribe({\r\n                    onMounted() {\r\n                        refFn(ele);\r\n                    },\r\n                });\r\n            return;\r\n        }\r\n        return;\r\n    }\r\n    // xlink命名空间\r\n    if (key.startsWith('xlink:')) {\r\n        // xlink属性命名空间\r\n        const attrNS = 'http://www.w3.org/1999/xlink';\r\n        if (value) {\r\n            ele.setAttributeNS(attrNS, key, value);\r\n        }\r\n        else {\r\n            ele.removeAttributeNS(attrNS, key);\r\n        }\r\n        return;\r\n    }\r\n    // Ref 属性值\r\n    if (key && isRef(value)) {\r\n        const refVal = value;\r\n        // 监听影响\r\n        watchEffect(() => {\r\n            ele.setAttribute(key, refVal.value);\r\n        });\r\n        return;\r\n    }\r\n    // 普通属性\r\n    if (key) {\r\n        // 普通属性\r\n        ele.setAttribute(key, value);\r\n    }\r\n}\r\n/**\r\n * @description 处理子元素\r\n * @param ele\r\n * @param children\r\n */\r\nfunction handleChildren(ele, children, subscribe, unsubscribe) {\r\n    // Ref\r\n    if (isRef(children)) {\r\n        // 注释元素\r\n        const comment = document.createComment('');\r\n        // 监听元素变化\r\n        watch(children, async (newEle, oldEle) => {\r\n            if (!newEle && oldEle) {\r\n                // Promise\r\n                if (oldEle instanceof Promise) {\r\n                    const oldEleRes = await oldEle;\r\n                    if (oldEleRes) {\r\n                        oldEleRes.forEach((ele) => {\r\n                            unsubscribe && unsubscribe(ele);\r\n                        });\r\n                    }\r\n                }\r\n                // unPromise\r\n                if (!(oldEle instanceof Promise)) {\r\n                    oldEle.forEach((ele) => {\r\n                        unsubscribe && unsubscribe(ele);\r\n                    });\r\n                }\r\n                ele.replaceChildren(comment);\r\n                return;\r\n            }\r\n            if (newEle) {\r\n                if (oldEle) {\r\n                    // Promise\r\n                    if (oldEle instanceof Promise) {\r\n                        const oldEleRes = await oldEle;\r\n                        if (oldEleRes) {\r\n                            oldEleRes.forEach((ele) => {\r\n                                unsubscribe && unsubscribe(ele);\r\n                            });\r\n                        }\r\n                    }\r\n                    // unPromise\r\n                    if (!(oldEle instanceof Promise)) {\r\n                        oldEle.forEach((ele) => {\r\n                            unsubscribe && unsubscribe(ele);\r\n                        });\r\n                    }\r\n                }\r\n                // Promise\r\n                if (newEle instanceof Promise) {\r\n                    const newEleRes = await newEle;\r\n                    if (newEleRes) {\r\n                        const eles = newEleRes.map((v) => {\r\n                            if (v.beforeMount || v.onMounted) {\r\n                                subscribe && subscribe(v);\r\n                            }\r\n                            return v.ele;\r\n                        });\r\n                        ele.replaceChildren(createElementBlock(eles));\r\n                    }\r\n                    return;\r\n                }\r\n                // unPromise\r\n                const eles = newEle.map((v) => {\r\n                    if (v.beforeMount || v.onMounted) {\r\n                        subscribe && subscribe(v);\r\n                    }\r\n                    return v.ele;\r\n                });\r\n                ele.replaceChildren(createElementBlock(eles));\r\n                return;\r\n            }\r\n        });\r\n        // Promise\r\n        if (children.value instanceof Promise) {\r\n            // 插入注释元素\r\n            ele.appendChild(comment);\r\n            children.value.then((childrenEle) => {\r\n                if (childrenEle) {\r\n                    const eles = childrenEle.map((v) => {\r\n                        if (v.beforeMount || v.onMounted) {\r\n                            subscribe && subscribe(v);\r\n                        }\r\n                        return v.ele;\r\n                    });\r\n                    ele.replaceChildren(createElementBlock(eles));\r\n                }\r\n            });\r\n            return;\r\n        }\r\n        // unPromise\r\n        if (children.value) {\r\n            const eles = children.value.map((v) => {\r\n                if (v.beforeMount || v.onMounted) {\r\n                    subscribe && subscribe(v);\r\n                }\r\n                return v.ele;\r\n            });\r\n            ele.appendChild(createElementBlock(eles));\r\n            return;\r\n        }\r\n        // 插入元素\r\n        ele.appendChild(comment);\r\n        return;\r\n    }\r\n    // Promise\r\n    if (children instanceof Promise) {\r\n        // 注释元素\r\n        const comment = document.createComment('');\r\n        // 插入注释元素\r\n        ele.appendChild(comment);\r\n        // 异步替换元素\r\n        children.then((childEle) => {\r\n            if (childEle) {\r\n                const { beforeMount, onMounted } = childEle;\r\n                if (beforeMount || onMounted) {\r\n                    subscribe && subscribe(childEle);\r\n                }\r\n                comment.replaceWith(childEle.ele);\r\n            }\r\n        });\r\n        return;\r\n    }\r\n    // Array\r\n    if (Array.isArray(children)) {\r\n        // 处理过后\r\n        const resChildren = [];\r\n        for (const i in children) {\r\n            const child = children[i];\r\n            // Ref\r\n            if (isRef(child)) {\r\n                // 注释\r\n                const comment = document.createComment('');\r\n                // 监听影响\r\n                watch(child, async (newEle, oldEle) => {\r\n                    // 新元素为空\r\n                    if (!newEle && oldEle) {\r\n                        // Promise\r\n                        if (oldEle instanceof Promise) {\r\n                            const oldEleRes = await oldEle;\r\n                            if (oldEleRes) {\r\n                                handleChangeElement(newEle, oldEleRes, comment, subscribe, unsubscribe);\r\n                            }\r\n                            return;\r\n                        }\r\n                        handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe);\r\n                        return;\r\n                    }\r\n                    // 旧元素为空\r\n                    if (newEle && !oldEle) {\r\n                        // Promise\r\n                        if (newEle instanceof Promise) {\r\n                            const newEleRes = await newEle;\r\n                            if (newEleRes) {\r\n                                handleChangeElement(newEleRes, oldEle, comment, subscribe, unsubscribe);\r\n                            }\r\n                            return;\r\n                        }\r\n                        handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe);\r\n                        return;\r\n                    }\r\n                    // 存在\r\n                    if (newEle && oldEle) {\r\n                        // Promise\r\n                        if (newEle instanceof Promise && oldEle instanceof Promise) {\r\n                            const newEleRes = await newEle;\r\n                            const oldEleRes = await oldEle;\r\n                            // 处理元素变化\r\n                            handleChangeElement(newEleRes, oldEleRes, comment, subscribe, unsubscribe);\r\n                            return;\r\n                        }\r\n                        // Promise\r\n                        if (newEle instanceof Promise && !(oldEle instanceof Promise)) {\r\n                            const newEleRes = await newEle;\r\n                            // 处理元素变化\r\n                            handleChangeElement(newEleRes, oldEle, comment, subscribe, unsubscribe);\r\n                            return;\r\n                        }\r\n                        // Promise\r\n                        if (!(newEle instanceof Promise) && oldEle instanceof Promise) {\r\n                            const oldEleRes = await oldEle;\r\n                            // 处理元素变化\r\n                            handleChangeElement(newEle, oldEleRes, comment, subscribe, unsubscribe);\r\n                            return;\r\n                        }\r\n                        // 非 Promise\r\n                        if (!(oldEle instanceof Promise) && !(newEle instanceof Promise)) {\r\n                            // 处理元素变化\r\n                            handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe);\r\n                            return;\r\n                        }\r\n                    }\r\n                });\r\n                // Promise\r\n                if (child.value instanceof Promise) {\r\n                    // 注释\r\n                    resChildren[i] = { ele: comment };\r\n                    // 异步替换\r\n                    child.value.then((childEle) => {\r\n                        if (childEle) {\r\n                            const { beforeMount, onMounted } = childEle;\r\n                            if (beforeMount || onMounted) {\r\n                                subscribe && subscribe(childEle);\r\n                            }\r\n                            comment.replaceWith(childEle.ele);\r\n                        }\r\n                    });\r\n                    continue;\r\n                }\r\n                // unPromise\r\n                if (child.value) {\r\n                    const { beforeMount, onMounted, ele } = child.value;\r\n                    resChildren[i] = { ele, beforeMount, onMounted };\r\n                    continue;\r\n                }\r\n                resChildren[i] = { ele: comment };\r\n                continue;\r\n            }\r\n            // Promise\r\n            if (child instanceof Promise) {\r\n                // 注释\r\n                const comment = document.createComment('');\r\n                resChildren[i] = { ele: comment };\r\n                // 异步替换元素\r\n                child.then((childEle) => {\r\n                    if (childEle) {\r\n                        const { beforeMount, onMounted } = childEle;\r\n                        if (beforeMount || onMounted) {\r\n                            subscribe && subscribe(childEle);\r\n                        }\r\n                        comment.replaceWith(childEle.ele);\r\n                    }\r\n                });\r\n                continue;\r\n            }\r\n            // 普通元素\r\n            if (child) {\r\n                const { beforeMount, onMounted, ele } = child;\r\n                resChildren[i] = { ele, beforeMount, onMounted };\r\n            }\r\n        }\r\n        const eles = resChildren.map((v) => {\r\n            if (v.beforeMount || v.onMounted) {\r\n                subscribe && subscribe(v);\r\n            }\r\n            return v.ele;\r\n        });\r\n        // 插入元素\r\n        ele.appendChild(createElementBlock(eles));\r\n        return;\r\n    }\r\n    // 普通元素\r\n    if (children) {\r\n        const { beforeMount, onMounted } = children;\r\n        if (beforeMount || onMounted) {\r\n            subscribe && subscribe(children);\r\n        }\r\n        // 插入元素\r\n        ele.appendChild(children.ele);\r\n        return;\r\n    }\r\n    return;\r\n}\r\n/**\r\n * @description 元素变化\r\n * @param newEle\r\n * @param oldEle\r\n * @param comment\r\n */\r\nfunction handleChangeElement(newEle, oldEle, comment, subscribe, unsubscribe) {\r\n    if (newEle && oldEle) {\r\n        const { beforeMount, onMounted } = newEle;\r\n        if (beforeMount || onMounted) {\r\n            subscribe && subscribe(newEle);\r\n        }\r\n        oldEle.ele.replaceWith(newEle.ele);\r\n        return;\r\n    }\r\n    if (newEle && !oldEle) {\r\n        const { beforeMount, onMounted } = newEle;\r\n        if (beforeMount || onMounted) {\r\n            subscribe && subscribe(newEle);\r\n        }\r\n        comment.replaceWith(newEle.ele);\r\n        return;\r\n    }\r\n    if (!newEle && oldEle) {\r\n        unsubscribe && unsubscribe(oldEle);\r\n        oldEle.ele.replaceWith(comment);\r\n        return;\r\n    }\r\n}\r\n/**\r\n * @description 创建文字节点\r\n * @param text\r\n * @returns\r\n */\r\nfunction createTextNode(text, options) {\r\n    const { onCreated, beforeCreat, onMounted, beforeMount } = options || {};\r\n    // 创建元素前\r\n    beforeCreat && beforeCreat();\r\n    // Ref\r\n    if (isRef(text)) {\r\n        // ref\r\n        const refVal = text;\r\n        // 元素\r\n        const ele = document.createTextNode('');\r\n        // 订阅变化\r\n        watchEffect(() => {\r\n            ele.data = refVal.value;\r\n        });\r\n        // 创建元素后\r\n        onCreated && onCreated();\r\n        return { ele, beforeMount, onMounted };\r\n    }\r\n    // 创建元素后\r\n    onCreated && onCreated();\r\n    return { ele: document.createTextNode(String(text)), beforeMount, onMounted };\r\n}\r\n/**\r\n * @description 挂载元素\r\n * @param eleOptions\r\n * @param parent\r\n */\r\nfunction mountElement(eleOptions, parent = document.body) {\r\n    const { ele, beforeMount, onMounted } = eleOptions;\r\n    if (ele) {\r\n        // 触发挂载前事件\r\n        beforeMount && beforeMount();\r\n        parent.appendChild(ele);\r\n        // 挂在后\r\n        onMounted && onMounted();\r\n    }\r\n}\r\n/**\r\n * @description 选择器\r\n * @param selector\r\n * @returns\r\n */\r\nfunction $$(selector, parent = document) {\r\n    return Array.from(parent.querySelectorAll(selector));\r\n}\r\n/**\r\n * @description 异步选择器\r\n * @param selector\r\n * @returns\r\n */\r\nfunction $_(selector, parent = document, timeout) {\r\n    return new Promise((resolve) => {\r\n        const timer = setInterval(() => {\r\n            const selectors = Array.from(parent.querySelectorAll(selector));\r\n            // 存在元素\r\n            if (selectors.length) {\r\n                clearInterval(timer);\r\n                resolve(selectors);\r\n            }\r\n        }, 10);\r\n        // 超时\r\n        if (timeout) {\r\n            setTimeout(() => {\r\n                clearInterval(timer);\r\n                resolve([]);\r\n            }, timeout);\r\n        }\r\n    });\r\n}\r\n/**\r\n * @description 创建元素块\r\n * @param eles\r\n * @returns\r\n */\r\nfunction createElementBlock(eles) {\r\n    const fragment = document.createDocumentFragment();\r\n    for (const i in eles) {\r\n        fragment.appendChild(eles[i]);\r\n    }\r\n    return fragment;\r\n}\r\n\r\n","from":"src\\utils\\element.ts","to":"element.js"},"src\\utils\\log.ts":{"timeStamp":"2023-03-04T04:21:42.715Z","data":"/**\r\n * @description 打印日志\r\n * @param text\r\n */\r\nfunction log(...text) {\r\n    printColor('dodgerblue', ...text);\r\n}\r\n/**\r\n * @description 打印错误\r\n * @param text\r\n */\r\nfunction error(...text) {\r\n    printColor('red', ...text);\r\n}\r\n/**\r\n * @description 打印信息\r\n * @param text\r\n */\r\nfunction info(...text) {\r\n    printColor('yellow', ...text);\r\n}\r\n/**\r\n * @description 打印颜色\r\n * @param text\r\n * @param color\r\n */\r\nfunction printColor(color, ...text) {\r\n    const textFormatted = text\r\n        .map((t) => (typeof t === 'object' ? JSON.stringify(t) : String(t)))\r\n        .join(' ');\r\n    console.log(`%c[${formatDateTime()}] %c${textFormatted}`, '', `color: ${color}`);\r\n}\r\n\r\n","from":"src\\utils\\log.ts","to":"log.js"},"src\\utils\\push.ts":{"timeStamp":"2023-07-04T09:34:28.693Z","data":"/**\r\n * @description html进度条\r\n * @param title\r\n * @param percent\r\n * @returns\r\n */\r\nfunction getProgressHTML(title, current, total) {\r\n    // html\r\n    const progressHTML = `<div\r\n    style=\"\r\n      display: flex;\r\n      align-items: center;\r\n      justify-content: space-between;\r\n      padding: 1px 0;\r\n    \"\r\n  >\r\n    <span>${title}</span>\r\n    <span>${getHighlightHTML(`${current}`)} / ${total}</span>\r\n  </div>\r\n  <div\r\n    style=\"\r\n      background: white;\r\n      border-radius: 10px;\r\n      height: 10px;\r\n      border: 1px solid #eee;\r\n      flex-shrink: 1;\r\n    \"\r\n  >\r\n    <div\r\n      style=\"\r\n        background: linear-gradient(to left, #188fff80, #1890ff);\r\n        height: 100%;\r\n        width: ${((100 * current) / total).toFixed(1)}%;\r\n        border-radius: 10px;\r\n      \"\r\n    ></div>\r\n  </div>`;\r\n    return progressHTML;\r\n}\r\n/**\r\n * @description html高亮文本\r\n * @param text\r\n * @returns\r\n */\r\nfunction getHighlightHTML(text) {\r\n    // html\r\n    const highlightHTML = `<span style=\"color: #1890ff\">${text}</span>`;\r\n    return highlightHTML;\r\n}\r\n/**\r\n * @description 二维码\r\n * @param src\r\n */\r\nfunction getImgHTML(src) {\r\n    // 图片\r\n    return `\r\n     <div style=\"padding: 10px 0\">\r\n     <div\r\n       style=\"\r\n         display: flex;\r\n         justify-content: center;\r\n         align-items: center;\r\n         padding: 20px;\r\n         background: #f7f7f7;\r\n         border-radius: 10px;\r\n       \"\r\n     >\r\n         <img src=\"${src}\" style=\"width:200px;height:200px;\" />\r\n       </div>\r\n     </div>\r\n`;\r\n}\r\n/**\r\n * @description 创建模态框\r\n * @param options 选项\r\n * @returns\r\n */\r\nfunction createModal(options) {\r\n    // 配置\r\n    const { title, subTitle = '', to = '用户', content, type, from = 'tech-study.js', } = options;\r\n    // 内容文本\r\n    let contentText = '';\r\n    if (Array.isArray(content)) {\r\n        contentText = content.map((ct) => `<div>${ct}</div>`).join('');\r\n    }\r\n    else {\r\n        contentText = content;\r\n    }\r\n    // 日期\r\n    const dateTime = formatDateTime();\r\n    // 类型html\r\n    let typeHTML = '';\r\n    if (type && type.length) {\r\n        if (type === 'info') {\r\n            typeHTML = `\r\n      <svg\r\n       viewBox=\"64 64 896 896\"\r\n       style=\"color: #1890ff; width: 18px; height: 18px\"\r\n       fill=\"currentColor\"\r\n       aria-hidden=\"true\"\r\n     >\r\n       <path\r\n         d=\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\"\r\n       ></path>\r\n     </svg>`;\r\n        }\r\n        if (type === 'warn') {\r\n            typeHTML = `\r\n      <svg\r\n        viewBox=\"64 64 896 896\"\r\n        style=\"color: #faad14; width: 18px; height: 18px\"\r\n        fill=\"currentColor\"\r\n        aria-hidden=\"true\"\r\n      >\r\n        <path\r\n          d=\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z\"\r\n        ></path>\r\n      </svg>\r\n      `;\r\n        }\r\n        if (type === 'success') {\r\n            typeHTML = `\r\n      <svg\r\n        viewBox=\"64 64 896 896\"\r\n        style=\"color: #52c41a; width: 18px; height: 18px\"\r\n        fill=\"currentColor\"\r\n        aria-hidden=\"true\"\r\n      >\r\n        <path\r\n          d=\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z\"\r\n        ></path>\r\n      </svg>\r\n      `;\r\n        }\r\n        if (type === 'fail') {\r\n            typeHTML = `\r\n      <svg\r\n        viewBox=\"64 64 896 896\"\r\n        style=\"color: #ff4d4f; width: 18px; height: 18px\"\r\n        fill=\"currentColor\"\r\n        aria-hidden=\"true\"\r\n      >\r\n        <path\r\n          d=\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z\"\r\n        ></path>\r\n      </svg>\r\n      `;\r\n        }\r\n    }\r\n    // 类型\r\n    const typeWrap = `\r\n  <span\r\n    style=\"\r\n      padding-right: 5px;\r\n      display: flex;\r\n      justify-content: center;\r\n      align-items: center;\r\n    \"\r\n  >\r\n    ${typeHTML}\r\n  </span>\r\n  `;\r\n    // 基础html\r\n    const baseHTML = `\r\n  <div\r\n  style=\"\r\n    padding: 5px;\r\n    display: flex;\r\n    justify-content: center;\r\n    align-items: center;\r\n  \"\r\n>\r\n  <div\r\n    style=\"\r\n      background: #ffffff;\r\n      box-shadow: 1px 1px 8px -1px #dadada;\r\n      padding: 5px 10px;\r\n      border-radius: 5px;\r\n      width: 100%;\r\n    \"\r\n  >\r\n    <div\r\n      style=\"\r\n        display: flex;\r\n        justify-content: space-between;\r\n        padding: 5px;\r\n        border-bottom: 1px solid #eee;\r\n      \"\r\n    >\r\n      <div style=\"display: flex; justify-content: center; align-items: center\">\r\n        ${typeWrap}\r\n        <span style=\"padding-left: 5px; font-size: 18px\">${title}</span>\r\n      </div>\r\n      <div style=\"font-size: 16px; color: #999\">${subTitle}</div>\r\n    </div>\r\n    <div></div>\r\n\r\n    <div style=\"padding:10px 5px; font-size: 16px; min-height: 80px\">\r\n      <div>\r\n        ${getHighlightHTML(to)}, 你好!\r\n      </div>\r\n      <div style=\"line-height: 28px;\">${contentText}</div>\r\n    </div>\r\n    <div\r\n      style=\"\r\n        font-size: 14px;\r\n        padding: 5px;\r\n        border-top: 1px solid #eee;\r\n        display: flex;\r\n        justify-content: space-between;\r\n        align-items: center;\r\n      \"\r\n    >\r\n      <div style=\"color: #999\">${dateTime}</div>\r\n      <div>\r\n        <span>来自</span>\r\n        <span style=\"color: #1890ff; padding-left: 1px\">${from}</span>\r\n      </div>\r\n    </div>\r\n  </div>\r\n</div>  \r\n  `;\r\n    return baseHTML;\r\n}\r\n/**\r\n * @description 推送消息\r\n */\r\nasync function pushMessage(options) {\r\n    // 选项\r\n    const { title, content, template, fromToken, toToken } = options;\r\n    // 推送\r\n    const res = await pushPlus(fromToken, title, content, template, toToken);\r\n    return res;\r\n}\r\n/**\r\n * @description 推送模态框\r\n */\r\nasync function pushModal(options, fromToken, toToken) {\r\n    // html\r\n    const html = createModal(options);\r\n    // 推送\r\n    const res = await pushMessage({\r\n        title: '消息提示',\r\n        content: html,\r\n        fromToken,\r\n        toToken,\r\n        template: 'html',\r\n    });\r\n    if (res && res.code === 200) {\r\n        return res;\r\n    }\r\n    return;\r\n}\r\n\r\n","from":"src\\utils\\push.ts","to":"push.js"},"src\\utils\\random.ts":{"timeStamp":"2023-02-11T12:25:34.984Z","data":"/**\r\n * @description 创建随机点\r\n * @param bounds 范围\r\n * @returns\r\n */\r\nfunction createRandomPoint(bounds) {\r\n    // 范围\r\n    const { x, y, width, height } = bounds;\r\n    // 横坐标\r\n    const randX = x + Math.random() * width * 0.5 + width * 0.25;\r\n    // 纵坐标\r\n    const randY = y + Math.random() * height * 0.5 + height * 0.25;\r\n    return {\r\n        x: randX,\r\n        y: randY,\r\n    };\r\n}\r\n/**\r\n * @description 生成随机路径\r\n * @param start\r\n * @param end\r\n * @param steps\r\n * @returns\r\n */\r\nfunction createRandomPath(start, end, steps) {\r\n    // 最小水平增量\r\n    const minDeltaX = (end.x - start.x) / steps;\r\n    // 最大垂直增量\r\n    const maxDeltaY = (end.y - start.y) / steps;\r\n    const path = [];\r\n    // 开始节点\r\n    path.push(start);\r\n    // 插入点\r\n    for (let i = 0; i < steps; i++) {\r\n        // 横坐标\r\n        const x = path[i].x + Math.random() * 5 + minDeltaX;\r\n        // 纵坐标\r\n        const y = path[i].y +\r\n            Math.random() * 5 * Math.pow(-1, ~~(Math.random() * 2 + 1)) +\r\n            maxDeltaY;\r\n        path.push({\r\n            x,\r\n            y,\r\n        });\r\n    }\r\n    return path;\r\n}\r\n/**\r\n * @description 随机数字\r\n * @returns\r\n */\r\nfunction generateNumAsChar() {\r\n    return (~~(Math.random() * 10)).toString();\r\n}\r\n/**\r\n * @description 随机大写字母\r\n * @returns\r\n */\r\nfunction generateUpperAsChar() {\r\n    return String.fromCharCode(~~(Math.random() * 26) + 65);\r\n}\r\n/**\r\n * @description 随机小写字母\r\n * @returns\r\n */\r\nfunction generateLowerAsChar() {\r\n    return String.fromCharCode(~~(Math.random() * 26) + 97);\r\n}\r\n/**\r\n * @description 随机混合字符\r\n * @param length\r\n * @returns\r\n */\r\nfunction generateMix(length = 6) {\r\n    // 随机字符串\r\n    const randomText = [];\r\n    // 生成器\r\n    const typeGenerator = [\r\n        generateNumAsChar,\r\n        generateUpperAsChar,\r\n        generateLowerAsChar,\r\n    ];\r\n    if (length) {\r\n        for (let i = 0; i < length; i++) {\r\n            // 随机位置\r\n            const randomIndex = ~~(Math.random() * typeGenerator.length);\r\n            randomText.push(typeGenerator[randomIndex]());\r\n        }\r\n    }\r\n    return randomText.join('');\r\n}\r\n\r\n","from":"src\\utils\\random.ts","to":"random.js"},"src\\utils\\time.ts":{"timeStamp":"2023-02-11T12:25:31.463Z","data":"/**\r\n * @description 格式化日期时间数字\r\n * @param num\r\n * @returns\r\n */\r\nfunction formatDateNum(num) {\r\n    return num < 10 ? `0${num}` : `${num}`;\r\n}\r\n/**\r\n * @description 格式化日期时间\r\n * @param time\r\n * @returns\r\n * @example\r\n * formatDateTime() -> \"2022-09-01 08:00:00\"\r\n * formatDateTime(new Date()) -> \"2022-09-01 08:00:00\"\r\n * formatDateTime(Date.now()) -> \"2022-09-01 08:00:00\"\r\n */\r\nfunction formatDateTime(time = Date.now()) {\r\n    const date = new Date(time);\r\n    const s = date.getSeconds();\r\n    const min = date.getMinutes();\r\n    const h = date.getHours();\r\n    const d = date.getDate();\r\n    const m = date.getMonth() + 1;\r\n    const y = date.getFullYear();\r\n    // 日期\r\n    const dateText = [y, m, d].map(formatDateNum).join('-');\r\n    // 时间\r\n    const timeText = [h, min, s].map(formatDateNum).join(':');\r\n    // 日期时间\r\n    const dateTimeText = `${dateText} ${timeText}`;\r\n    return dateTimeText;\r\n}\r\n/**\r\n * @description 格式化时间\r\n * @param time\r\n * @returns\r\n * @example\r\n * formatTime() -> \"08:00:00\"\r\n * formatTime(new Date()) -> \"08:00:00\"\r\n * formatTime(Date.now()) -> \"08:00:00\"\r\n */\r\nconst formatTime = (time = Date.now()) => {\r\n    const date = new Date(time);\r\n    const s = date.getSeconds();\r\n    const min = date.getMinutes();\r\n    const h = date.getHours();\r\n    // 时间\r\n    const timeText = [h, min, s].map(formatDateNum).join(':');\r\n    return timeText;\r\n};\r\n/**\r\n * @description 时间已过\r\n * @param hour\r\n * @param minute\r\n * @returns\r\n */\r\nfunction isLate({ hour, minute }) {\r\n    const date = new Date();\r\n    const h = date.getHours();\r\n    const min = date.getMinutes();\r\n    return h > hour || (h === hour && min >= minute);\r\n}\r\n/**\r\n * @description 时间已过\r\n * @param hour\r\n * @param minute\r\n * @returns\r\n */\r\nfunction isNow({ hour, minute }) {\r\n    const date = new Date();\r\n    const h = date.getHours();\r\n    const min = date.getMinutes();\r\n    const s = date.getSeconds();\r\n    return h === hour && min === minute && s === 0;\r\n}\r\n\r\n","from":"src\\utils\\time.ts","to":"time.js"},"src\\utils\\utils.ts":{"timeStamp":"2023-07-12T04:27:35.825Z","data":"/* 工具函数 */\r\n/**\r\n * @description 设置cookie\r\n * @param name\r\n * @param value\r\n * @param expires\r\n */\r\nfunction setCookie(name, value, expires, domain) {\r\n    // 当前日期\r\n    const date = new Date();\r\n    // 过期日期\r\n    date.setTime(date.getTime() + expires);\r\n    // 设置cookie\r\n    document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;domain=${domain}`;\r\n}\r\n/**\r\n * @description 获取cookie\r\n * @param name\r\n * @returns\r\n */\r\nfunction getCookie(name) {\r\n    // 获取当前所有cookie\r\n    const strCookies = document.cookie;\r\n    // 截取变成cookie数组\r\n    const cookieText = strCookies.split(';');\r\n    // 循环每个cookie\r\n    for (const i in cookieText) {\r\n        // 将cookie截取成两部分\r\n        const item = cookieText[i].split('=');\r\n        // 判断cookie的name 是否相等\r\n        if (item[0].trim() === name) {\r\n            return item[1].trim();\r\n        }\r\n    }\r\n    return null;\r\n}\r\n/**\r\n * @description 删除cookie\r\n * @param name\r\n */\r\nfunction delCookie(name, domain) {\r\n    // 存在cookie\r\n    const value = getCookie(name);\r\n    if (value !== null) {\r\n        setCookie(name, '', -1, domain);\r\n    }\r\n}\r\n/**\r\n * @description 防抖\r\n * @param callback\r\n * @param delay\r\n * @returns\r\n */\r\nfunction debounce(callback, delay) {\r\n    let timer = -1;\r\n    return function (...args) {\r\n        if (timer !== -1) {\r\n            clearTimeout(timer);\r\n        }\r\n        timer = setTimeout(() => {\r\n            callback.apply(this, args);\r\n        }, delay);\r\n    };\r\n}\r\n/**\r\n * @description 判断是否为移动端\r\n * @returns\r\n */\r\nfunction hasMobile() {\r\n    let isMobile = false;\r\n    if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) {\r\n        log('移动端');\r\n        isMobile = true;\r\n    }\r\n    if (document.body.clientWidth < 800) {\r\n        log('小尺寸设备端');\r\n        isMobile = true;\r\n    }\r\n    return isMobile;\r\n}\r\n/**\r\n * @description 等待时间\r\n * @param time\r\n * @returns\r\n */\r\nfunction sleep(time) {\r\n    // 延时\r\n    let timeDelay = Number(time);\r\n    if (!Number.isInteger(timeDelay)) {\r\n        timeDelay = 1000;\r\n    }\r\n    timeDelay += Math.random() * 500 - 250;\r\n    return new Promise((resolve) => {\r\n        setTimeout(() => {\r\n            resolve(undefined);\r\n        }, timeDelay);\r\n    });\r\n}\r\n/**\r\n * @description 暂停学习锁\r\n */\r\nfunction studyPauseLock(callback) {\r\n    return new Promise((resolve) => {\r\n        // 暂停\r\n        const pauseStudy = GM_getValue('pauseStudy') || false;\r\n        if (pauseStudy) {\r\n            const doing = setInterval(() => {\r\n                // 暂停\r\n                const pauseStudy = GM_getValue('pauseStudy') || false;\r\n                if (!pauseStudy) {\r\n                    // 停止定时器\r\n                    clearInterval(doing);\r\n                    log('学习等待结束!');\r\n                    if (callback && callback instanceof Function) {\r\n                        callback(true);\r\n                    }\r\n                    resolve(true);\r\n                    return;\r\n                }\r\n                if (callback && callback instanceof Function) {\r\n                    callback(false);\r\n                }\r\n                log('学习等待...');\r\n            }, 500);\r\n            return;\r\n        }\r\n        resolve(true);\r\n    });\r\n}\r\n/**\r\n * @description 加载\r\n * @param match\r\n * @param callback\r\n */\r\nfunction load(match, callback) {\r\n    // 链接\r\n    const { href } = window.location;\r\n    window.addEventListener('load', () => {\r\n        // 函数\r\n        if (match instanceof Function) {\r\n            match(href) && callback();\r\n            return;\r\n        }\r\n        // 布尔\r\n        if (typeof match === 'boolean') {\r\n            match && callback();\r\n            return;\r\n        }\r\n        // 字符正则\r\n        if (href.match(match)) {\r\n            callback();\r\n            return;\r\n        }\r\n    });\r\n}\r\n\r\n","from":"src\\utils\\utils.ts","to":"utils.js"},"src\\shared\\index.ts":{"timeStamp":"2023-07-12T05:59:54.970Z","data":"/* 变量 */\r\n/**\r\n * @description 链接\r\n */\r\nconst href = window.location.href;\r\n/**\r\n * @description 任务配置\r\n */\r\nconst taskConfig = reactive([\r\n    {\r\n        title: '登录',\r\n        currentScore: 0,\r\n        dayMaxScore: 0,\r\n        need: 0,\r\n        status: false,\r\n        tip: '每日首次登录积1分。',\r\n        score: 0,\r\n        active: true,\r\n        immutable: true,\r\n        type: TaskType.LOGIN,\r\n    },\r\n    {\r\n        title: '文章选读',\r\n        currentScore: 0,\r\n        dayMaxScore: 0,\r\n        need: 0,\r\n        status: false,\r\n        tip: '每有效阅读一篇文章积1分,上限6分。有效阅读文章累计1分钟积1分,上限6分。每日上限积12分。',\r\n        score: 0,\r\n        active: true,\r\n        immutable: false,\r\n        type: TaskType.READ,\r\n    },\r\n    {\r\n        title: '视听学习',\r\n        currentScore: 0,\r\n        dayMaxScore: 0,\r\n        need: 0,\r\n        status: false,\r\n        tip: '每有效一个音频或观看一个视频积1分,上限6分。有效收听音频或观看视频累计1分钟积1分,上限6分。每日上限积12分。',\r\n        score: 0,\r\n        active: true,\r\n        immutable: false,\r\n        type: TaskType.WATCH,\r\n    },\r\n    {\r\n        title: '每日答题',\r\n        currentScore: 0,\r\n        dayMaxScore: 0,\r\n        need: 0,\r\n        status: false,\r\n        tip: '每组答题每答对1道积1分。每日上限积5分。',\r\n        score: 0,\r\n        active: true,\r\n        immutable: false,\r\n        type: TaskType.PRACTICE,\r\n    },\r\n]);\r\n/**\r\n * @description 设置\r\n */\r\nconst settings = reactive([\r\n    false,\r\n    false,\r\n    false,\r\n    false,\r\n    false,\r\n    false,\r\n    false,\r\n    false,\r\n]);\r\n/**\r\n * @description 总分\r\n */\r\nconst totalScore = ref(0);\r\n/**\r\n * @description 当天分数\r\n */\r\nconst todayScore = ref(0);\r\n/**\r\n * @description 用户信息\r\n */\r\nconst userinfo = reactive({\r\n    nick: '',\r\n    avatar: '',\r\n});\r\n/**\r\n * @description 进度\r\n */\r\nconst taskStatus = ref(TaskStatusType.LOADING);\r\n/**\r\n * @description 答题暂停\r\n */\r\nconst examPause = ref(false);\r\n/**\r\n * @description 登录\r\n */\r\nconst login = ref(!!getCookie('token'));\r\n/**\r\n * @description 窗口id\r\n */\r\nconst id = ref('');\r\n/**\r\n * @description 定时刷新列表\r\n */\r\nconst scheduleList = shallowReactive([]);\r\n/**\r\n * @description 推送token\r\n */\r\nconst pushToken = ref('');\r\n/**\r\n * @description 刷新次数\r\n */\r\nconst refreshCount = ref(0);\r\n/**\r\n * @description 窗口关闭\r\n */\r\nconst frame = reactive({\r\n    title: '',\r\n    show: false,\r\n    exist: false,\r\n    closed: true,\r\n    ele: undefined,\r\n    src: '',\r\n});\r\n/**\r\n * @description 页面\r\n */\r\nconst page = ref(undefined);\r\n/**\r\n * @description 开始登录\r\n */\r\nconst loginQRCodeShow = ref(false);\r\n/**\r\n * @description 最大选读时长\r\n */\r\nconst maxRead = ref(100);\r\n/**\r\n * @description 最大视听时长\r\n */\r\nconst maxWatch = ref(120);\r\n/**\r\n * @description 运行其他任务\r\n */\r\nconst running = ref(false);\r\n/**\r\n * @description 主题色\r\n */\r\nconst themeColor = ref('#fa3333');\r\n\r\n","from":"src\\shared\\index.ts","to":"index.js"},"src\\controller\\exam.ts":{"timeStamp":"2023-06-03T04:43:48.117Z","data":"/**\r\n * @description  考试类型\r\n */\r\nvar ExamType;\r\n(function (ExamType) {\r\n    ExamType[ExamType[\"PRACTICE\"] = 0] = \"PRACTICE\";\r\n    ExamType[ExamType[\"PAPER\"] = 1] = \"PAPER\";\r\n})(ExamType || (ExamType = {}));\r\n/**\r\n * @description 获取答题按钮\r\n */\r\nfunction getNextButton() {\r\n    return new Promise((resolve) => {\r\n        const timer = setInterval(() => {\r\n            // 答题按钮\r\n            const nextAll = $$('.ant-btn').filter((next) => next.innerText);\r\n            if (nextAll.length) {\r\n                // 停止定时器\r\n                clearInterval(timer);\r\n                if (nextAll.length === 2) {\r\n                    resolve(nextAll[1]);\r\n                    return;\r\n                }\r\n                resolve(nextAll[0]);\r\n            }\r\n        }, 500);\r\n    });\r\n}\r\n/**\r\n * @description 处理滑动验证\r\n */\r\nfunction handleSlideVerify() {\r\n    return new Promise(async (resolve) => {\r\n        // 滑动验证\r\n        const mask = $$('#nc_mask')[0];\r\n        if (mask && getComputedStyle(mask).display !== 'none') {\r\n            // 创建提示\r\n            createTip('等待滑动验证');\r\n            // 提高层级\r\n            mask.style.zIndex = '999';\r\n            // 轨道\r\n            const track = (await $_('.nc_scale', undefined, 3000))[0];\r\n            // 滑块\r\n            const slide = (await $_('.btn_slide', undefined, 3000))[0];\r\n            // 延时\r\n            await sleep(2000);\r\n            // 矩形范围\r\n            const rectTrack = track.getBoundingClientRect();\r\n            // 矩形范围\r\n            const rectSlide = slide.getBoundingClientRect();\r\n            // 窗口\r\n            const window = unsafeWindow;\r\n            // 范围内随机起点\r\n            const start = createRandomPoint(rectSlide);\r\n            // 终点\r\n            const end = {\r\n                x: rectTrack.x + rectTrack.width,\r\n                y: rectTrack.y + rectTrack.height / 2,\r\n            };\r\n            // 路径\r\n            const path = createRandomPath(start, end, 10);\r\n            // 移动端\r\n            const mobile = hasMobile();\r\n            if (mobile) {\r\n                slide.style.touchAction = 'none';\r\n                const touchstartTouch = new Touch({\r\n                    identifier: 0,\r\n                    target: slide,\r\n                    clientX: path[0].x,\r\n                    clientY: path[0].y,\r\n                });\r\n                const touchstartList = [touchstartTouch];\r\n                // 开始触摸\r\n                const touchstart = new TouchEvent('touchstart', {\r\n                    targetTouches: touchstartList,\r\n                    touches: touchstartList,\r\n                    changedTouches: touchstartList,\r\n                    view: window,\r\n                    bubbles: true,\r\n                });\r\n                slide.dispatchEvent(touchstart);\r\n                // 触摸滑动\r\n                for (const i in path) {\r\n                    const touchmoveTouch = new Touch({\r\n                        identifier: 0,\r\n                        target: slide,\r\n                        clientX: path[i].x,\r\n                        clientY: path[i].y,\r\n                    });\r\n                    const touchmoveList = [touchmoveTouch];\r\n                    const touchmove = new TouchEvent('touchmove', {\r\n                        targetTouches: touchmoveList,\r\n                        touches: touchmoveList,\r\n                        changedTouches: touchmoveList,\r\n                        view: window,\r\n                        bubbles: true,\r\n                    });\r\n                    slide.dispatchEvent(touchmove);\r\n                    await sleep(10);\r\n                }\r\n                const touchendTouch = new Touch({\r\n                    identifier: 0,\r\n                    target: slide,\r\n                    clientX: path[path.length - 1].x,\r\n                    clientY: path[path.length - 1].y,\r\n                });\r\n                // 触摸结束\r\n                const touchendList = [touchendTouch];\r\n                // 开始触摸\r\n                const touchend = new TouchEvent('touchend', {\r\n                    targetTouches: [],\r\n                    touches: [],\r\n                    changedTouches: touchendList,\r\n                    view: window,\r\n                    bubbles: true,\r\n                });\r\n                slide.dispatchEvent(touchend);\r\n            }\r\n            else {\r\n                // 鼠标按下\r\n                const mousedown = new MouseEvent('mousedown', {\r\n                    clientX: path[0].x,\r\n                    clientY: path[0].y,\r\n                    bubbles: true,\r\n                    view: window,\r\n                });\r\n                slide.dispatchEvent(mousedown);\r\n                // 鼠标滑动\r\n                for (const i in path) {\r\n                    const mousemove = new MouseEvent('mousemove', {\r\n                        clientX: path[i].x,\r\n                        clientY: path[i].y,\r\n                        bubbles: true,\r\n                        view: window,\r\n                    });\r\n                    slide.dispatchEvent(mousemove);\r\n                    await sleep(10);\r\n                }\r\n                // 鼠标抬起\r\n                const mouseup = new MouseEvent('mouseup', {\r\n                    clientX: path[path.length - 1].x,\r\n                    clientY: path[path.length - 1].y,\r\n                    bubbles: true,\r\n                    view: window,\r\n                });\r\n                slide.dispatchEvent(mouseup);\r\n            }\r\n            // 创建提示\r\n            createTip('滑动验证完成!');\r\n            // 定时器\r\n            const timer = setInterval(() => {\r\n                // 滑动验证\r\n                const mask = $$('#nc_mask')[0];\r\n                if (!mask || getComputedStyle(mask).display === 'none') {\r\n                    log('滑动验证成功!');\r\n                    // 创建提示\r\n                    createTip('滑动验证成功!');\r\n                    clearInterval(timer);\r\n                    resolve(true);\r\n                    return;\r\n                }\r\n                resolve(false);\r\n                log('滑动验证失败!');\r\n                // 创建提示\r\n                createTip('滑动验证失败!');\r\n            }, 1000);\r\n            return;\r\n        }\r\n        resolve(true);\r\n    });\r\n}\r\n/**\r\n * @description 处理选项\r\n */\r\nfunction handleChoiceBtn(answers) {\r\n    // 选项按钮\r\n    const allBtns = $$('.q-answer');\r\n    // 答案存在\r\n    if (answers.length && allBtns.length) {\r\n        // 作答\r\n        return answers.every((answer) => {\r\n            // 答案存在\r\n            if (answer && answer.length) {\r\n                // 包含答案最短长度选项\r\n                let minLengthChoice;\r\n                // 遍历\r\n                allBtns.forEach((choice) => {\r\n                    // 选项文本\r\n                    const choiceText = choice.innerText.trim();\r\n                    // 无符号选项文本\r\n                    const unsignedChoiceText = choiceText.replaceAll(/[、,,。 ]/g, '');\r\n                    // 无符号答案\r\n                    const unsignedAnswer = answer.replaceAll(/[、,,。 ]/g, '');\r\n                    // 包含答案\r\n                    if (choiceText === answer ||\r\n                        choiceText.includes(answer) ||\r\n                        answer.includes(choiceText) ||\r\n                        unsignedChoiceText.includes(unsignedAnswer)) {\r\n                        // 最小长度选项有值\r\n                        if (minLengthChoice) {\r\n                            // 最短长度选项与当前选项比较长度\r\n                            if (minLengthChoice.innerText.length > choiceText.length) {\r\n                                minLengthChoice = choice;\r\n                            }\r\n                        }\r\n                        else {\r\n                            // 最小长度选项赋值\r\n                            minLengthChoice = choice;\r\n                        }\r\n                    }\r\n                });\r\n                // 存在选项\r\n                if (minLengthChoice) {\r\n                    // 选择\r\n                    if (!minLengthChoice.classList.contains('chosen')) {\r\n                        minLengthChoice.click();\r\n                    }\r\n                    return true;\r\n                }\r\n            }\r\n            return false;\r\n        });\r\n    }\r\n    return false;\r\n}\r\n/**\r\n * @description 随机处理单选\r\n */\r\nfunction handleSingleChoiceRand() {\r\n    // 选项按钮\r\n    const allBtns = $$('.q-answer');\r\n    // 按钮存在\r\n    if (allBtns.length) {\r\n        const index = ~~(Math.random() * allBtns.length);\r\n        const randBtn = allBtns[index];\r\n        // 选择\r\n        if (!randBtn.classList.contains('chosen')) {\r\n            randBtn.click();\r\n        }\r\n    }\r\n}\r\n/**\r\n * @description 随机处理多选\r\n */\r\nfunction handleMutiplyChoiceRand() {\r\n    // 选项按钮\r\n    const allBtns = $$('.q-answer');\r\n    // 按钮存在\r\n    if (allBtns.length) {\r\n        allBtns.forEach((allBtn) => {\r\n            // 选择\r\n            if (!allBtn.classList.contains('chosen')) {\r\n                allBtn.click();\r\n            }\r\n        });\r\n    }\r\n}\r\n/**\r\n * @description 处理填空\r\n */\r\nconst handleBlankInput = (answers) => {\r\n    // 所有填空\r\n    const blanks = $$('.blank');\r\n    // 答案存在\r\n    if (blanks.length && answers.length) {\r\n        // 填空数量和答案数量一致\r\n        if (answers.length === blanks.length) {\r\n            return answers.every((answer, i) => {\r\n                // 答案存在\r\n                if (answer && answer.length) {\r\n                    // 输入事件\r\n                    const inputEvent = new Event('input', {\r\n                        bubbles: true,\r\n                    });\r\n                    // 设置答案\r\n                    blanks[i].setAttribute('value', answer);\r\n                    // 触发输入input\r\n                    blanks[i].dispatchEvent(inputEvent);\r\n                    return true;\r\n                }\r\n                return false;\r\n            });\r\n        }\r\n        // 填空数量为1和提示数量大于1\r\n        if (blanks.length === 1 && answers.length > 1) {\r\n            // 直接将所有答案整合填进去\r\n            const answer = answers.join('');\r\n            // 答案存在\r\n            if (answer && answer.length) {\r\n                // 输入事件\r\n                const inputEvent = new Event('input', {\r\n                    bubbles: true,\r\n                });\r\n                // 设置答案\r\n                blanks[0].setAttribute('value', answer);\r\n                // 触发输入input\r\n                blanks[0].dispatchEvent(inputEvent);\r\n                return true;\r\n            }\r\n        }\r\n    }\r\n    return false;\r\n};\r\n/**\r\n * @description 处理填空随机\r\n */\r\nasync function handleBlankInputRand() {\r\n    // 所有填空\r\n    const blanks = $$('.blank');\r\n    if (blanks.length) {\r\n        // 输入事件\r\n        const inputEvent = new Event('input', {\r\n            bubbles: true,\r\n        });\r\n        blanks.forEach((blank) => {\r\n            // 设置答案\r\n            blank.setAttribute('value', '答案');\r\n            // 触发输入input\r\n            blank.dispatchEvent(inputEvent);\r\n        });\r\n    }\r\n}\r\n/**\r\n * @description 暂停锁\r\n */\r\nfunction examPauseLock(callback) {\r\n    return new Promise((resolve) => {\r\n        // 学习暂停\r\n        const pauseStudy = (GM_getValue('pauseStudy') || false);\r\n        // 全局暂停\r\n        if (pauseStudy) {\r\n            examPause.value = true;\r\n        }\r\n        // 暂停\r\n        if (examPause.value) {\r\n            // 创建提示\r\n            createTip('已暂停, 手动开启自动答题! ', 10);\r\n            const doing = setInterval(() => {\r\n                if (!examPause.value) {\r\n                    // 停止定时器\r\n                    clearInterval(doing);\r\n                    log('答题等待结束!');\r\n                    if (callback && callback instanceof Function) {\r\n                        // 创建提示\r\n                        createTip('已开启, 自动答题!');\r\n                        callback(true);\r\n                    }\r\n                    resolve(true);\r\n                    return;\r\n                }\r\n                if (callback && callback instanceof Function) {\r\n                    callback(false);\r\n                }\r\n                log('答题等待...');\r\n            }, 500);\r\n            return;\r\n        }\r\n        resolve(true);\r\n    });\r\n}\r\n/**\r\n * @description 答题\r\n */\r\nasync function doingExam(type) {\r\n    // 下一个按钮\r\n    let nextButton;\r\n    // 下一个文本\r\n    let nextText;\r\n    // 保存答案\r\n    let shouldSaveAnswer = false;\r\n    while (true) {\r\n        // 先等等再开始做题\r\n        await sleep(2500);\r\n        // 暂停\r\n        await examPauseLock();\r\n        // 获取下一个按钮\r\n        nextButton = await getNextButton();\r\n        // 下一个文本\r\n        nextText = nextButton.innerText.replaceAll(' ', '');\r\n        // 结束\r\n        const finish = ['再练一次', '再来一组', '查看解析'];\r\n        if (finish.includes(nextButton.innerText)) {\r\n            break;\r\n        }\r\n        // 点击提示\r\n        $$('.tips')[0]?.click();\r\n        // 所有提示\r\n        const allTips = $$('.line-feed font[color]');\r\n        // 答案\r\n        const answers = allTips.map((tip) => tip.innerText.trim());\r\n        // 获取题目的文本内容\r\n        const question = $$('.q-body')[0].innerText;\r\n        // 等待一段时间\r\n        await sleep(1500);\r\n        // 暂停\r\n        await examPauseLock();\r\n        // 选项按钮\r\n        const allBtns = $$('.q-answer');\r\n        // 所有填空\r\n        const blanks = $$('input[type=text][class=blank]');\r\n        // 问题类型\r\n        const questionType = ($$('.q-header')[0].innerText.substring(0, 3));\r\n        // 暂停\r\n        await examPauseLock();\r\n        // 题型分类作答\r\n        switch (questionType) {\r\n            case '填空题': {\r\n                // 根据提示作答\r\n                if (answers.length) {\r\n                    const res = handleBlankInput(answers);\r\n                    // 成功\r\n                    if (res) {\r\n                        break;\r\n                    }\r\n                }\r\n                // 创建提示\r\n                createTip('答案异常, 尝试网络题库获取!');\r\n                log('正在获取答案...');\r\n                // 尝试题库获取\r\n                const answersNetwork = await getAnswer(question);\r\n                log(`获取答案${answersNetwork.length ? '成功' : '失败'}!`, {\r\n                    question,\r\n                    answersNetwork,\r\n                });\r\n                // 根据题库作答\r\n                if (answersNetwork.length) {\r\n                    const res = handleBlankInput(answersNetwork);\r\n                    // 成功\r\n                    if (res) {\r\n                        break;\r\n                    }\r\n                }\r\n                // 随机作答\r\n                if (type === ExamType.PRACTICE || settings[SettingType.RANDOM_EXAM]) {\r\n                    log('答案不存在, 随机作答!');\r\n                    // 创建提示\r\n                    createTip('答案不存在, 随机作答!');\r\n                    await handleBlankInputRand();\r\n                }\r\n                else {\r\n                    // 推送\r\n                    const res = await pushModal({\r\n                        title: '学习推送',\r\n                        to: userinfo.nick,\r\n                        content: '答题存在异常, 已暂停答题!',\r\n                        type: 'fail',\r\n                    }, pushToken.value);\r\n                    createTip(`学习推送${res ? '成功' : '失败'}!`);\r\n                    // 暂停\r\n                    examPause.value = true;\r\n                    // 提交答案\r\n                    shouldSaveAnswer = true;\r\n                }\r\n                break;\r\n            }\r\n            case '多选题': {\r\n                // 根据提示作答\r\n                if (answers.length) {\r\n                    // 选项文本\r\n                    const choicesText = allBtns.map((btn) => btn.innerText);\r\n                    // 选项内容\r\n                    const choicesContent = choicesText\r\n                        .map((choiceText) => choiceText.split(/[A-Z]./)[1].trim())\r\n                        .join('');\r\n                    // 空格\r\n                    const blanks = question.match(/()/g);\r\n                    // 填空数量、选项数量、答案数量相同 | 选项全文等于答案全文\r\n                    if ((blanks && allBtns.length === blanks.length) ||\r\n                        question === choicesContent ||\r\n                        allBtns.length === 2) {\r\n                        // 全选\r\n                        allBtns.forEach((choice) => {\r\n                            if (!choice.classList.contains('chosen')) {\r\n                                choice.click();\r\n                            }\r\n                        });\r\n                        break;\r\n                    }\r\n                    // 选项数量大于等于答案\r\n                    if (allBtns.length >= answers.length) {\r\n                        const res = handleChoiceBtn(answers);\r\n                        // 成功\r\n                        if (res) {\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n                // 创建提示\r\n                createTip('答案异常, 尝试网络题库获取!');\r\n                log('正在获取答案...');\r\n                // 尝试题库获取\r\n                const answersNetwork = await getAnswer(question);\r\n                log(`获取答案${answersNetwork.length ? '成功' : '失败'}!`, {\r\n                    question,\r\n                    answersNetwork,\r\n                });\r\n                // 答案存在\r\n                if (answersNetwork.length) {\r\n                    const res = handleChoiceBtn(answersNetwork);\r\n                    // 成功\r\n                    if (res) {\r\n                        break;\r\n                    }\r\n                }\r\n                // 随机作答\r\n                if (type === ExamType.PRACTICE || settings[SettingType.RANDOM_EXAM]) {\r\n                    log('答案不存在, 随机作答!');\r\n                    // 创建提示\r\n                    createTip('答案不存在, 随机作答!');\r\n                    await handleMutiplyChoiceRand();\r\n                }\r\n                else {\r\n                    // 推送\r\n                    const res = await pushModal({\r\n                        title: '学习推送',\r\n                        to: userinfo.nick,\r\n                        content: '答题存在异常, 已暂停答题!',\r\n                        type: 'fail',\r\n                    }, pushToken.value);\r\n                    createTip(`学习推送${res ? '成功' : '失败'}!`);\r\n                    // 暂停\r\n                    examPause.value = true;\r\n                    // 提交答案\r\n                    shouldSaveAnswer = true;\r\n                }\r\n                break;\r\n            }\r\n            case '单选题': {\r\n                // 根据提示作答\r\n                if (answers.length) {\r\n                    // 创建提示为1\r\n                    if (answers.length === 1) {\r\n                        const res = handleChoiceBtn(answers);\r\n                        // 成功\r\n                        if (res) {\r\n                            break;\r\n                        }\r\n                    }\r\n                    else {\r\n                        // 可能的分隔符\r\n                        const seperator = [\r\n                            '',\r\n                            ' ',\r\n                            ',',\r\n                            ';',\r\n                            ',',\r\n                            '、',\r\n                            '-',\r\n                            '|',\r\n                            '+',\r\n                            '/',\r\n                        ];\r\n                        // 可能的答案\r\n                        const answersLike = seperator\r\n                            .map((s) => answers.join(s).trim())\r\n                            .filter((answer) => answer.length);\r\n                        // 答案存在\r\n                        if (answersLike.length) {\r\n                            // 可能答案是否正确\r\n                            const res = answersLike.some((answer) => {\r\n                                // 尝试查找点击\r\n                                return handleChoiceBtn([answer]);\r\n                            });\r\n                            if (res) {\r\n                                break;\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                // 创建提示\r\n                createTip('答案异常, 尝试网络题库获取!');\r\n                log('正在获取答案...');\r\n                // 尝试题库获取\r\n                const answersNetwork = await getAnswer(question);\r\n                log(`获取答案${answersNetwork.length ? '成功' : '失败'}!`, {\r\n                    question,\r\n                    answersNetwork,\r\n                });\r\n                // 存在答案\r\n                if (answersNetwork.length) {\r\n                    // 单答案单选项\r\n                    if (answersNetwork.length === 1) {\r\n                        // 尝试查找点击\r\n                        const res = handleChoiceBtn(answersNetwork);\r\n                        if (res) {\r\n                            break;\r\n                        }\r\n                    }\r\n                    else {\r\n                        // 多答案单选项 选项意外拆分\r\n                        // 可能分隔符\r\n                        const seperator = ['', ' '];\r\n                        // 可能答案\r\n                        const answersLike = seperator.map((s) => answers.join(s));\r\n                        // 答案存在\r\n                        if (answersLike.every((answer) => answer.length)) {\r\n                            // 可能答案是否正确\r\n                            const res = answersLike.some((answer) => {\r\n                                // 尝试查找点击\r\n                                return handleChoiceBtn([answer]);\r\n                            });\r\n                            if (res) {\r\n                                break;\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                // 随机作答\r\n                if (type === ExamType.PRACTICE || settings[SettingType.RANDOM_EXAM]) {\r\n                    log('答案不存在, 随机作答!');\r\n                    // 创建提示\r\n                    createTip('答案不存在, 随机作答!');\r\n                    await handleSingleChoiceRand();\r\n                }\r\n                else {\r\n                    // 推送\r\n                    const res = await pushModal({\r\n                        title: '学习推送',\r\n                        to: userinfo.nick,\r\n                        content: '答题存在异常, 已暂停答题!',\r\n                        type: 'fail',\r\n                    }, pushToken.value);\r\n                    createTip(`学习推送${res ? '成功' : '失败'}!`);\r\n                    // 暂停\r\n                    examPause.value = true;\r\n                    // 提交答案\r\n                    shouldSaveAnswer = true;\r\n                }\r\n                break;\r\n            }\r\n        }\r\n        // 暂停\r\n        await examPauseLock();\r\n        // 获取下一个按钮\r\n        nextButton = await getNextButton();\r\n        // 下一个文本\r\n        nextText = nextButton.innerText.replaceAll(' ', '');\r\n        // 需要提交答案\r\n        if (shouldSaveAnswer) {\r\n            // 答案\r\n            const answers = [];\r\n            if (questionType === '填空题') {\r\n                blanks.forEach((blank) => {\r\n                    answers.push(blank.value);\r\n                });\r\n            }\r\n            if (questionType === '单选题' || questionType === '多选题') {\r\n                allBtns.forEach((choice) => {\r\n                    if (choice.classList.contains('chosen')) {\r\n                        // 带字母的选项\r\n                        const answerTemp = choice.innerText;\r\n                        // 从字符串中拿出答案\r\n                        const [, answer] = answerTemp.split('.');\r\n                        if (answer && answer.length) {\r\n                            answers.push(answer);\r\n                        }\r\n                    }\r\n                });\r\n            }\r\n            // 答案\r\n            const answer = answers.join(';');\r\n            // 存在答案\r\n            if (answer.length) {\r\n                log('正在上传答案...');\r\n                // 上传答案\r\n                const res = await saveAnswer(question, answer);\r\n                log(`上传答案${res ? '成功' : '失败'}!`, { question, answer });\r\n            }\r\n            // 重置\r\n            shouldSaveAnswer = false;\r\n        }\r\n        // 确定\r\n        if (nextText === '确定') {\r\n            // 确认\r\n            nextButton.click();\r\n            // 等待一段时间\r\n            await sleep(2000);\r\n            // 暂停\r\n            await examPauseLock();\r\n            // 答案解析\r\n            const answerBox = $$('.answer')[0];\r\n            // 答题错误\r\n            if (answerBox) {\r\n                const answerTemp = answerBox.innerText;\r\n                // 从字符串中拿出答案\r\n                const [, answerText] = answerTemp.split(':');\r\n                if (answerText && answerText.length) {\r\n                    const answer = answerText.replaceAll(' ', ';');\r\n                    log('正在上传答案...');\r\n                    // 上传答案\r\n                    const res = await saveAnswer(question, answer);\r\n                    log(`上传答案${res ? '成功' : '失败'}!`, { question, answer });\r\n                }\r\n            }\r\n        }\r\n        // 获取按钮\r\n        nextButton = await getNextButton();\r\n        // 下一个文本\r\n        nextText = nextButton.innerText.replaceAll(' ', '');\r\n        if (nextText === '下一题' || nextText === '完成' || nextText === '交卷') {\r\n            // 等待一段时间\r\n            await sleep(2500);\r\n            // 下一题\r\n            nextButton.click();\r\n        }\r\n        // 滑动验证\r\n        await handleSlideVerify();\r\n    }\r\n    // 关闭任务窗口\r\n    handleCloseTaskWin();\r\n}\r\n/**\r\n * @description 每日答题\r\n */\r\nasync function doExamPractice() {\r\n    // 暂停\r\n    await studyPauseLock();\r\n    log('正在每日答题...');\r\n    // 创建提示\r\n    createTip('正在每日答题');\r\n    // 链接\r\n    const url = URL_CONFIG.examPractice;\r\n    // 等待任务窗口\r\n    await waitTaskWin(url, '每日答题');\r\n    // 创建提示\r\n    createTip('完成每日答题!');\r\n    // 等待一段时间\r\n    await sleep(1500);\r\n    // 刷新分数数据\r\n    await refreshScoreInfo();\r\n    // 刷新任务数据\r\n    await refreshTaskList();\r\n    // 任务完成状况\r\n    if (taskConfig[TaskType.PRACTICE].active &&\r\n        !taskConfig[TaskType.PRACTICE].status) {\r\n        log('任务未完成, 继续每日答题!');\r\n        // 创建提示\r\n        createTip('任务未完成, 继续每日答题!');\r\n        await doExamPractice();\r\n    }\r\n}\r\n/**\r\n * @description 专项练习\r\n */\r\nasync function doExamPaper() {\r\n    running.value = true;\r\n    log('正在专项练习...');\r\n    // 创建提示\r\n    createTip('正在专项练习');\r\n    // id\r\n    const examPaperId = await findExamPaper();\r\n    if (examPaperId) {\r\n        // 链接\r\n        const url = `${URL_CONFIG.examPaper}?id=${examPaperId}`;\r\n        log(`链接: ${url}`);\r\n        // 等待窗口任务\r\n        await waitTaskWin(url, '专项练习');\r\n        // 创建提示\r\n        createTip('完成专项练习!');\r\n        running.value = false;\r\n        // 同屏任务\r\n        if (settings[SettingType.SAME_TAB]) {\r\n            // 窗口不存在\r\n            frame.exist = false;\r\n        }\r\n        return;\r\n    }\r\n    running.value = false;\r\n    // 创建提示\r\n    createTip('专项练习均已完成!');\r\n}\r\n/**\r\n * @description 初始化总页数属性\r\n */\r\nasync function initExam() {\r\n    // 默认从第一页获取全部页属性\r\n    const data = await getExamPaper(1);\r\n    if (data) {\r\n        // 等待\r\n        await sleep(3000);\r\n        return data.totalPageCount;\r\n    }\r\n}\r\n/**\r\n * @description 查询专项练习列表\r\n */\r\nasync function findExamPaper() {\r\n    // 获取总页数\r\n    const total = await initExam();\r\n    // 当前页数\r\n    let current = 1;\r\n    log(`正在寻找的专项练习...`);\r\n    // 创建提示\r\n    createTip(`正在寻找的专项练习...`);\r\n    while (current <= total && current) {\r\n        // 请求数据\r\n        const data = await getExamPaper(current);\r\n        if (data) {\r\n            // 获取专项练习的列表\r\n            const examPapers = data.list;\r\n            for (const i in examPapers) {\r\n                // 遍历查询有没有没做过的\r\n                if (examPapers[i].status !== 2) {\r\n                    // status: 1 开始答题, 2 已满分/重新答题, 3 继续答题\r\n                    return examPapers[i].id;\r\n                }\r\n            }\r\n            // 增加页码\r\n            current += 1;\r\n            // 等待\r\n            await sleep(3000);\r\n        }\r\n        else {\r\n            break;\r\n        }\r\n    }\r\n}\r\n\r\n","from":"src\\controller\\exam.ts","to":"exam.js"},"src\\controller\\frame.ts":{"timeStamp":"2023-03-04T06:19:57.161Z","data":"/**\r\n * @description 初始化主页面\r\n */\r\nfunction initMainListener() {\r\n    // 监听关闭\r\n    window.addEventListener('message', (msg) => {\r\n        const { data } = msg;\r\n        if (data.id === id.value && data.closed) {\r\n            // 关闭窗口\r\n            closeFrame();\r\n            return;\r\n        }\r\n    });\r\n}\r\n/**\r\n * @description 初始化子页面\r\n */\r\nfunction initChildListener() {\r\n    window.addEventListener('message', (msg) => {\r\n        const { data } = msg;\r\n        if (data.id && !data.closed) {\r\n            // 设置窗口id\r\n            id.value = data.id;\r\n            log(`初始化窗口 ID: ${id.value}`);\r\n            return;\r\n        }\r\n    });\r\n}\r\n/**\r\n * @description 打开窗口\r\n * @param url\r\n * @returns\r\n */\r\nasync function openFrame(url, title) {\r\n    // 设置 URL\r\n    frame.src = url;\r\n    // 等待元素\r\n    await $_('.egg_frame');\r\n    if (frame.ele) {\r\n        // id\r\n        id.value = generateMix(10);\r\n        // 打开\r\n        frame.closed = false;\r\n        // 设置标题\r\n        frame.title = title || '';\r\n        // 等待页面加载\r\n        await waitFrameLoaded(frame.ele);\r\n        // 发送窗口 ID\r\n        frame.ele.contentWindow?.postMessage({ id: id.value, closed: false }, url);\r\n        return true;\r\n    }\r\n    return false;\r\n}\r\n/**\r\n * @description 关闭窗口\r\n */\r\nfunction closeFrame() {\r\n    log(`关闭窗口 ID: ${id.value}`);\r\n    // 窗口显示\r\n    frame.show = false;\r\n    // 关闭\r\n    frame.closed = true;\r\n    // 标题\r\n    frame.title = '';\r\n    // src\r\n    frame.src = '';\r\n}\r\n/**\r\n * @description 关闭 frame\r\n */\r\nfunction handleCloseFrame() {\r\n    window.parent.postMessage({ id: id.value, closed: true }, URL_CONFIG.homeOrigin);\r\n}\r\n/**\r\n * @description 等待窗口任务结束\r\n * @param id\r\n * @returns\r\n */\r\nfunction waitFrameClose() {\r\n    return new Promise((resolve) => {\r\n        const timer = setInterval(() => {\r\n            // 窗口关闭\r\n            if (frame.closed) {\r\n                clearInterval(timer);\r\n                resolve(true);\r\n            }\r\n        }, 100);\r\n    });\r\n}\r\n// 等待窗口加载\r\nfunction waitFrameLoaded(iframe) {\r\n    return new Promise((resolve) => {\r\n        iframe.addEventListener('load', () => {\r\n            resolve(true);\r\n        });\r\n    });\r\n}\r\n/**\r\n * @description 打开新窗口\r\n */\r\nfunction openWin(url) {\r\n    return GM_openInTab(url, {\r\n        active: true,\r\n        insert: true,\r\n        setParent: true,\r\n    });\r\n}\r\n/**\r\n * @description 关闭窗口\r\n */\r\nfunction closeWin() {\r\n    page.value && page.value.close();\r\n}\r\n/**\r\n * @description 关闭子窗口\r\n */\r\nfunction handleCloseWin() {\r\n    try {\r\n        window.opener = window;\r\n        const win = window.open('', '_self');\r\n        win?.close();\r\n        top?.close();\r\n    }\r\n    catch (e) { }\r\n}\r\n/**\r\n * @description 等待窗口关闭\r\n * @param newPage\r\n * @returns\r\n */\r\nfunction waitWinClose(newPage) {\r\n    return new Promise((resolve) => {\r\n        newPage.onclose = () => {\r\n            resolve(undefined);\r\n        };\r\n    });\r\n}\r\n/**\r\n * @description 关闭任务窗口\r\n */\r\nfunction closeTaskWin() {\r\n    // 同屏任务\r\n    if (settings[SettingType.SAME_TAB] && id.value) {\r\n        closeFrame();\r\n        return;\r\n    }\r\n    // 非同屏任务\r\n    closeWin();\r\n}\r\n/**\r\n * @description 关闭任务窗口\r\n */\r\nfunction handleCloseTaskWin() {\r\n    // 同屏任务\r\n    if (settings[SettingType.SAME_TAB] && id.value) {\r\n        handleCloseFrame();\r\n        return;\r\n    }\r\n    // 子窗口\r\n    handleCloseWin();\r\n}\r\n/**\r\n * @description 打开并等待任务结束\r\n */\r\nasync function waitTaskWin(url, title) {\r\n    // 同屏任务\r\n    if (settings[SettingType.SAME_TAB]) {\r\n        // 窗口存在\r\n        frame.exist = true;\r\n        // 显示窗体\r\n        frame.show = !settings[SettingType.SILENT_RUN];\r\n        // 新窗口\r\n        const res = await openFrame(url, title);\r\n        if (res) {\r\n            // 等待窗口关闭\r\n            await waitFrameClose();\r\n        }\r\n        return;\r\n    }\r\n    // 子页面任务\r\n    page.value = openWin(url);\r\n    await waitWinClose(page.value);\r\n}\r\n\r\n","from":"src\\controller\\frame.ts","to":"frame.js"},"src\\controller\\login.ts":{"timeStamp":"2023-07-04T09:31:09.444Z","data":"/**\r\n * @description 二维码刷新定时器\r\n */\r\nlet refreshTimer = -1;\r\n/**\r\n * @description 尝试登录\r\n */\r\nlet tryLoginTimer = -1;\r\n/**\r\n * @description 生成二维码\r\n */\r\nasync function getQRCode() {\r\n    log('正在生成登录二维码...');\r\n    const qrCode = await generateQRCode();\r\n    if (qrCode) {\r\n        log('生成登录二维码成功!');\r\n        // 链接\r\n        const url = `https://login.xuexi.cn/login/qrcommit?showmenu=false&code=${qrCode}&appId=dingoankubyrfkttorhpou`;\r\n        return {\r\n            code: qrCode,\r\n            src: `${API_CONFIG.qrcode}?data=${encodeURIComponent(url)}`,\r\n            url,\r\n        };\r\n    }\r\n    log('生成登录二维码失败!');\r\n}\r\n/**\r\n * @description 验证登录二维码\r\n * @param code\r\n * @returns\r\n */\r\nasync function checkQRCode(code) {\r\n    log('尝试用二维码登录...');\r\n    // 二维码登录\r\n    const res = await loginWithQRCode(code);\r\n    if (res) {\r\n        const { data, code, success } = res;\r\n        // 临时登录验证码\r\n        if (success && data) {\r\n            return data;\r\n        }\r\n        // 二维码失效\r\n        if (code === '11019') {\r\n            return;\r\n        }\r\n    }\r\n    return new Promise((resolve) => {\r\n        // 清除定时\r\n        clearTimeout(tryLoginTimer);\r\n        // 设置定时\r\n        tryLoginTimer = setTimeout(async () => {\r\n            resolve(await checkQRCode(code));\r\n        }, 1000);\r\n    });\r\n}\r\n/**\r\n * @description 尝试二维码登录\r\n */\r\nasync function tryLogin(checkCode) {\r\n    log('正在获取签名...');\r\n    // 获取签名\r\n    const sign = await getSign();\r\n    if (sign) {\r\n        // 生成uuid\r\n        const uuid = crypto.randomUUID();\r\n        const [, code] = checkCode.split('=');\r\n        const state = `${sign}${uuid}`;\r\n        // 安全检查\r\n        const res = await secureCheck({ code, state });\r\n        return res;\r\n    }\r\n}\r\n/**\r\n * @description 刷新登录二维码\r\n */\r\nasync function handleLogin() {\r\n    // 清除刷新\r\n    clearInterval(refreshTimer);\r\n    // 每隔一段时间刷新\r\n    refreshTimer = setInterval(() => {\r\n        // 刷新二维码\r\n        handleLogin();\r\n    }, autoRefreshQRCodeInterval);\r\n    // 是否超出次数\r\n    if (refreshCount.value >= maxRefreshCount) {\r\n        createTip('超过最大重试次数, 登录失败!');\r\n        // 重置刷新数\r\n        refreshCount.value = 0;\r\n        // 隐藏二维码\r\n        loginQRCodeShow.value = false;\r\n        // 远程推送\r\n        if (settings[SettingType.REMOTE_PUSH]) {\r\n            // 推送\r\n            const res = await pushModal({\r\n                title: '登录推送',\r\n                content: '超过最大重试次数, 登录失败!',\r\n                type: 'fail',\r\n            }, pushToken.value);\r\n            createTip(`登录推送${res ? '成功' : '失败'}!`);\r\n        }\r\n        return;\r\n    }\r\n    // 配置\r\n    const imgWrap = $$('.egg_login_img_wrap')[0];\r\n    // 图片\r\n    const img = $$('.egg_login_img', imgWrap)[0];\r\n    if (imgWrap && img) {\r\n        // 刷新二维码\r\n        log('刷新登录二维码!');\r\n        // 刷新次数累加\r\n        refreshCount.value++;\r\n        // 获取二维码\r\n        const qrCode = await getQRCode();\r\n        if (qrCode) {\r\n            // 获取连接\r\n            const { src, code, url } = qrCode;\r\n            // src\r\n            img.src = src;\r\n            // 开始登录\r\n            loginQRCodeShow.value = true;\r\n            // 远程推送\r\n            if (settings[SettingType.REMOTE_PUSH]) {\r\n                // img html\r\n                const imgWrap = getImgHTML(src);\r\n                // 跳转链接\r\n                const aWrap = `\r\n   <div>\r\n      或在浏览器\r\n      <a\r\n        href=\"dtxuexi://appclient/page/study_feeds?url=${encodeURIComponent(url)}\"\r\n        style=\"text-decoration: none\"\r\n        >${getHighlightHTML('打开学习强国APP')}</a\r\n      >\r\n    </div>\r\n  `;\r\n                // 推送\r\n                const res = await pushModal({\r\n                    title: '登录推送',\r\n                    content: ['扫一扫, 登录学习强国!', aWrap, imgWrap],\r\n                    type: 'info',\r\n                }, pushToken.value);\r\n                createTip(`登录推送${res ? '成功' : '失败'}!`);\r\n            }\r\n            // 获取验证码\r\n            const checkCode = await checkQRCode(code);\r\n            // 验证成功\r\n            if (checkCode) {\r\n                // 尝试登录\r\n                const loginRes = await tryLogin(checkCode);\r\n                if (loginRes) {\r\n                    // 清除刷新\r\n                    clearInterval(refreshTimer);\r\n                    // 二维码显示\r\n                    loginQRCodeShow.value = false;\r\n                    // 登录成功\r\n                    log('登录成功!');\r\n                    // 创建提示\r\n                    createTip('登录成功!');\r\n                    // 登录成功\r\n                    login.value = true;\r\n                    // 刷新用户信息\r\n                    await refreshUserInfo();\r\n                    // 刷新分数信息\r\n                    await refreshScoreInfo();\r\n                    // 刷新任务信息\r\n                    await refreshTaskList();\r\n                    // 远程推送\r\n                    if (settings[SettingType.REMOTE_PUSH]) {\r\n                        const res = await pushModal({\r\n                            title: '登录推送',\r\n                            to: userinfo.nick,\r\n                            content: [\r\n                                '学习强国, 登录成功!',\r\n                                `当天积分:  ${getHighlightHTML(todayScore.value)} 分`,\r\n                                `总积分: ${getHighlightHTML(totalScore.value)} 分`,\r\n                                ...taskConfig.map((task) => getProgressHTML(task.title, task.currentScore, task.dayMaxScore)),\r\n                            ],\r\n                            type: 'success',\r\n                        }, pushToken.value);\r\n                        createTip(`登录推送${res ? '成功' : '失败'}!`);\r\n                    }\r\n                }\r\n                return;\r\n            }\r\n            // 二维码失效\r\n            log('登录二维码失效!');\r\n            // 二维码失效刷新\r\n            handleLogin();\r\n        }\r\n    }\r\n}\r\n/**\r\n * @description 退出登录\r\n */\r\nfunction handleLogout() {\r\n    // 删除token\r\n    delCookie('token', '.xuexi.cn');\r\n    // 关闭窗口\r\n    closeFrame();\r\n    frame.exist = false;\r\n    // 退出登录\r\n    login.value = false;\r\n    // 清除用户信息\r\n    userinfo.nick = '';\r\n    userinfo.avatar = '';\r\n    // 总分\r\n    totalScore.value = 0;\r\n    // 当天分数\r\n    todayScore.value = 0;\r\n    // 任务进度重置\r\n    taskConfig.forEach((task) => {\r\n        task.currentScore = 0;\r\n    });\r\n    taskStatus.value = TaskStatusType.LOADING;\r\n    // 退出登录\r\n    log('退出登录');\r\n}\r\n\r\n","from":"src\\controller\\login.ts","to":"login.js"},"src\\controller\\readAndWatch.ts":{"timeStamp":"2023-07-13T13:15:08.730Z","data":"/**\r\n * @description 新闻\r\n */\r\nlet news = [];\r\n/**\r\n * @description 视频\r\n */\r\nlet videos = [];\r\n/**\r\n * @description 处理文章\r\n */\r\nasync function handleNews() {\r\n    // section\r\n    const sections = await $_('section', undefined, 5000);\r\n    const section = sections[0];\r\n    if (!(section && section.innerText.includes('系统正在维护中'))) {\r\n        // 文章选读\r\n        reading(0);\r\n        return;\r\n    }\r\n    log('未找到文章!');\r\n    // 提示\r\n    createTip('未找到文章!');\r\n    // 关闭页面\r\n    handleCloseTaskWin();\r\n}\r\n/**\r\n * @description 处理视频\r\n */\r\nasync function handleVideo() {\r\n    // videos\r\n    const videos = await $_('video', undefined, 10000);\r\n    // 视频\r\n    const video = videos[0];\r\n    // 播放按键\r\n    const playBtn = $$('.prism-play-btn')[0];\r\n    if (video && playBtn) {\r\n        log('正在尝试播放视频...');\r\n        // 播放超时\r\n        const timeout = setTimeout(() => {\r\n            log('视频播放超时!');\r\n            // 提示\r\n            createTip('视频播放超时!');\r\n            // 关闭页面\r\n            handleCloseTaskWin();\r\n        }, 20000);\r\n        // 设置是否静音\r\n        watchEffect(() => (video.muted = settings[SettingType.VIDEO_MUTED]));\r\n        // 能播放\r\n        video.addEventListener('canplay', () => {\r\n            const timer = setInterval(() => {\r\n                // 尝试点击播放按钮播放\r\n                playBtn.click();\r\n                // 播放未成功\r\n                if (video.paused) {\r\n                    // 尝试使用js的方式播放\r\n                    video.play();\r\n                }\r\n            }, 1000);\r\n            video.addEventListener('playing', () => {\r\n                // 清除超时定时器\r\n                clearTimeout(timeout);\r\n                // 清除定时器\r\n                clearInterval(timer);\r\n                log('播放视频成功!');\r\n                // 视听学习\r\n                reading(1);\r\n                return;\r\n            }, { once: true });\r\n        }, { once: true });\r\n        return;\r\n    }\r\n    log('未找到视频!');\r\n    // 关闭页面\r\n    handleCloseTaskWin();\r\n}\r\n/**\r\n * @description 读新闻或者看视频\r\n * @param type :0为新闻,1为视频\r\n */\r\nasync function reading(type) {\r\n    let time = 30;\r\n    // 文章选读\r\n    if (type === 0) {\r\n        // 章节\r\n        const sections = $$('section');\r\n        // 最大字数\r\n        const maxTextCount = Math.max(...sections.map((s) => s.innerText.length), 200);\r\n        // 预计时间\r\n        const predictTime = ~~((60 * maxTextCount) / 1000);\r\n        // min(predictTime,  maxWatch.value) 秒后关闭页面\r\n        time = Math.min(predictTime, maxRead.value);\r\n    }\r\n    // 视听学习\r\n    if (type === 1) {\r\n        // 视频\r\n        const video = $$('video')[0];\r\n        // 预计时间\r\n        const predictTime = ~~video.duration;\r\n        // min(predictTime,  maxWatch.value) 秒后关闭页面\r\n        time = Math.min(predictTime, maxWatch.value);\r\n    }\r\n    // 随机\r\n    time = time - ~~(Math.random() * 10) + 5;\r\n    // 第一次滚动时间\r\n    const firstTime = time - (~~(Math.random() * 4) + 4);\r\n    // 第二次滚动时间\r\n    const secendTime = ~~(Math.random() * 4) + 8;\r\n    // 窗口\r\n    const window = unsafeWindow;\r\n    // 创建提示\r\n    const tip = createTip('距离关闭页面还剩', time, true, async (time) => {\r\n        // 暂停锁\r\n        await studyPauseLock((flag) => {\r\n            if (type === 1) {\r\n                // 视频\r\n                const video = $$('video')[0];\r\n                // 排除反复设置\r\n                if (video.paused === !flag) {\r\n                    return;\r\n                }\r\n                // 设置播放状态\r\n                video[flag ? 'play' : 'pause']();\r\n            }\r\n        });\r\n        // 第一次滚动\r\n        if (time === firstTime) {\r\n            // 滚动\r\n            window.scrollTo(0, 400);\r\n            // 模拟滚动\r\n            const scroll = new Event('scroll', {\r\n                bubbles: true,\r\n            });\r\n            document.dispatchEvent(scroll);\r\n            // 模拟滑动\r\n            const mousemove = new MouseEvent('mousemove', {\r\n                bubbles: true,\r\n            });\r\n            document.dispatchEvent(mousemove);\r\n            // 模拟点击\r\n            const click = new Event('click', {\r\n                bubbles: true,\r\n            });\r\n            document.dispatchEvent(click);\r\n        }\r\n        // 第二次滚动\r\n        if (time === secendTime) {\r\n            // 滚动长度\r\n            const scrollLength = document.body.scrollHeight / 2;\r\n            // 滚动\r\n            window.scrollTo(0, scrollLength);\r\n            // 模拟滚动\r\n            const scroll = new Event('scroll', {\r\n                bubbles: true,\r\n            });\r\n            document.dispatchEvent(scroll);\r\n            // 模拟滑动\r\n            const mousemove = new MouseEvent('mousemove', {\r\n                bubbles: true,\r\n            });\r\n            document.dispatchEvent(mousemove);\r\n            // 模拟点击\r\n            const click = new Event('click', {\r\n                bubbles: true,\r\n            });\r\n            document.dispatchEvent(click);\r\n        }\r\n    });\r\n    // 倒计时结束\r\n    await tip.waitCountDown();\r\n    // 关闭任务窗口\r\n    handleCloseTaskWin();\r\n}\r\n/**\r\n * @description 获取新闻列表\r\n */\r\nasync function getNews() {\r\n    // 需要学习的新闻数量\r\n    const need = taskConfig[TaskType.READ].need < maxNewsNum\r\n        ? taskConfig[TaskType.READ].need\r\n        : maxNewsNum;\r\n    log(`剩余 ${need} 个新闻`);\r\n    // 获取新闻\r\n    const data = await getNewsList();\r\n    if (data && data.length) {\r\n        // 索引\r\n        let i = 0;\r\n        // 最新新闻\r\n        const latestItems = data.slice(0, 100);\r\n        // 当前年份\r\n        const currentYear = new Date().getFullYear().toString();\r\n        // 查找今年新闻\r\n        while (i < need) {\r\n            const randomIndex = ~~(Math.random() * latestItems.length);\r\n            // 新闻\r\n            const item = latestItems[randomIndex];\r\n            // 是否存在\r\n            if (item.publishTime.startsWith(currentYear) && item.type === 'tuwen') {\r\n                news[i] = item;\r\n                i++;\r\n            }\r\n        }\r\n    }\r\n    else {\r\n        news = [];\r\n    }\r\n}\r\n/**\r\n * @description 获取视频列表\r\n */\r\nasync function getVideos() {\r\n    // 需要学习的视频数量\r\n    const need = taskConfig[TaskType.WATCH].need < maxVideoNum\r\n        ? taskConfig[TaskType.WATCH].need\r\n        : maxVideoNum;\r\n    log(`剩余 ${need} 个视频`);\r\n    // 获取视频\r\n    const data = await getVideoList();\r\n    if (data && data.length) {\r\n        // 索引\r\n        let i = 0;\r\n        // 最新视频\r\n        const latestItems = data.slice(0, 100);\r\n        // 当前年份\r\n        const currentYear = new Date().getFullYear().toString();\r\n        // 查找今年视频\r\n        while (i < need) {\r\n            const randomIndex = ~~(Math.random() * latestItems.length);\r\n            // 新闻\r\n            const item = latestItems[randomIndex];\r\n            // 是否存在\r\n            if (item.publishTime.startsWith(currentYear) &&\r\n                (item.type === 'shipin' || item.type === 'juji')) {\r\n                videos[i] = item;\r\n                i++;\r\n            }\r\n        }\r\n    }\r\n    else {\r\n        videos = [];\r\n    }\r\n}\r\n/**\r\n * @description 阅读文章\r\n */\r\nasync function readNews() {\r\n    // 获取文章\r\n    await getNews();\r\n    // 观看文章\r\n    for (const i in news) {\r\n        // 任务关闭跳出循环\r\n        if (!taskConfig[TaskType.READ].active) {\r\n            return;\r\n        }\r\n        // 暂停\r\n        await studyPauseLock();\r\n        log(`正在阅读第 ${Number(i) + 1} 个新闻...`);\r\n        // 创建提示\r\n        createTip(`正在阅读第 ${Number(i) + 1} 个新闻`);\r\n        // 链接\r\n        const { url } = news[i];\r\n        // 链接\r\n        GM_setValue('readingUrl', url);\r\n        // 等待任务窗口\r\n        await waitTaskWin(url, '文章选读');\r\n        // 清空链接\r\n        GM_setValue('readingUrl', null);\r\n        // 创建提示\r\n        createTip(`完成阅读第 ${Number(i) + 1} 个新闻!`);\r\n        // 等待一段时间\r\n        await sleep(1500);\r\n        // 刷新分数数据\r\n        await refreshScoreInfo();\r\n        // 刷新任务数据\r\n        await refreshTaskList();\r\n        // 任务完成跳出循环\r\n        if (taskConfig[TaskType.READ].active && taskConfig[TaskType.READ].status) {\r\n            break;\r\n        }\r\n    }\r\n    // 任务关闭跳出循环\r\n    if (!taskConfig[TaskType.READ].active) {\r\n        return;\r\n    }\r\n    // 任务完成状况\r\n    if (taskConfig[TaskType.READ].active && !taskConfig[TaskType.READ].status) {\r\n        log('任务未完成, 继续阅读新闻!');\r\n        // 创建提示\r\n        createTip('任务未完成, 继续阅读新闻!');\r\n        await readNews();\r\n    }\r\n}\r\n/**\r\n * @description 观看视频\r\n */\r\nasync function watchVideo() {\r\n    // 获取视频\r\n    await getVideos();\r\n    // 观看视频\r\n    for (const i in videos) {\r\n        // 任务关闭跳出循环\r\n        if (!taskConfig[TaskType.WATCH].active) {\r\n            return;\r\n        }\r\n        // 暂停\r\n        await studyPauseLock();\r\n        log(`正在观看第 ${Number(i) + 1} 个视频...`);\r\n        // 创建提示\r\n        createTip(`正在观看第 ${Number(i) + 1} 个视频`);\r\n        // 链接\r\n        const { url } = videos[i];\r\n        // 链接\r\n        GM_setValue('watchingUrl', url);\r\n        // 等待任务窗口\r\n        await waitTaskWin(url, '视听学习');\r\n        // 清空链接\r\n        GM_setValue('watchingUrl', null);\r\n        // 创建提示\r\n        createTip(`完成观看第 ${Number(i) + 1} 个视频!`);\r\n        // 等待一段时间\r\n        await sleep(1500);\r\n        // 刷新分数数据\r\n        await refreshScoreInfo();\r\n        // 刷新任务数据\r\n        await refreshTaskList();\r\n        // 任务完成跳出循环\r\n        if (taskConfig[TaskType.WATCH].active &&\r\n            taskConfig[TaskType.WATCH].status) {\r\n            break;\r\n        }\r\n    }\r\n    // 任务关闭跳出循环\r\n    if (!taskConfig[TaskType.WATCH].active) {\r\n        return;\r\n    }\r\n    // 任务完成状况\r\n    if (taskConfig[TaskType.WATCH].active && !taskConfig[TaskType.WATCH].status) {\r\n        log('任务未完成, 继续观看视频!');\r\n        // 创建提示\r\n        createTip('任务未完成, 继续观看看视频!');\r\n        await watchVideo();\r\n    }\r\n}\r\n\r\n","from":"src\\controller\\readAndWatch.ts","to":"readAndWatch.js"},"src\\controller\\schedule.ts":{"timeStamp":"2023-02-25T03:17:29.804Z","data":"/**\r\n * @description 定时刷新定时器\r\n */\r\nlet scheduleTimer = -1;\r\n/**\r\n * @description 刷新定时任务\r\n */\r\nasync function refreshScheduleTask() {\r\n    // 清除定时刷新\r\n    clearInterval(scheduleTimer);\r\n    // 未登录\r\n    if (!login.value) {\r\n        // 剩余定时任务\r\n        const restList = scheduleList.filter((s) => !isLate(s));\r\n        // 存在剩余任务\r\n        if (restList.length) {\r\n            const rest = restList[0];\r\n            log(`已设置 ${rest.time} 的定时任务!`);\r\n            // 提示\r\n            createTip(`已设置 ${rest.time} 的定时任务!`);\r\n            // 时间\r\n            let time = 0;\r\n            // 刷新间隔\r\n            const interval = 10;\r\n            scheduleTimer = setInterval(() => {\r\n                if (!(time++ % interval)) {\r\n                    log('定时刷新正在运行...');\r\n                }\r\n                // 到达定时\r\n                if (isNow(rest)) {\r\n                    clearInterval(scheduleTimer);\r\n                    log(`执行 ${rest.time} 的定时任务!`);\r\n                    // 提示\r\n                    createTip(`执行 ${rest.time} 的定时任务!`);\r\n                    // 登录\r\n                    handleLogin();\r\n                }\r\n            }, 1000);\r\n        }\r\n    }\r\n}\r\n/**\r\n * @description 清除定时\r\n */\r\nfunction clearScheduleTask() {\r\n    clearInterval(scheduleTimer);\r\n}\r\n\r\n","from":"src\\controller\\schedule.ts","to":"schedule.js"},"src\\controller\\tip.ts":{"timeStamp":"2023-06-03T02:54:53.972Z","data":"/**\r\n * @description 创建学习提示\r\n */\r\nfunction createTip(text, delay = 2, countShow = false, callback) {\r\n    const tipWrap = $$('.egg_tip_wrap')[0];\r\n    // 提前去除\r\n    const tips = $$('.egg_tip');\r\n    if (tips.length) {\r\n        tips.forEach((t) => t.delay());\r\n    }\r\n    // 延迟\r\n    const delayCount = ref(delay);\r\n    // 文字\r\n    const textContent = ref(text);\r\n    //显示\r\n    const show = ref(false);\r\n    // 延迟显示\r\n    const delayShow = ref(false);\r\n    // 销毁\r\n    let destroyed = false;\r\n    // 倒计时结束\r\n    let done = false;\r\n    // 提示\r\n    const tip = Tip({\r\n        text: textContent,\r\n        count: delayCount,\r\n        show,\r\n        delayShow,\r\n        countShow: ref(countShow),\r\n        callback: async (count) => {\r\n            callback && (await callback(count));\r\n            // 恢复显示\r\n            if (delayShow.value && count === delay) {\r\n                delayShow.value = false;\r\n            }\r\n            // 倒计时结束\r\n            if (count <= 0) {\r\n                done = true;\r\n                operate.destroy();\r\n            }\r\n        },\r\n    });\r\n    // 操作\r\n    const operate = {\r\n        destroy() {\r\n            if (!destroyed) {\r\n                // 隐藏\r\n                operate.hide();\r\n                // 销毁\r\n                destroyed = true;\r\n                return new Promise((resolve) => {\r\n                    setTimeout(() => {\r\n                        tip.ele.remove();\r\n                        resolve(undefined);\r\n                    }, 300);\r\n                });\r\n            }\r\n        },\r\n        hide() {\r\n            if (!destroyed) {\r\n                show.value = false;\r\n            }\r\n        },\r\n        show() {\r\n            if (!destroyed) {\r\n                return new Promise((resolve) => {\r\n                    setTimeout(() => {\r\n                        show.value = true;\r\n                        resolve(undefined);\r\n                    }, 300);\r\n                });\r\n            }\r\n        },\r\n        setText(text) {\r\n            if (!destroyed) {\r\n                textContent.value = text;\r\n            }\r\n        },\r\n        waitCountDown() {\r\n            return new Promise((resolve) => {\r\n                // 计时器\r\n                const timer = setInterval(() => {\r\n                    // 结束\r\n                    if (done) {\r\n                        clearInterval(timer);\r\n                        resolve(true);\r\n                    }\r\n                }, 100);\r\n            });\r\n        },\r\n        delay() {\r\n            if (!destroyed) {\r\n                delayShow.value = true;\r\n                delayCount.value += 2;\r\n            }\r\n        },\r\n    };\r\n    Object.assign(tip.ele, operate);\r\n    // 插入节点\r\n    mountElement(tip, tipWrap);\r\n    // 显示\r\n    operate.show();\r\n    return operate;\r\n}\r\n\r\n","from":"src\\controller\\tip.ts","to":"tip.js"},"src\\controller\\user.ts":{"timeStamp":"2023-07-04T09:34:59.201Z","data":"/**\r\n * @description 刷新用户信息\r\n */\r\nasync function refreshUserInfo() {\r\n    // 未登录\r\n    if (!login.value) {\r\n        throw new Error('用户未登录!');\r\n    }\r\n    // 已存在信息\r\n    if (userinfo.nick) {\r\n        return true;\r\n    }\r\n    log('加载用户信息...');\r\n    // 获取用户信息\r\n    const res = await getUserInfo();\r\n    if (res) {\r\n        const { avatarMediaUrl = '', nick: nickRes } = res;\r\n        if (nickRes) {\r\n            // 设置昵称\r\n            userinfo.nick = nickRes;\r\n            // 设置头像\r\n            userinfo.avatar = avatarMediaUrl;\r\n            return true;\r\n        }\r\n    }\r\n    log('加载用户信息失败!');\r\n    return false;\r\n}\r\n/**\r\n * @description 刷新分数信息\r\n */\r\nasync function refreshScoreInfo() {\r\n    // 未登录\r\n    if (!login.value) {\r\n        throw new Error('用户未登录!');\r\n    }\r\n    log('加载分数信息...');\r\n    // 获取总分\r\n    const totalScoreRes = await getTotalScore();\r\n    // 获取当天总分\r\n    const todayScoreRes = await getTodayScore();\r\n    // 整数值\r\n    if (Number.isInteger(totalScoreRes) && Number.isInteger(todayScoreRes)) {\r\n        // 设置分数\r\n        totalScore.value = totalScoreRes;\r\n        todayScore.value = todayScoreRes;\r\n        return true;\r\n    }\r\n    log('加载分数信息失败!');\r\n    return false;\r\n}\r\n/**\r\n * @description 刷新任务列表\r\n */\r\nasync function refreshTaskList() {\r\n    // 未登录\r\n    if (!login.value) {\r\n        throw new Error('用户未登录!');\r\n    }\r\n    log('加载任务进度...');\r\n    // 原始任务进度\r\n    const taskProgress = await getTaskList();\r\n    if (taskProgress) {\r\n        // 登录\r\n        taskConfig[TaskType.LOGIN].currentScore = taskProgress[2].currentScore;\r\n        taskConfig[TaskType.LOGIN].dayMaxScore = taskProgress[2].dayMaxScore;\r\n        taskConfig[TaskType.LOGIN].need =\r\n            taskProgress[2].dayMaxScore - taskProgress[2].currentScore;\r\n        // 文章选读\r\n        taskConfig[TaskType.READ].currentScore = taskProgress[0].currentScore;\r\n        taskConfig[TaskType.READ].dayMaxScore = taskProgress[0].dayMaxScore;\r\n        taskConfig[TaskType.READ].need =\r\n            taskProgress[0].dayMaxScore - taskProgress[0].currentScore;\r\n        // 视听学习\r\n        taskConfig[TaskType.WATCH].currentScore = taskProgress[1].currentScore;\r\n        taskConfig[TaskType.WATCH].dayMaxScore = taskProgress[1].dayMaxScore;\r\n        taskConfig[TaskType.WATCH].need =\r\n            taskProgress[1].dayMaxScore - taskProgress[1].currentScore;\r\n        // 每日答题\r\n        taskConfig[TaskType.PRACTICE].currentScore = taskProgress[3].currentScore;\r\n        taskConfig[TaskType.PRACTICE].dayMaxScore = taskProgress[3].dayMaxScore;\r\n        taskConfig[TaskType.PRACTICE].need = taskProgress[3].dayMaxScore;\r\n        // 更新数据\r\n        for (const i in taskConfig) {\r\n            const { currentScore, dayMaxScore } = taskConfig[i];\r\n            // 进度\r\n            const rate = Number(((100 * currentScore) / dayMaxScore).toFixed(1));\r\n            // 分数\r\n            taskConfig[i].score = currentScore;\r\n            // 完成状态\r\n            taskConfig[i].status = rate === 100;\r\n        }\r\n        return;\r\n    }\r\n    // 重试\r\n    await sleep(2000);\r\n    refreshTaskList();\r\n    return;\r\n}\r\n\r\n","from":"src\\controller\\user.ts","to":"user.js"},"src\\component\\Tip.ts":{"timeStamp":"2023-03-08T03:09:22.868Z","data":"function Tip({ text, count, show, delayShow, countShow, callback, }) {\r\n    return createElementNode('div', undefined, {\r\n        class: watchRef([show, delayShow], () => `egg_tip${show.value ? (delayShow.value ? ' active delay' : ' active') : ''}`),\r\n    }, [\r\n        createElementNode('span', undefined, {\r\n            class: 'egg_text',\r\n        }, createTextNode(text)),\r\n        watchEffectRef(() => countShow.value\r\n            ? createElementNode('span', undefined, {\r\n                class: 'egg_countdown',\r\n            }, createTextNode(watchEffectRef(() => `${count.value}s`)))\r\n            : undefined),\r\n    ], {\r\n        onMounted() {\r\n            // 倒计时\r\n            const countDown = async () => {\r\n                // 倒计时回调\r\n                await callback(count.value);\r\n                // 倒计时结束\r\n                if (!count.value) {\r\n                    show.value = false;\r\n                    return;\r\n                }\r\n                count.value--;\r\n                setTimeout(countDown, 1000);\r\n            };\r\n            countDown();\r\n        },\r\n    });\r\n}\r\n\r\n","from":"src\\component\\Tip.ts","to":"Tip.js"},"src\\component\\Hr.ts":{"timeStamp":"2023-02-17T10:07:48.523Z","data":"/**\r\n * @description 分隔符\r\n * @returns\r\n */\r\nfunction Hr({ text }) {\r\n    return createElementNode('div', undefined, {\r\n        class: 'egg_hr_wrap',\r\n    }, [\r\n        createElementNode('div', undefined, { class: 'egg_hr' }),\r\n        createElementNode('div', undefined, { class: 'egg_hr_title' }, createTextNode(text)),\r\n        createElementNode('div', undefined, { class: 'egg_hr' }),\r\n    ]);\r\n}\r\n\r\n","from":"src\\component\\Hr.ts","to":"Hr.js"},"src\\component\\Select.ts":{"timeStamp":"2023-03-08T14:04:23.134Z","data":"function Select({ data, maxlength, placeholder = '', onchange, onblur, value, keep, }) {\r\n    const selectData = reactive(data.map((v) => ({ selected: false, active: false, ele: undefined, ...v })));\r\n    const focus = ref(false);\r\n    const input = shallowRef(undefined);\r\n    const list = shallowRef(undefined);\r\n    const valueRef = ref('');\r\n    value &&\r\n        watch(value, () => {\r\n            const item = selectData.find((v) => v.value === value.value);\r\n            valueRef.value = item ? item.label : '';\r\n            if (!item) {\r\n                selectData.forEach((v) => (v.selected = false));\r\n                list.value && (list.value.scrollTop = 0);\r\n            }\r\n        }, true);\r\n    return createElementNode('div', undefined, {\r\n        class: 'egg_select',\r\n    }, [\r\n        createElementNode('input', { value: valueRef }, {\r\n            class: 'egg_select_input',\r\n            type: 'text',\r\n            placeholder,\r\n            maxlength,\r\n            ref: input,\r\n            onfocus() {\r\n                if (list.value && input.value) {\r\n                    focus.value = true;\r\n                    if (input.value.value && valueRef.value) {\r\n                        const index = selectData.findIndex((v) => v.label === valueRef.value);\r\n                        if (index + 1) {\r\n                            list.value.scrollTop = selectData[index].ele?.offsetTop || 0;\r\n                            selectData.forEach((v, i) => (v.selected = i === index));\r\n                        }\r\n                        return;\r\n                    }\r\n                }\r\n            },\r\n            oninput() {\r\n                if (list.value && input.value) {\r\n                    const { value } = input.value;\r\n                    // 文本存在\r\n                    if (value) {\r\n                        const index = selectData.findIndex((v) => v.label.includes(value));\r\n                        // 存在匹配\r\n                        if (index + 1) {\r\n                            list.value.scrollTop = selectData[index].ele?.offsetTop || 0;\r\n                            selectData.forEach((v, i) => {\r\n                                v.active = i === index;\r\n                                v.active &&\r\n                                    setTimeout(() => {\r\n                                        v.active = false;\r\n                                    }, 300);\r\n                            });\r\n                        }\r\n                        return;\r\n                    }\r\n                    // 清除\r\n                    selectData.forEach((v) => (v.active = v.selected = false));\r\n                    list.value.scrollTop = 0;\r\n                }\r\n            },\r\n            onblur() {\r\n                if (list.value && input.value) {\r\n                    const item = selectData.find((v) => v.selected);\r\n                    // 关闭选项\r\n                    if (item || !input.value.value) {\r\n                        setTimeout(() => {\r\n                            focus.value = false;\r\n                        }, 100);\r\n                    }\r\n                    // 恢复文本\r\n                    if (item && input.value.value !== item.label) {\r\n                        input.value.value = item.label;\r\n                    }\r\n                    // 保留文本\r\n                    if (!item && keep) {\r\n                        input.value.value = valueRef.value;\r\n                    }\r\n                    onblur &&\r\n                        onblur(item ? { label: item.label, value: item.value } : undefined);\r\n                }\r\n            },\r\n        }),\r\n        createElementNode('div', undefined, {\r\n            class: watchEffectRef(() => `egg_select_list${focus.value ? '' : ' hide'}`),\r\n            ref: list,\r\n        }, selectData.map((v, index) => createElementNode('div', undefined, {\r\n            class: watchRef(() => [v.selected, v.active], () => `egg_select_item${v.selected ? ' selected' : v.active ? ' active' : ''}`),\r\n            ref: (e) => (v.ele = e),\r\n            onclick: debounce(() => {\r\n                if (valueRef.value !== v.label) {\r\n                    onchange && onchange({ label: v.label, value: v.value });\r\n                    selectData.forEach((v, i) => {\r\n                        v.selected = i === index;\r\n                        v.selected && (valueRef.value = v.label);\r\n                    });\r\n                }\r\n                focus.value = false;\r\n            }, 300),\r\n        }, createTextNode(v.label)))),\r\n    ]);\r\n}\r\n\r\n","from":"src\\component\\Select.ts","to":"Select.js"},"src\\component\\ExamBtn.ts":{"timeStamp":"2023-07-12T06:10:48.404Z","data":"/**\r\n * @description 答题按钮\r\n */\r\nfunction ExamBtn() {\r\n    // 设置初始状态\r\n    watchEffect(() => (examPause.value = !settings[SettingType.AUTO_ANSWER]));\r\n    return createElementNode('button', undefined, {\r\n        class: watchEffectRef(() => `egg_exam_btn${examPause.value ? ' manual' : ''}`),\r\n        type: 'button',\r\n        onclick(e) {\r\n            e.stopPropagation();\r\n            examPause.value = !examPause.value;\r\n        },\r\n        onmousedown(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmousemove(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseup(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseenter(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseleave(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseover(e) {\r\n            e.stopPropagation();\r\n        },\r\n        ontouchstart(e) {\r\n            e.stopPropagation();\r\n        },\r\n        ontouchmove(e) {\r\n            e.stopPropagation();\r\n        },\r\n        ontouchend(e) {\r\n            e.stopPropagation();\r\n        },\r\n        oninput(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onchange(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onblur(e) {\r\n            e.stopPropagation();\r\n        },\r\n    }, createTextNode(watchEffectRef(() => `${examPause.value ? '开启自动答题' : '关闭自动答题'}`)));\r\n}\r\n\r\n","from":"src\\component\\ExamBtn.ts","to":"ExamBtn.js"},"src\\component\\Frame.ts":{"timeStamp":"2023-07-13T13:32:54.161Z","data":"/**\r\n * @description 任务窗口\r\n * @returns\r\n */\r\nfunction Frame() {\r\n    // 最大化\r\n    const max = ref(false);\r\n    // 容器\r\n    return createElementNode('div', undefined, {\r\n        class: watchEffectRef(() => `egg_frame_wrap${frame.show ? '' : ' hide'}`),\r\n        onclick(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmousedown(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmousemove(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseup(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseenter(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseleave(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onmouseover(e) {\r\n            e.stopPropagation();\r\n        },\r\n        ontouchstart(e) {\r\n            e.stopPropagation();\r\n        },\r\n        ontouchmove(e) {\r\n            e.stopPropagation();\r\n        },\r\n        ontouchend(e) {\r\n            e.stopPropagation();\r\n        },\r\n        oninput(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onchange(e) {\r\n            e.stopPropagation();\r\n        },\r\n        onblur(e) {\r\n            e.stopPropagation();\r\n        },\r\n    }, watchRef(() => [login.value, settings[SettingType.SAME_TAB]], () => {\r\n        // 同屏任务\r\n        if (login.value && settings[SettingType.SAME_TAB]) {\r\n            return [\r\n                // 遮罩\r\n                createElementNode('div', undefined, { class: 'egg_frame_mask' }),\r\n                // 窗口内容\r\n                createElementNode('div', undefined, {\r\n                    class: watchEffectRef(() => `egg_frame_content_wrap ${max.value ? ' max' : ''}`),\r\n                }, [\r\n                    // 窗口控制\r\n                    createElementNode('div', undefined, { class: 'egg_frame_controls_wrap' }, [\r\n                        // 标题\r\n                        createElementNode('div', undefined, {\r\n                            class: 'egg_frame_title',\r\n                        }),\r\n                        createElementNode('div', undefined, {\r\n                            class: 'egg_frame_controls',\r\n                        }, [\r\n                            // 隐藏\r\n                            createElementNode('button', undefined, {\r\n                                class: 'egg_frame_btn',\r\n                                type: 'button',\r\n                                title: '隐藏',\r\n                                onclick: debounce(() => {\r\n                                    // 隐藏窗口\r\n                                    frame.show = false;\r\n                                }, 300),\r\n                            }, createNSElementNode('svg', undefined, {\r\n                                viewBox: '0 0 1024 1024',\r\n                                class: 'egg_icon',\r\n                            }, createNSElementNode('path', undefined, {\r\n                                d: 'M863.7 552.5H160.3c-10.6 0-19.2-8.6-19.2-19.2v-41.7c0-10.6 8.6-19.2 19.2-19.2h703.3c10.6 0 19.2 8.6 19.2 19.2v41.7c0 10.6-8.5 19.2-19.1 19.2z',\r\n                            }))),\r\n                            // 改变大小\r\n                            createElementNode('button', undefined, {\r\n                                class: 'egg_frame_btn',\r\n                                type: 'button',\r\n                                title: '缩放',\r\n                                onclick: debounce(() => {\r\n                                    max.value = !max.value;\r\n                                }, 300),\r\n                            }, createNSElementNode('svg', undefined, {\r\n                                viewBox: '0 0 1024 1024',\r\n                                class: 'egg_icon',\r\n                            }, createNSElementNode('path', undefined, {\r\n                                d: 'M609.52 584.92a35.309 35.309 0 0 1 24.98-10.36c9.37 0 18.36 3.73 24.98 10.36l189.29 189.22-0.07-114.3 0.57-6.35c3.25-17.98 19.7-30.5 37.9-28.85 18.2 1.65 32.12 16.92 32.09 35.2v200.23c-0.05 1.49-0.19 2.97-0.42 4.45l-0.21 1.13c-0.22 1.44-0.55 2.85-0.99 4.24l-0.57 1.62-0.56 1.41a34.163 34.163 0 0 1-7.62 11.36l2.12-2.4-0.14 0.14-0.92 1.06-1.06 1.2-0.57 0.57-0.56 0.57a36.378 36.378 0 0 1-16.23 8.39l-3.53 0.5-4.02 0.35h-199.6l-6.35-0.63c-16.73-3.06-28.9-17.63-28.93-34.64l0.56-6.35c3.07-16.76 17.67-28.93 34.71-28.92l114.29-0.14-189.07-189.1-4.09-4.94c-9.71-14.01-8.01-32.95 4.02-45.02z m-162.06 0c12.06 12.05 13.78 30.99 4.09 45.01l-4.09 4.94-189.15 189.08 114.3 0.14c17.04-0.01 31.65 12.17 34.71 28.92l0.57 6.35c-0.03 17.01-12.19 31.58-28.92 34.64l-6.35 0.63H173.09l-4.23-0.42-3.39-0.49a36.38 36.38 0 0 1-17.36-9.52l-1.06-1.13-0.98-1.13 0.98 1.06-1.97-2.26 0.85 1.06-0.42-0.56a35.137 35.137 0 0 1-3.74-5.64l-1.13-2.68a34.71 34.71 0 0 1-2.11-7.33l-0.28-1.13c-0.21-1.47-0.33-2.96-0.36-4.45V659.78c-0.03-18.28 13.89-33.55 32.09-35.2 18.2-1.65 34.65 10.87 37.9 28.85l0.57 6.35-0.07 114.36 189.29-189.22c13.77-13.77 36.11-13.77 49.88 0h-0.09z m-74.71-471.71l6.35 0.57c16.76 3.06 28.93 17.67 28.92 34.71l-0.63 6.35c-3.07 16.76-17.67 28.93-34.71 28.92l-114.3 0.14 189.15 189.08 4.09 4.94c10.26 15.02 7.42 35.37-6.55 47.01-13.98 11.63-34.51 10.74-47.42-2.07L208.29 233.71l0.07 114.3-0.57 6.35c-3.25 17.98-19.7 30.5-37.9 28.85-18.2-1.65-32.12-16.92-32.09-35.2V147.78c0-1.55 0.14-3.03 0.35-4.51l0.21-1.13c0.24-1.44 0.59-2.85 1.06-4.23a34.97 34.97 0 0 1 8.68-14.39l-2.12 2.4-0.42 0.57 1.55-1.84-0.99 1.06 0.92-0.98 2.26-2.33c3.04-2.73 6.52-4.92 10.3-6.49l2.82-1.06c3.45-1.07 7.04-1.62 10.65-1.62l-3.6 0.14h0.49l1.48-0.14h201.31z m512.91 0l1.41 0.14h0.42c2.43 0.29 4.84 0.79 7.19 1.48l2.82 1.06 2.61 1.2 3.04 1.76c2.09 1.33 4.03 2.89 5.78 4.66l1.13 1.2 0.78 0.98 0.21 0.14 0.49 0.64 2.33 3.17c2.35 3.83 3.98 8.07 4.8 12.49l0.21 1.13c0.21 1.48 0.35 2.96 0.35 4.44v200.37c-0.16 18.13-14.03 33.19-32.08 34.83-18.06 1.64-34.42-10.67-37.83-28.48l-0.57-6.35V233.65L659.54 422.87c-12.9 12.95-33.56 13.91-47.59 2.2-14.04-11.71-16.81-32.2-6.38-47.22l4.02-4.86 189.22-189.08-114.29-0.14c-17.06 0.04-31.71-12.14-34.78-28.92l-0.63-6.35c-0.01-17.04 12.16-31.65 28.93-34.71l6.35-0.57h201.27z m0 0',\r\n                            }))),\r\n                            // 关闭\r\n                            createElementNode('button', undefined, {\r\n                                class: 'egg_frame_btn',\r\n                                type: 'button',\r\n                                title: '关闭',\r\n                                onclick: debounce(() => {\r\n                                    // 关闭窗口\r\n                                    closeFrame();\r\n                                }, 300),\r\n                            }, createNSElementNode('svg', undefined, {\r\n                                viewBox: '0 0 1024 1024',\r\n                                class: 'egg_icon',\r\n                            }, createNSElementNode('path', undefined, {\r\n                                d: 'M453.44 512L161.472 220.032a41.408 41.408 0 0 1 58.56-58.56L512 453.44 803.968 161.472a41.408 41.408 0 0 1 58.56 58.56L570.56 512l291.968 291.968a41.408 41.408 0 0 1-58.56 58.56L512 570.56 220.032 862.528a41.408 41.408 0 0 1-58.56-58.56L453.44 512z',\r\n                            }))),\r\n                        ]),\r\n                    ]),\r\n                    // 窗口内容\r\n                    createElementNode('div', undefined, {\r\n                        class: 'egg_frame_content',\r\n                    }, watchEffectRef(() => frame.src\r\n                        ? [\r\n                            createElementNode('iframe', undefined, {\r\n                                class: 'egg_frame',\r\n                                src: frame.src,\r\n                                ref(ele) {\r\n                                    frame.ele = ele;\r\n                                },\r\n                            }, undefined),\r\n                        ]\r\n                        : undefined)),\r\n                ], {\r\n                    onMounted() {\r\n                        // 隐藏窗口\r\n                        watch(() => [\r\n                            taskStatus.value,\r\n                            running.value,\r\n                            settings[SettingType.SAME_TAB],\r\n                            settings[SettingType.SILENT_RUN],\r\n                        ], () => {\r\n                            // 同屏任务\r\n                            if (settings[SettingType.SAME_TAB] &&\r\n                                (taskStatus.value === TaskStatusType.START ||\r\n                                    taskStatus.value === TaskStatusType.PAUSE ||\r\n                                    running.value)) {\r\n                                // 设置窗口显示\r\n                                frame.show = !settings[SettingType.SILENT_RUN];\r\n                            }\r\n                        });\r\n                    },\r\n                }),\r\n            ];\r\n        }\r\n    }), {\r\n        onMounted() {\r\n            // 关闭窗口\r\n            watch(() => [login.value, settings[SettingType.SAME_TAB]], () => {\r\n                if (login.value) {\r\n                    if (settings[SettingType.SAME_TAB]) {\r\n                        frame.exist = true;\r\n                        closeWin();\r\n                    }\r\n                    else {\r\n                        closeFrame();\r\n                        frame.exist = false;\r\n                    }\r\n                }\r\n                else {\r\n                    closeWin();\r\n                    closeFrame();\r\n                    frame.exist = false;\r\n                }\r\n            });\r\n        },\r\n    });\r\n}\r\n\r\n","from":"src\\component\\Frame.ts","to":"Frame.js"},"src\\component\\LoginItem.ts":{"timeStamp":"2023-03-04T06:24:38.622Z","data":"/**\r\n * @description 登录\r\n */\r\nfunction LoginItem() {\r\n    return watchEffectRef(() => {\r\n        return login.value\r\n            ? undefined\r\n            : createElementNode('div', undefined, {\r\n                class: 'egg_login_item',\r\n            }, [\r\n                // 登录按钮\r\n                createElementNode('button', undefined, {\r\n                    type: 'button',\r\n                    class: 'egg_login_btn',\r\n                    onclick: debounce(async () => {\r\n                        // 开始登录\r\n                        handleLogin();\r\n                    }, 300),\r\n                }, createTextNode('扫码登录')),\r\n                // 窗口\r\n                createElementNode('div', undefined, {\r\n                    class: watchEffectRef(() => `egg_login_img_wrap${loginQRCodeShow.value ? ' active' : ''}`),\r\n                }, createElementNode('img', undefined, {\r\n                    class: 'egg_login_img',\r\n                })),\r\n            ], {\r\n                onMounted() {\r\n                    watch(() => settings[SettingType.SCHEDULE_RUN], () => {\r\n                        // 未开启定时展示二维码\r\n                        if (!settings[SettingType.SCHEDULE_RUN]) {\r\n                            // 开始登录\r\n                            handleLogin();\r\n                        }\r\n                    }, true);\r\n                },\r\n            });\r\n    });\r\n}\r\n\r\n","from":"src\\component\\LoginItem.ts","to":"LoginItem.js"},"src\\component\\InfoItem.ts":{"timeStamp":"2023-02-22T15:18:21.583Z","data":"/**\r\n * @description 信息\r\n * @returns\r\n */\r\nfunction InfoItem() {\r\n    return watchEffectRef(() => {\r\n        if (login.value) {\r\n            return createElementNode('div', undefined, {\r\n                class: 'egg_info_item',\r\n            }, [\r\n                // 用户信息\r\n                createElementNode('div', undefined, { class: 'egg_userinfo' }, [\r\n                    // 头像\r\n                    createElementNode('div', undefined, { class: 'egg_avatar' }, watchEffectRef(() => {\r\n                        return [\r\n                            userinfo.avatar\r\n                                ? createElementNode('img', undefined, {\r\n                                    src: userinfo.avatar,\r\n                                    class: 'egg_avatar_img',\r\n                                })\r\n                                : createElementNode('div', undefined, {\r\n                                    class: 'egg_avatar_nick',\r\n                                }, createTextNode(watchEffectRef(() => userinfo.nick.substring(1, 3)))),\r\n                        ];\r\n                    })),\r\n                    // 昵称\r\n                    createElementNode('div', undefined, { class: 'egg_nick' }, createTextNode(watchEffectRef(() => userinfo.nick))),\r\n                ]),\r\n                // 退出按钮\r\n                createElementNode('button', undefined, {\r\n                    type: 'button',\r\n                    class: 'egg_login_btn',\r\n                    onclick: debounce(() => {\r\n                        // 退出登录\r\n                        handleLogout();\r\n                    }, 300),\r\n                }, createTextNode('退出')),\r\n            ], {\r\n                onMounted() {\r\n                    // 刷新用户信息\r\n                    refreshUserInfo();\r\n                },\r\n            });\r\n        }\r\n    });\r\n}\r\n\r\n","from":"src\\component\\InfoItem.ts","to":"InfoItem.js"},"src\\component\\ScoreItem.ts":{"timeStamp":"2023-06-02T03:32:53.159Z","data":"/**\r\n * @description 分数详情\r\n */\r\nfunction ScoreItem() {\r\n    return watchEffectRef(() => {\r\n        if (login.value) {\r\n            // 分数显示\r\n            const scoreShow = ref(false);\r\n            // 分数信息\r\n            return createElementNode('div', undefined, {\r\n                class: 'egg_score_item',\r\n            }, createElementNode('div', undefined, { class: 'egg_scoreinfo' }, [\r\n                createElementNode('div', undefined, {\r\n                    class: 'egg_totalscore',\r\n                }, [\r\n                    createTextNode('总积分'),\r\n                    createElementNode('span', undefined, undefined, createTextNode(totalScore)),\r\n                ]),\r\n                createElementNode('div', undefined, {\r\n                    class: 'egg_todayscore',\r\n                }, [\r\n                    createElementNode('button', undefined, {\r\n                        type: 'button',\r\n                        class: 'egg_todayscore_btn',\r\n                        title: '查看分数详情',\r\n                        onclick: debounce(() => {\r\n                            scoreShow.value = !scoreShow.value;\r\n                        }, 300),\r\n                        onblur: () => {\r\n                            scoreShow.value = false;\r\n                        },\r\n                    }, [\r\n                        createTextNode('当天分数'),\r\n                        // 当天分数\r\n                        createElementNode('span', undefined, undefined, createTextNode(todayScore)),\r\n                        // icon\r\n                        createNSElementNode('svg', undefined, {\r\n                            viewBox: '0 0 1024 1024',\r\n                            class: 'egg_icon',\r\n                        }, createNSElementNode('path', undefined, {\r\n                            d: 'M332.16 883.84a40.96 40.96 0 0 0 58.24 0l338.56-343.04a40.96 40.96 0 0 0 0-58.24L390.4 140.16a40.96 40.96 0 0 0-58.24 58.24L640 512l-307.84 314.24a40.96 40.96 0 0 0 0 57.6z',\r\n                        })),\r\n                        createElementNode('div', undefined, {\r\n                            class: watchEffectRef(() => `egg_score_details${scoreShow.value ? '' : ' hide'}`),\r\n                        }, [\r\n                            createElementNode('div', undefined, { class: 'egg_score_title' }, [\r\n                                createNSElementNode('svg', undefined, {\r\n                                    viewBox: '0 0 1024 1024',\r\n                                    class: 'egg_icon',\r\n                                }, [\r\n                                    createNSElementNode('path', undefined, {\r\n                                        d: 'M314.81 304.01h415.86v58.91H314.81zM314.81 440.24h415.86v58.91H314.81z',\r\n                                    }),\r\n                                    createNSElementNode('path', undefined, {\r\n                                        d: 'M814.8 892.74h-8.64l-283.51-182-283.51 182h-8.64A69.85 69.85 0 0 1 160.72 823V188.22a69.85 69.85 0 0 1 69.77-69.77H814.8a69.85 69.85 0 0 1 69.77 69.77V823a69.85 69.85 0 0 1-69.77 69.74zM230.5 177.35a10.87 10.87 0 0 0-10.86 10.86V823a10.86 10.86 0 0 0 5 9.11l298.01-191.42 298.06 191.38a10.86 10.86 0 0 0 5-9.11V188.22a10.87 10.87 0 0 0-10.86-10.86z',\r\n                                    }),\r\n                                ]),\r\n                                createElementNode('div', undefined, {\r\n                                    class: 'egg_score_title_text',\r\n                                }, createTextNode('积分详情')),\r\n                            ]),\r\n                            ...taskConfig.map((task) => createElementNode('div', undefined, { class: 'egg_score_item' }, [\r\n                                createTextNode(task.title),\r\n                                createElementNode('span', undefined, {\r\n                                    class: 'egg_score_detail',\r\n                                }, createTextNode(watchEffectRef(() => task.score))),\r\n                            ])),\r\n                        ]),\r\n                    ]),\r\n                ]),\r\n            ]), {\r\n                onMounted() {\r\n                    // 刷新分数信息\r\n                    refreshScoreInfo();\r\n                },\r\n            });\r\n        }\r\n    });\r\n}\r\n\r\n","from":"src\\component\\ScoreItem.ts","to":"ScoreItem.js"},"src\\component\\NoramlItem.ts":{"timeStamp":"2023-07-14T10:51:00.944Z","data":"/**\r\n * @description 设置普通项\r\n * @returns\r\n */\r\nfunction NormalItem({ title, tip, checked, onchange, }) {\r\n    return createElementNode('div', undefined, { class: 'egg_setting_item' }, [\r\n        createElementNode('div', undefined, { class: 'egg_label_wrap' }, [\r\n            createElementNode('label', undefined, { class: 'egg_task_title' }, [\r\n                createTextNode(title),\r\n                createElementNode('span', undefined, {\r\n                    class: 'egg_detail',\r\n                    title: tip,\r\n                }, createTextNode('i')),\r\n            ]),\r\n        ]),\r\n        createElementNode('input', undefined, {\r\n            title: tip,\r\n            class: 'egg_switch',\r\n            type: 'checkbox',\r\n            checked,\r\n            onchange,\r\n        }),\r\n    ]);\r\n}\r\n\r\n","from":"src\\component\\NoramlItem.ts","to":"NoramlItem.js"},"src\\component\\TaskItem.ts":{"timeStamp":"2023-07-04T09:36:52.245Z","data":"/**\r\n * @description 设置任务项\r\n * @returns\r\n */\r\nfunction TaskItem({ title, tip, checked, currentScore, dayMaxScore, onchange, immutable, }) {\r\n    return createElementNode('div', undefined, {\r\n        class: 'egg_task_item',\r\n    }, [\r\n        createElementNode('div', undefined, { class: 'egg_label_wrap' }, [\r\n            createElementNode('div', undefined, { class: 'egg_task_title_wrap' }, [\r\n                createElementNode('div', undefined, { class: 'egg_task_title' }, createTextNode(title)),\r\n                createElementNode('div', undefined, { class: 'egg_task_progress_wrap' }, [\r\n                    createElementNode('div', undefined, {\r\n                        class: 'egg_task_current',\r\n                    }, createTextNode(currentScore)),\r\n                    createElementNode('div', undefined, {\r\n                        class: 'egg_task_max',\r\n                    }, createTextNode(watchEffectRef(() => `/${dayMaxScore.value}`))),\r\n                ]),\r\n            ]),\r\n            createElementNode('div', undefined, { class: 'egg_progress' }, [\r\n                createElementNode('div', undefined, { class: 'egg_track' }, createElementNode('div', undefined, {\r\n                    class: 'egg_bar',\r\n                    style: watchEffectRef(() => `width: ${((100 * currentScore.value) /\r\n                        dayMaxScore.value).toFixed(1)}%;`),\r\n                })),\r\n            ]),\r\n        ]),\r\n        createElementNode('input', undefined, {\r\n            title: tip,\r\n            class: 'egg_switch',\r\n            type: 'checkbox',\r\n            checked,\r\n            onchange,\r\n            disabled: immutable,\r\n        }),\r\n    ]);\r\n}\r\n\r\n","from":"src\\component\\TaskItem.ts","to":"TaskItem.js"},"src\\component\\TaskList.ts":{"timeStamp":"2023-07-04T09:37:38.008Z","data":"/**\r\n * @description 任务\r\n */\r\nfunction TaskList() {\r\n    // 处理任务设置变化\r\n    const handleTaskChange = (e, type, title) => {\r\n        // 开关\r\n        const { checked } = e.target;\r\n        if (taskConfig[type].active !== checked) {\r\n            taskConfig[type].active = checked;\r\n            // 设置\r\n            GM_setValue('taskConfig', JSON.stringify(taskConfig));\r\n            // 创建提示\r\n            createTip(`${title} ${checked ? '打开' : '关闭'}!`);\r\n        }\r\n    };\r\n    // 登录加载\r\n    watch(login, async () => {\r\n        if (login.value) {\r\n            // 加载任务列表\r\n            await refreshTaskList();\r\n            // 未完成任务\r\n            if (taskConfig.some((task) => task.active && !task.status)) {\r\n                // 全局暂停\r\n                GM_setValue('pauseStudy', false);\r\n                // 加载完毕\r\n                taskStatus.value = TaskStatusType.LOADED;\r\n                return;\r\n            }\r\n            // 任务完毕\r\n            taskStatus.value = TaskStatusType.FINISH;\r\n        }\r\n    }, true);\r\n    return createElementNode('div', undefined, {\r\n        class: 'egg_task_list',\r\n    }, taskConfig.map((label) => label.immutable\r\n        ? TaskItem({\r\n            title: label.title,\r\n            tip: label.tip,\r\n            checked: watchEffectRef(() => label.active),\r\n            currentScore: watchEffectRef(() => label.currentScore),\r\n            dayMaxScore: watchEffectRef(() => label.dayMaxScore),\r\n            onchange: debounce((e) => {\r\n                handleTaskChange(e, label.type, label.title);\r\n            }, 300),\r\n            immutable: label.immutable,\r\n        })\r\n        : TaskItem({\r\n            title: label.title,\r\n            tip: label.tip,\r\n            checked: watchEffectRef(() => label.active),\r\n            currentScore: watchEffectRef(() => label.currentScore),\r\n            dayMaxScore: watchEffectRef(() => label.dayMaxScore),\r\n            onchange: debounce((e) => {\r\n                handleTaskChange(e, label.type, label.title);\r\n            }, 300),\r\n            immutable: label.immutable,\r\n        })));\r\n}\r\n\r\n","from":"src\\component\\TaskList.ts","to":"TaskList.js"},"src\\component\\TaskBtn.ts":{"timeStamp":"2023-07-13T12:00:01.869Z","data":"/**\r\n * @description 任务按钮\r\n */\r\nfunction TaskBtn() {\r\n    return watchEffectRef(() => {\r\n        if (login.value) {\r\n            /**\r\n             * @description 学习\r\n             */\r\n            async function study() {\r\n                // 创建提示\r\n                createTip('开始学习!');\r\n                // 暂停\r\n                await studyPauseLock();\r\n                // 文章选读\r\n                if (taskConfig[TaskType.READ].active &&\r\n                    !taskConfig[TaskType.READ].status) {\r\n                    log('任务一: 文章选读');\r\n                    // 创建提示\r\n                    createTip('任务一: 文章选读');\r\n                    // 暂停\r\n                    await studyPauseLock();\r\n                    // 看新闻\r\n                    await readNews();\r\n                }\r\n                log('任务一: 文章选读已完成!');\r\n                // 视听学习\r\n                if (taskConfig[TaskType.WATCH].active &&\r\n                    !taskConfig[TaskType.WATCH].status) {\r\n                    log('任务二: 视听学习');\r\n                    // 创建提示\r\n                    createTip('任务二: 视听学习');\r\n                    // 暂停\r\n                    await studyPauseLock();\r\n                    // 看视频\r\n                    await watchVideo();\r\n                }\r\n                log('任务二: 视听学习已完成!');\r\n                // 每日答题\r\n                if (taskConfig[TaskType.PRACTICE].active &&\r\n                    !taskConfig[TaskType.PRACTICE].status) {\r\n                    log('任务三: 每日答题');\r\n                    // 创建提示\r\n                    createTip('任务三: 每日答题');\r\n                    // 暂停\r\n                    await studyPauseLock();\r\n                    // 做每日答题\r\n                    await doExamPractice();\r\n                }\r\n                log('任务三: 每日答题已完成!');\r\n            }\r\n            /**\r\n             * @description 暂停任务\r\n             */\r\n            function pauseTask() {\r\n                // 全局暂停\r\n                GM_setValue('pauseStudy', true);\r\n                taskStatus.value = TaskStatusType.PAUSE;\r\n            }\r\n            /**\r\n             * @description 继续任务\r\n             */\r\n            function continueTask() {\r\n                // 全局暂停\r\n                GM_setValue('pauseStudy', false);\r\n                taskStatus.value = TaskStatusType.START;\r\n            }\r\n            /**\r\n             * @description 开始任务\r\n             */\r\n            async function startTask() {\r\n                // 未完成任务\r\n                if (taskConfig.some((task) => task.active && !task.status)) {\r\n                    // 开始任务\r\n                    taskStatus.value = TaskStatusType.START;\r\n                    try {\r\n                        // 学习\r\n                        await study();\r\n                        // 同屏任务\r\n                        if (settings[SettingType.SAME_TAB]) {\r\n                            // 关闭窗口\r\n                            closeFrame();\r\n                            // 窗口不存在\r\n                            frame.exist = false;\r\n                        }\r\n                    }\r\n                    catch (err) {\r\n                        if (err instanceof Error) {\r\n                            // 提示\r\n                            createTip(err.message);\r\n                            // 错误\r\n                            error(err.message);\r\n                            return;\r\n                        }\r\n                        // 提示\r\n                        createTip(String(err));\r\n                        // 错误\r\n                        error(err);\r\n                    }\r\n                }\r\n                // 刷新任务\r\n                taskStatus.value = TaskStatusType.FINISH;\r\n                log('已完成');\r\n                // 创建提示\r\n                createTip('完成学习!');\r\n                // 远程推送\r\n                if (settings[SettingType.REMOTE_PUSH]) {\r\n                    // 推送\r\n                    const res = await pushModal({\r\n                        title: '学习推送',\r\n                        to: userinfo.nick,\r\n                        content: [\r\n                            '学习强国, 学习完成!',\r\n                            `当天积分:  ${getHighlightHTML(todayScore.value)} 分`,\r\n                            `总积分: ${getHighlightHTML(totalScore.value)} 分`,\r\n                            ...taskConfig.map((task) => getProgressHTML(task.title, task.currentScore, task.dayMaxScore)),\r\n                        ],\r\n                        type: 'success',\r\n                    }, pushToken.value);\r\n                    createTip(`学习推送${res ? '成功' : '失败'}!`);\r\n                }\r\n            }\r\n            // 已在等待\r\n            let flag = false;\r\n            // 自动答题\r\n            watch(() => [taskStatus.value, settings[SettingType.AUTO_START]], async () => {\r\n                // 加载完毕\r\n                if (!flag && taskStatus.value === TaskStatusType.LOADED) {\r\n                    // 自动答题\r\n                    if (settings[SettingType.AUTO_START]) {\r\n                        // 等待中\r\n                        flag = true;\r\n                        // 创建提示\r\n                        const tip = createTip('即将自动开始任务', 5, true);\r\n                        // 等待倒计时结束\r\n                        await tip.waitCountDown();\r\n                        // 再次查看是否开启\r\n                        if (settings[SettingType.AUTO_START] &&\r\n                            taskStatus.value !== TaskStatusType.START) {\r\n                            // 创建提示\r\n                            createTip('自动开始任务');\r\n                            // 开始任务\r\n                            startTask();\r\n                            return;\r\n                        }\r\n                        // 取消等待\r\n                        flag = false;\r\n                        // 创建提示\r\n                        createTip('已取消自动开始任务!');\r\n                    }\r\n                }\r\n            });\r\n            // 切换开关任务未完成\r\n            taskConfig.forEach((task) => {\r\n                watch(() => [task.active], () => {\r\n                    if (taskStatus.value === TaskStatusType.FINISH) {\r\n                        if (task.active && !task.status) {\r\n                            taskStatus.value = TaskStatusType.LOADED;\r\n                        }\r\n                    }\r\n                });\r\n            });\r\n            return createElementNode('div', undefined, { class: 'egg_study_item' }, createElementNode('button', undefined, {\r\n                class: watchEffectRef(() => `egg_study_btn${taskStatus.value === TaskStatusType.START ? ' loading' : ''}`),\r\n                type: 'button',\r\n                disabled: watchRef(() => [running.value, taskStatus.value], () => running.value ||\r\n                    taskStatus.value === TaskStatusType.LOADING ||\r\n                    taskStatus.value === TaskStatusType.FINISH),\r\n                onclick: watchEffectRef(() => taskStatus.value === TaskStatusType.LOADED\r\n                    ? debounce(startTask, 300)\r\n                    : taskStatus.value === TaskStatusType.START\r\n                        ? debounce(pauseTask, 300)\r\n                        : taskStatus.value === TaskStatusType.PAUSE\r\n                            ? debounce(continueTask, 300)\r\n                            : undefined),\r\n            }, createTextNode(watchEffectRef(() => `${taskStatus.value === TaskStatusType.LOADING\r\n                ? '等待中'\r\n                : taskStatus.value === TaskStatusType.LOADED\r\n                    ? '开始学习'\r\n                    : taskStatus.value === TaskStatusType.START\r\n                        ? '正在学习, 点击暂停'\r\n                        : taskStatus.value === TaskStatusType.PAUSE\r\n                            ? '继续学习'\r\n                            : taskStatus.value === TaskStatusType.FINISH\r\n                                ? '已完成'\r\n                                : ''}`))));\r\n        }\r\n    });\r\n}\r\n\r\n","from":"src\\component\\TaskBtn.ts","to":"TaskBtn.js"},"src\\component\\ScheduleList.ts":{"timeStamp":"2023-03-08T05:39:30.595Z","data":"/**\r\n * @description 定时项目\r\n * @returns\r\n */\r\nfunction ScheduleList() {\r\n    return createElementNode('div', undefined, { class: 'egg_schedule_list' }, watchEffectRef(() => {\r\n        return scheduleList.length\r\n            ? scheduleList.map((schedule) => createElementNode('div', undefined, { class: 'egg_schedule_item' }, [\r\n                createElementNode('div', undefined, {\r\n                    class: `egg_schedule_detail_time_wrap${isLate(schedule) ? ' inactive' : ''}`,\r\n                }, [\r\n                    createElementNode('div', undefined, {\r\n                        class: 'egg_schedule_detail_icon',\r\n                    }, createNSElementNode('svg', undefined, {\r\n                        viewBox: '0 0 1024 1024',\r\n                        class: 'egg_icon',\r\n                    }, [\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M810.137703 213.860762c-164.388001-164.4187-431.887404-164.4187-596.277452 0-164.417677 164.388001-164.417677 431.889451 0 596.278475 164.390048 164.417677 431.890474 164.417677 596.277452 0C974.557426 645.750213 974.557426 378.248763 810.137703 213.860762zM767.347131 767.345596c-140.797723 140.829446-369.927237 140.797723-510.693238 0-140.828422-140.797723-140.828422-369.895515 0-510.708588 140.767024-140.783397 369.896538-140.813073 510.693238 0C908.14383 397.420405 908.14383 626.578572 767.347131 767.345596z',\r\n                        }),\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M721.450824 521.495258 515.404028 521.495258l0.028653-227.948619c0-15.124466-12.362562-27.458375-27.501354-27.458375s-27.443026 12.33391-27.443026 27.458375l0 235.115855c0 0.835018-1.013073 20.48659 12.094456 34.459836 8.331759 8.809643 20.038382 13.288654 35.148521 13.288654l213.720569 0.031722c15.140839 0 27.472702-12.304234 27.472702-27.474748C748.922503 533.887496 736.620315 521.584286 721.450824 521.495258z',\r\n                        }),\r\n                    ])),\r\n                    createElementNode('div', undefined, { class: 'egg_schedule_detail_time' }, createTextNode(schedule.time)),\r\n                ]),\r\n                createElementNode('div', undefined, { class: 'egg_schedule_detail_del_wrap' }, [\r\n                    createElementNode('button', undefined, {\r\n                        class: 'egg_schedule_del_btn',\r\n                        onclick: debounce(() => {\r\n                            // 定时刷新\r\n                            if (!settings[SettingType.SCHEDULE_RUN]) {\r\n                                createTip('未开启定时刷新!');\r\n                                return;\r\n                            }\r\n                            // 索引\r\n                            const index = scheduleList.findIndex((s) => s === schedule);\r\n                            // 删除元素\r\n                            scheduleList.splice(index, 1);\r\n                            // 存储\r\n                            GM_setValue('scheduleList', JSON.stringify(scheduleList));\r\n                            // 刷新任务\r\n                            refreshScheduleTask();\r\n                        }, 300),\r\n                    }, createNSElementNode('svg', undefined, {\r\n                        viewBox: '0 0 1024 1024',\r\n                        class: 'egg_icon',\r\n                    }, [\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M896.22 896.22c14.262-14.263 11.263-40.449-6.583-58.295L230.473 178.76c-17.847-17.847-44.105-20.846-58.295-6.583-14.263 14.19-11.264 40.448 6.583 58.295l659.164 659.164c17.846 17.846 44.032 20.845 58.294 6.582',\r\n                        }),\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M172.178 896.22c-14.263-14.263-11.264-40.449 6.583-58.295L837.925 178.76c17.846-17.847 44.032-20.846 58.294-6.583 14.263 14.19 11.264 40.448-6.582 58.295L230.4 889.637c-17.847 17.846-44.105 20.845-58.295 6.582',\r\n                        }),\r\n                    ])),\r\n                ]),\r\n            ]))\r\n            : [\r\n                createElementNode('div', undefined, { class: 'egg_schedule_list_none' }, [\r\n                    createNSElementNode('svg', undefined, {\r\n                        viewBox: '0 0 1024 1024',\r\n                        class: 'egg_icon',\r\n                    }, [\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M238.1 520.5c-17.6 0-31.9-14.3-31.9-31.9 0-17.6 14.3-31.9 31.9-31.9h293c17.6 0 31.9 14.3 31.9 31.9 0 17.6-14.3 31.9-31.9 31.9h-293zM238.1 733.6c-17.6 0-31.9-14.3-31.9-31.9s14.3-31.9 31.9-31.9h186.5c17.6 0 31.9 14.3 31.9 31.9s-14.3 31.9-31.9 31.9H238.1zM241.6 314.9c-17.6 0-31.9-14.3-31.9-31.9s14.3-31.9 31.9-31.9h426.1c17.6 0 31.9 14.3 31.9 31.9 0 17.5-14.3 31.7-31.8 31.9H241.6z',\r\n                        }),\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M160 926.6c-46.9 0-85.1-38.2-85.1-85.1V149.1c0-46.9 38.2-85.1 85.1-85.1h586c46.9 0 85.1 38.2 85.1 85.1v297.4c0 17.6-14.3 31.9-31.9 31.9-17.6 0-31.9-14.3-31.9-31.9V149.1c0-11.8-9.6-21.4-21.4-21.4H160c-11.8 0-21.4 9.6-21.4 21.4v692.4c0 11.8 9.6 21.4 21.4 21.4h304.5c17.5 0 31.8 14.2 31.9 31.8 0 17.6-14.3 31.8-31.9 31.8H160z',\r\n                        }),\r\n                        createNSElementNode('path', undefined, {\r\n                            d: 'M917.2 959.9c-8.5 0-16.5-3.3-22.5-9.3l-78.5-78.5-5.3-0.5-0.6 0.4c-31.7 21.6-68.7 33-107 33-105.2 0-190.8-85.6-190.8-190.8s85.6-190.8 190.8-190.8c105.2 0 190.8 85.6 190.8 190.8 0 38.2-11.4 75.2-33 107l-0.4 0.6 0.5 5.3 78.5 78.5c6 6 9.3 14 9.3 22.5s-3.4 16.5-9.4 22.5c-5.9 6-13.9 9.3-22.4 9.3zM703.4 587c-70.1 0-127.2 57.1-127.2 127.2s57.1 127.2 127.2 127.2 127.2-57.1 127.2-127.2S773.6 587 703.4 587z',\r\n                        }),\r\n                    ]),\r\n                    createElementNode('div', undefined, {\r\n                        class: 'egg_schedule_list_none_text',\r\n                    }, createTextNode('暂无定时任务')),\r\n                ]),\r\n            ];\r\n    }));\r\n}\r\n\r\n","from":"src\\component\\ScheduleList.ts","to":"ScheduleList.js"},"src\\component\\TimeInput.ts":{"timeStamp":"2023-03-08T07:30:02.342Z","data":"/**\r\n * @description 时间输入\r\n * @returns\r\n */\r\nfunction TimeInput({ hour, minute, onchange, }) {\r\n    // 小时\r\n    const hours = new Array(24).fill(undefined).map((v, i) => ({\r\n        value: i,\r\n        label: formatDateNum(i),\r\n    }));\r\n    // 分钟\r\n    const minutes = new Array(60).fill(undefined).map((v, i) => ({\r\n        value: i,\r\n        label: formatDateNum(i),\r\n    }));\r\n    const valueRef = watchEffectRef(() => {\r\n        const h = hours.find((h) => h.value === hour.value);\r\n        const min = minutes.find((min) => min.value === minute.value);\r\n        return {\r\n            hour: h ? h.value : -1,\r\n            minute: min ? min.value : -1,\r\n        };\r\n    });\r\n    return createElementNode('div', undefined, { class: 'egg_time_input' }, [\r\n        createElementNode('div', undefined, { class: 'egg_hour_wrap' }, [\r\n            Select({\r\n                data: hours,\r\n                placeholder: '00',\r\n                maxlength: 2,\r\n                value: hour,\r\n                onchange({ value }) {\r\n                    valueRef.value.hour = value;\r\n                    onchange && onchange(valueRef.value);\r\n                },\r\n                onblur(res) {\r\n                    if (!res) {\r\n                        valueRef.value.hour = -1;\r\n                        onchange && onchange(valueRef.value);\r\n                    }\r\n                },\r\n            }),\r\n        ]),\r\n        createElementNode('span', undefined, { class: 'egg_separator' }, createTextNode(':')),\r\n        createElementNode('div', undefined, { class: 'egg_minute_wrap' }, [\r\n            Select({\r\n                data: minutes,\r\n                placeholder: '00',\r\n                maxlength: 2,\r\n                value: minute,\r\n                onchange({ value }) {\r\n                    valueRef.value.minute = value;\r\n                    onchange && onchange(valueRef.value);\r\n                },\r\n                onblur(res) {\r\n                    if (!res) {\r\n                        valueRef.value.minute = -1;\r\n                        onchange && onchange(valueRef.value);\r\n                    }\r\n                },\r\n            }),\r\n        ]),\r\n    ]);\r\n}\r\n\r\n","from":"src\\component\\TimeInput.ts","to":"TimeInput.js"},"src\\component\\SettingsPanel.ts":{"timeStamp":"2023-06-06T09:16:08.312Z","data":"/**\r\n * @description 设置面板组件\r\n * @returns\r\n */\r\nfunction SettingsPanel({ show }) {\r\n    // token\r\n    let token = '';\r\n    // 小时\r\n    let hour = ref(-1);\r\n    // 分钟\r\n    let minute = ref(-1);\r\n    return createElementNode('div', undefined, {\r\n        class: watchEffectRef(() => `egg_settings${show.value ? ' active' : ''}`),\r\n    }, [\r\n        createElementNode('div', undefined, { class: 'egg_settings_version_wrap' }, [\r\n            createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('版本信息')),\r\n            createElementNode('div', undefined, {\r\n                class: 'egg_settings_version',\r\n            }, [\r\n                createTextNode(`v${version}`),\r\n                createElementNode('a', undefined, {\r\n                    class: 'egg_settings_version_detail',\r\n                    title: 'GitHub Xu22Web/tech-study-js',\r\n                    href: 'https://github.com/Xu22Web/tech-study-js',\r\n                }, createNSElementNode('svg', undefined, {\r\n                    viewBox: '0 0 16 16',\r\n                    class: 'egg_icon',\r\n                }, createNSElementNode('path', undefined, {\r\n                    d: 'M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z',\r\n                }))),\r\n            ]),\r\n        ]),\r\n        createElementNode('div', undefined, { class: 'egg_settings_theme_wrap' }, [\r\n            createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('主题预设')),\r\n            createElementNode('div', undefined, { class: 'egg_settings_theme_colors' }, [\r\n                {\r\n                    value: '#fa3333',\r\n                    title: '强国红',\r\n                    detail: 'XueXi Red',\r\n                    code: 'none',\r\n                },\r\n                {\r\n                    value: '#bb2649',\r\n                    title: '非凡洋红',\r\n                    detail: 'Viva Magenta',\r\n                    code: '18-1750',\r\n                },\r\n                {\r\n                    value: '#35548a',\r\n                    title: '经典蓝',\r\n                    detail: 'Classic Blue',\r\n                    code: '19-4052',\r\n                },\r\n                {\r\n                    value: '#f36f63',\r\n                    title: '活珊瑚橘',\r\n                    detail: 'Living Coral',\r\n                    code: '16-1546',\r\n                },\r\n                {\r\n                    value: '#6d5b97',\r\n                    title: '紫外光色',\r\n                    detail: 'Ultra Violet',\r\n                    code: '18-3838',\r\n                },\r\n                {\r\n                    value: '#86af49',\r\n                    title: '草木绿',\r\n                    detail: 'Greenery',\r\n                    code: '15-0343',\r\n                },\r\n                {\r\n                    value: '#fc8bab',\r\n                    title: 'B站粉',\r\n                    detail: 'Bilibili Pink',\r\n                    code: 'none',\r\n                },\r\n                {\r\n                    value: '#056de8',\r\n                    title: '知乎蓝',\r\n                    detail: 'Zhihu Blue',\r\n                    code: 'none',\r\n                },\r\n            ].map((color) => createElementNode('div', undefined, {\r\n                class: 'egg_settings_theme_color_wrap',\r\n            }, createElementNode('button', undefined, {\r\n                class: 'egg_settings_theme_color',\r\n                type: 'button',\r\n                style: watchEffectRef(() => `color: ${color.value};${themeColor.value === color.value\r\n                    ? ''\r\n                    : ` box-shadow: 0rem 0.4rem 0.1rem 0.1rem ${color.value}30;`}`),\r\n                title: color.title,\r\n                onclick: debounce(() => {\r\n                    if (themeColor.value !== color.value) {\r\n                        themeColor.value = color.value;\r\n                        // 存储\r\n                        GM_setValue('themeColor', themeColor.value);\r\n                    }\r\n                }, 300),\r\n            })))),\r\n        ]),\r\n        createElementNode('div', undefined, {\r\n            class: 'egg_settings_read_time_wrap',\r\n        }, [\r\n            createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('最大文章时长')),\r\n            Select({\r\n                data: [\r\n                    {\r\n                        label: '40s',\r\n                        value: 40,\r\n                    },\r\n                    {\r\n                        label: '60s',\r\n                        value: 60,\r\n                    },\r\n                    {\r\n                        label: '80s',\r\n                        value: 80,\r\n                    },\r\n                    {\r\n                        label: '100s',\r\n                        value: 100,\r\n                    },\r\n                ],\r\n                value: maxRead,\r\n                placeholder: '100s',\r\n                maxlength: 4,\r\n                keep: true,\r\n                onchange({ value }) {\r\n                    // 创建提示\r\n                    createTip('最大文章时长 已保存!');\r\n                    maxRead.value = value;\r\n                    // 存储\r\n                    GM_setValue('maxRead', value);\r\n                },\r\n            }),\r\n        ], {\r\n            onMounted() {\r\n                try {\r\n                    const maxReadTemp = GM_getValue('maxRead');\r\n                    if (maxReadTemp) {\r\n                        maxRead.value = maxReadTemp;\r\n                    }\r\n                }\r\n                catch (e) { }\r\n            },\r\n        }),\r\n        createElementNode('div', undefined, {\r\n            class: 'egg_settings_watch_time_wrap',\r\n        }, [\r\n            createElementNode('div', undefined, { class: 'egg_settings_label' }, createTextNode('最大视听时长')),\r\n            Select({\r\n                data: [\r\n                    {\r\n                        label: '40s',\r\n                        value: 40,\r\n                    },\r\n                    {\r\n                        label: '60s',\r\n                        value: 60,\r\n                    },\r\n                    {\r\n                        label: '80s',\r\n                        value: 80,\r\n                    },\r\n                    {\r\n                        label: '100s',\r\n                        value: 100,\r\n                    },\r\n                    {\r\n                        label: '120s',\r\n                        value: 120,\r\n                    },\r\n                ],\r\n                value: maxWatch,\r\n                placeholder: '120s',\r\n                maxlength: 4,\r\n                keep: true,\r\n                onchange({ value }) {\r\n                    // 创建提示\r\n                    createTip('最大视听时长 已保存!');\r\n                    maxWatch.value = value;\r\n                    // 存储\r\n                    GM_setValue('maxWatch', value);\r\n                },\r\n            }),\r\n        ], {\r\n            onMounted() {\r\n                try {\r\n                    const maxWatchTemp = GM_getValue('maxWatch');\r\n                    if (maxWatchTemp) {\r\n                        maxWatch.value = maxWatchTemp;\r\n                    }\r\n                }\r\n                catch (e) { }\r\n            },\r\n        }),\r\n        watchEffectRef(() => settings[SettingType.REMOTE_PUSH]\r\n            ? createElementNode('div', undefined, { class: 'egg_settings_token_wrap' }, [\r\n                createElementNode('div', undefined, { class: 'egg_settings_token' }, [\r\n                    createElementNode('div', un
Download .txt
gitextract_aws1pt39/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README.md
├── bin/
│   └── index.ts
├── cache.json
├── package.json
├── src/
│   ├── api/
│   │   ├── answer.ts
│   │   ├── data.ts
│   │   ├── login.ts
│   │   ├── push.ts
│   │   └── user.ts
│   ├── component/
│   │   ├── ExamBtn.ts
│   │   ├── Frame.ts
│   │   ├── Hr.ts
│   │   ├── InfoItem.ts
│   │   ├── LoginItem.ts
│   │   ├── NoramlItem.ts
│   │   ├── Panel.ts
│   │   ├── ScheduleList.ts
│   │   ├── ScoreItem.ts
│   │   ├── Select.ts
│   │   ├── SettingsPanel.ts
│   │   ├── TaskBtn.ts
│   │   ├── TaskItem.ts
│   │   ├── TaskList.ts
│   │   ├── TimeInput.ts
│   │   └── Tip.ts
│   ├── config/
│   │   ├── api.ts
│   │   ├── compile.ts
│   │   ├── script.ts
│   │   ├── task.ts
│   │   ├── url.ts
│   │   └── version.ts
│   ├── controller/
│   │   ├── exam.ts
│   │   ├── frame.ts
│   │   ├── login.ts
│   │   ├── readAndWatch.ts
│   │   ├── schedule.ts
│   │   ├── tip.ts
│   │   └── user.ts
│   ├── css/
│   │   └── index.css
│   ├── index.js
│   ├── index.ts
│   ├── shared/
│   │   └── index.ts
│   ├── types/
│   │   └── index.ts
│   └── utils/
│       ├── composition.ts
│       ├── element.ts
│       ├── log.ts
│       ├── push.ts
│       ├── random.ts
│       ├── time.ts
│       └── utils.ts
├── tech-study.js
├── tsconfig.json
└── types/
    └── global.d.ts
Download .txt
SYMBOL INDEX (429 symbols across 43 files)

FILE: src/api/answer.ts
  function getAnswer (line 8) | async function getAnswer(question: string) {
  function saveAnswer (line 51) | async function saveAnswer(question: string, answer: string) {

FILE: src/api/data.ts
  function getNewsList (line 8) | async function getNewsList() {
  function getVideoList (line 27) | async function getVideoList() {
  function getExamPaper (line 46) | async function getExamPaper(pageNo: number) {

FILE: src/api/login.ts
  function generateQRCode (line 6) | async function generateQRCode() {
  function loginWithQRCode (line 26) | async function loginWithQRCode(qrCode: string) {
  function getSign (line 54) | async function getSign() {
  function secureCheck (line 76) | async function secureCheck(data: { code: string; state: string }) {

FILE: src/api/push.ts
  function pushPlus (line 7) | async function pushPlus(

FILE: src/api/user.ts
  type UserInfo (line 6) | type UserInfo = {
  function getUserInfo (line 15) | async function getUserInfo(): Promise<UserInfo | undefined> {
  function getTotalScore (line 32) | async function getTotalScore() {
  function getTodayScore (line 51) | async function getTodayScore() {
  function getTaskList (line 70) | async function getTaskList() {

FILE: src/component/ExamBtn.ts
  function ExamBtn (line 9) | function ExamBtn() {

FILE: src/component/Frame.ts
  function Frame (line 12) | function Frame() {

FILE: src/component/Hr.ts
  function Hr (line 7) | function Hr({ text }: { text: string }) {

FILE: src/component/InfoItem.ts
  function InfoItem (line 12) | function InfoItem() {

FILE: src/component/LoginItem.ts
  function LoginItem (line 11) | function LoginItem() {

FILE: src/component/NoramlItem.ts
  function NormalItem (line 7) | function NormalItem({

FILE: src/component/Panel.ts
  function Panel (line 25) | function Panel() {

FILE: src/component/ScheduleList.ts
  function ScheduleList (line 18) | function ScheduleList() {

FILE: src/component/ScoreItem.ts
  function ScoreItem (line 14) | function ScoreItem() {

FILE: src/component/Select.ts
  function Select (line 13) | function Select<T>({

FILE: src/component/SettingsPanel.ts
  function SettingsPanel (line 30) | function SettingsPanel({ show }: { show: Ref<boolean> }) {

FILE: src/component/TaskBtn.ts
  function TaskBtn (line 27) | function TaskBtn() {

FILE: src/component/TaskItem.ts
  function TaskItem (line 8) | function TaskItem({

FILE: src/component/TaskList.ts
  function TaskList (line 13) | function TaskList() {

FILE: src/component/TimeInput.ts
  function TimeInput (line 10) | function TimeInput({

FILE: src/component/Tip.ts
  function Tip (line 4) | function Tip({

FILE: src/config/api.ts
  constant API_CONFIG (line 4) | const API_CONFIG = {

FILE: src/config/compile.ts
  constant COMPILE_CONFIG (line 7) | const COMPILE_CONFIG = {

FILE: src/config/script.ts
  constant SCRIPT_CONFIG (line 6) | const SCRIPT_CONFIG = {

FILE: src/config/url.ts
  constant URL_CONFIG (line 4) | const URL_CONFIG = {

FILE: src/controller/exam.ts
  type ExamType (line 26) | enum ExamType {
  function getNextButton (line 34) | function getNextButton() {
  function handleSlideVerify (line 57) | function handleSlideVerify() {
  function handleChoiceBtn (line 200) | function handleChoiceBtn(answers: string[]) {
  function handleSingleChoiceRand (line 256) | function handleSingleChoiceRand() {
  function handleMutiplyChoiceRand (line 272) | function handleMutiplyChoiceRand() {
  function handleBlankInputRand (line 336) | async function handleBlankInputRand() {
  function examPauseLock (line 356) | function examPauseLock(callback?: (status: boolean) => void) {
  function doingExam (line 395) | async function doingExam(type: ExamType) {
  function doExamPractice (line 755) | async function doExamPractice() {
  function doExamPaper (line 788) | async function doExamPaper() {
  function initExam (line 819) | async function initExam() {
  function findExamPaper (line 832) | async function findExamPaper() {

FILE: src/controller/frame.ts
  function initMainListener (line 11) | function initMainListener() {
  function initChildListener (line 26) | function initChildListener() {
  function openFrame (line 43) | async function openFrame(url: string, title?: string) {
  function closeFrame (line 67) | function closeFrame() {
  function handleCloseFrame (line 82) | function handleCloseFrame() {
  function waitFrameClose (line 94) | function waitFrameClose() {
  function waitFrameLoaded (line 107) | function waitFrameLoaded(iframe: HTMLElement) {
  function openWin (line 118) | function openWin(url: string) {
  function closeWin (line 129) | function closeWin() {
  function handleCloseWin (line 136) | function handleCloseWin() {
  function waitWinClose (line 150) | function waitWinClose(newPage: Tampermonkey.OpenTabObject) {
  function closeTaskWin (line 161) | function closeTaskWin() {
  function handleCloseTaskWin (line 174) | function handleCloseTaskWin() {
  function waitTaskWin (line 187) | async function waitTaskWin(url: string, title?: string) {

FILE: src/controller/login.ts
  function getQRCode (line 48) | async function getQRCode() {
  function checkQRCode (line 69) | async function checkQRCode(code: string) {
  function tryLogin (line 97) | async function tryLogin(checkCode: string) {
  function handleLogin (line 115) | async function handleLogin() {
  function handleLogout (line 248) | function handleLogout() {

FILE: src/controller/readAndWatch.ts
  function handleNews (line 26) | async function handleNews() {
  function handleVideo (line 45) | async function handleVideo() {
  function reading (line 105) | async function reading(type: number) {
  function getNews (line 205) | async function getNews() {
  function getVideos (line 240) | async function getVideos() {
  function readNews (line 278) | async function readNews() {
  function watchVideo (line 329) | async function watchVideo() {

FILE: src/controller/schedule.ts
  function refreshScheduleTask (line 15) | async function refreshScheduleTask() {
  function clearScheduleTask (line 53) | function clearScheduleTask() {

FILE: src/controller/tip.ts
  function createTip (line 8) | function createTip(

FILE: src/controller/user.ts
  function refreshUserInfo (line 15) | async function refreshUserInfo() {
  function refreshScoreInfo (line 44) | async function refreshScoreInfo() {
  function refreshTaskList (line 68) | async function refreshTaskList() {

FILE: src/index.js
  function initLogo (line 149) | function initLogo() {
  function initTaskConfig (line 155) | function initTaskConfig() {
  function initSettings (line 184) | function initSettings() {
  function initFontSize (line 213) | function initFontSize() {
  function initMaxRead (line 230) | function initMaxRead() {
  function initMaxWatch (line 242) | function initMaxWatch() {
  function initThemeColor (line 254) | function initThemeColor() {
  function renderTip (line 282) | function renderTip() {
  function renderExamBtn (line 330) | async function renderExamBtn() {
  function renderPanel (line 341) | async function renderPanel() {
  function renderFrame (line 350) | function renderFrame() {
  function getAnswer (line 362) | async function getAnswer(question) {
  function saveAnswer (line 398) | async function saveAnswer(question, answer) {
  function getNewsList (line 433) | async function getNewsList() {
  function getVideoList (line 452) | async function getVideoList() {
  function getExamPaper (line 471) | async function getExamPaper(pageNo) {
  function generateQRCode (line 499) | async function generateQRCode() {
  function loginWithQRCode (line 519) | async function loginWithQRCode(qrCode) {
  function getSign (line 547) | async function getSign() {
  function secureCheck (line 569) | async function secureCheck(data) {
  function pushPlus (line 594) | async function pushPlus(token, title, content, template, toToken) {
  function getUserInfo (line 630) | async function getUserInfo() {
  function getTotalScore (line 647) | async function getTotalScore() {
  function getTodayScore (line 666) | async function getTodayScore() {
  function getTaskList (line 685) | async function getTaskList() {
  constant URL_CONFIG (line 725) | const URL_CONFIG = {
  constant API_CONFIG (line 740) | const API_CONFIG = {
  function triggerRef (line 881) | function triggerRef(target, newVal, oldVal) {
  function trigger (line 947) | function trigger(target, key, newVal, oldVal) {
  class Ref (line 981) | class Ref {
    method constructor (line 986) | constructor(val, shallow = false) {
    method toJSON (line 1018) | toJSON() {
  function createElementNode (line 1354) | function createElementNode(tagName, props, attrs, children, options) {
  function createNSElementNode (line 1416) | function createNSElementNode(tagName, props, attrs, children, options) {
  function handleProps (line 1477) | function handleProps(ele, props) {
  function handleAttributes (line 1494) | function handleAttributes(ele, attrs, subscribe, unsubscribe) {
  function handleEventOptions (line 1507) | function handleEventOptions(option) {
  function handleAttribute (line 1523) | function handleAttribute(ele, key, value, subscribe, unsubscribe) {
  function handleChildren (line 1649) | function handleChildren(ele, children, subscribe, unsubscribe) {
  function handleChangeElement (line 1914) | function handleChangeElement(newEle, oldEle, comment, subscribe, unsubsc...
  function createTextNode (line 1942) | function createTextNode(text, options) {
  function mountElement (line 1969) | function mountElement(eleOptions, parent = document.body) {
  function $$ (line 1984) | function $$(selector, parent = document) {
  function $_ (line 1992) | function $_(selector, parent = document, timeout) {
  function createElementBlock (line 2016) | function createElementBlock(eles) {
  function log (line 2029) | function log(...text) {
  function error (line 2036) | function error(...text) {
  function info (line 2043) | function info(...text) {
  function printColor (line 2051) | function printColor(color, ...text) {
  function getProgressHTML (line 2065) | function getProgressHTML(title, current, total) {
  function getHighlightHTML (line 2103) | function getHighlightHTML(text) {
  function getImgHTML (line 2112) | function getImgHTML(src) {
  function createModal (line 2136) | function createModal(options) {
  function pushMessage (line 2286) | async function pushMessage(options) {
  function pushModal (line 2296) | async function pushModal(options, fromToken, toToken) {
  function createRandomPoint (line 2319) | function createRandomPoint(bounds) {
  function createRandomPath (line 2338) | function createRandomPath(start, end, steps) {
  function generateNumAsChar (line 2365) | function generateNumAsChar() {
  function generateUpperAsChar (line 2372) | function generateUpperAsChar() {
  function generateLowerAsChar (line 2379) | function generateLowerAsChar() {
  function generateMix (line 2387) | function generateMix(length = 6) {
  function formatDateNum (line 2412) | function formatDateNum(num) {
  function formatDateTime (line 2424) | function formatDateTime(time = Date.now()) {
  function isLate (line 2464) | function isLate({ hour, minute }) {
  function isNow (line 2476) | function isNow({ hour, minute }) {
  function setCookie (line 2492) | function setCookie(name, value, expires, domain) {
  function getCookie (line 2505) | function getCookie(name) {
  function delCookie (line 2525) | function delCookie(name, domain) {
  function debounce (line 2538) | function debounce(callback, delay) {
  function hasMobile (line 2553) | function hasMobile() {
  function sleep (line 2570) | function sleep(time) {
  function studyPauseLock (line 2586) | function studyPauseLock(callback) {
  function load (line 2619) | function load(match, callback) {
  function getNextButton (line 2804) | function getNextButton() {
  function handleSlideVerify (line 2824) | function handleSlideVerify() {
  function handleChoiceBtn (line 2967) | function handleChoiceBtn(answers) {
  function handleSingleChoiceRand (line 3021) | function handleSingleChoiceRand() {
  function handleMutiplyChoiceRand (line 3037) | function handleMutiplyChoiceRand() {
  function handleBlankInputRand (line 3099) | async function handleBlankInputRand() {
  function examPauseLock (line 3118) | function examPauseLock(callback) {
  function doingExam (line 3156) | async function doingExam(type) {
  function doExamPractice (line 3507) | async function doExamPractice() {
  function doExamPaper (line 3537) | async function doExamPaper() {
  function initExam (line 3567) | async function initExam() {
  function findExamPaper (line 3579) | async function findExamPaper() {
  function initMainListener (line 3615) | function initMainListener() {
  function initChildListener (line 3629) | function initChildListener() {
  function openFrame (line 3645) | async function openFrame(url, title) {
  function closeFrame (line 3668) | function closeFrame() {
  function handleCloseFrame (line 3682) | function handleCloseFrame() {
  function waitFrameClose (line 3690) | function waitFrameClose() {
  function waitFrameLoaded (line 3702) | function waitFrameLoaded(iframe) {
  function openWin (line 3712) | function openWin(url) {
  function closeWin (line 3722) | function closeWin() {
  function handleCloseWin (line 3728) | function handleCloseWin() {
  function waitWinClose (line 3742) | function waitWinClose(newPage) {
  function closeTaskWin (line 3752) | function closeTaskWin() {
  function handleCloseTaskWin (line 3764) | function handleCloseTaskWin() {
  function waitTaskWin (line 3776) | async function waitTaskWin(url, title) {
  function getQRCode (line 3808) | async function getQRCode() {
  function checkQRCode (line 3828) | async function checkQRCode(code) {
  function tryLogin (line 3855) | async function tryLogin(checkCode) {
  function handleLogin (line 3872) | async function handleLogin() {
  function handleLogout (line 3991) | function handleLogout() {
  function handleNews (line 4027) | async function handleNews() {
  function handleVideo (line 4045) | async function handleVideo() {
  function reading (line 4096) | async function reading(type) {
  function getNews (line 4192) | async function getNews() {
  function getVideos (line 4226) | async function getVideos() {
  function readNews (line 4261) | async function readNews() {
  function watchVideo (line 4311) | async function watchVideo() {
  function refreshScheduleTask (line 4368) | async function refreshScheduleTask() {
  function clearScheduleTask (line 4405) | function clearScheduleTask() {
  function createTip (line 4413) | function createTip(text, delay = 2, countShow = false, callback) {
  function refreshUserInfo (line 4519) | async function refreshUserInfo() {
  function refreshScoreInfo (line 4547) | async function refreshScoreInfo() {
  function refreshTaskList (line 4570) | async function refreshTaskList() {
  function Tip (line 4617) | function Tip({ text, count, show, delayShow, countShow, callback, }) {
  function Hr (line 4653) | function Hr({ text }) {
  function Select (line 4664) | function Select({ data, maxlength, placeholder = '', onchange, onblur, v...
  function ExamBtn (line 4771) | function ExamBtn() {
  function Frame (line 4825) | function Frame() {
  function LoginItem (line 4999) | function LoginItem() {
  function InfoItem (line 5040) | function InfoItem() {
  function ScoreItem (line 5087) | function ScoreItem() {
  function NormalItem (line 5169) | function NormalItem({ title, tip, checked, onchange, }) {
  function TaskItem (line 5195) | function TaskItem({ title, tip, checked, currentScore, dayMaxScore, onch...
  function TaskList (line 5234) | function TaskList() {
  function TaskBtn (line 5295) | function TaskBtn() {
  function ScheduleList (line 5487) | function ScheduleList() {
  function TimeInput (line 5569) | function TimeInput({ hour, minute, onchange, }) {
  function SettingsPanel (line 5634) | function SettingsPanel({ show }) {
  function Panel (line 5986) | function Panel() {

FILE: src/index.ts
  function initLogo (line 214) | function initLogo() {
  function initTaskConfig (line 225) | function initTaskConfig() {
  function initSettings (line 254) | function initSettings() {
  function initFontSize (line 286) | function initFontSize() {
  function initMaxRead (line 304) | function initMaxRead() {
  function initMaxWatch (line 316) | function initMaxWatch() {
  function initThemeColor (line 328) | function initThemeColor() {
  function renderTip (line 359) | function renderTip() {
  function renderExamBtn (line 408) | async function renderExamBtn() {
  function renderPanel (line 420) | async function renderPanel() {
  function renderFrame (line 430) | function renderFrame() {

FILE: src/types/index.ts
  type TaskType (line 4) | enum TaskType {
  type SettingType (line 14) | enum SettingType {
  type Settings (line 28) | type Settings = [
  type Schedule (line 42) | type Schedule = {
  type TaskStatusType (line 51) | enum TaskStatusType {
  type NewsVideoList (line 62) | type NewsVideoList = {

FILE: src/utils/composition.ts
  function triggerRef (line 59) | function triggerRef(target: object, newVal: any, oldVal: any) {
  function trigger (line 127) | function trigger(target: object, key: string, newVal: any, oldVal: any) {
  type ReactiveFlags (line 152) | enum ReactiveFlags {
  class Ref (line 162) | class Ref<T = any> {
    method constructor (line 167) | constructor(val: T, shallow: boolean = false) {
    method toJSON (line 198) | toJSON() {
  type UnwrapRef (line 206) | type UnwrapRef<T> = T extends Ref<infer P> ? P : T;
  type UnwrapRefArray (line 211) | type UnwrapRefArray<T> = T extends Ref<infer K>[]
  type Reactive (line 369) | type Reactive<T extends object = object> = {

FILE: src/utils/element.ts
  type BaseEleChild (line 3) | type BaseEleChild = Ele<HTMLElement | SVGElement | Text | Comment>;
  type EleChild (line 6) | type EleChild = BaseEleChild | EleChildPromise | undefined;
  type EleChildPromise (line 9) | type EleChildPromise = Promise<BaseEleChild | undefined>;
  type EleChildren (line 12) | type EleChildren = (
  type EleChildrenPromise (line 19) | type EleChildrenPromise = Promise<BaseEleChild[] | undefined>;
  type EleChildenRef (line 22) | type EleChildenRef = Ref<BaseEleChild[] | EleChildrenPromise | undefined>;
  type EleCreateOptions (line 27) | type EleCreateOptions = {
  type EleMountOptions (line 35) | type EleMountOptions = {
  type EleEventOptions (line 43) | type EleEventOptions = EleCreateOptions & EleMountOptions;
  type Ele (line 48) | type Ele<T extends HTMLElement | SVGElement | Text | Comment> =
  function createElementNode (line 61) | function createElementNode<T extends keyof HTMLElementTagNameMap>(
  function createNSElementNode (line 139) | function createNSElementNode<T extends keyof SVGElementTagNameMap>(
  function handleProps (line 216) | function handleProps(
  function handleAttributes (line 237) | function handleAttributes(
  function handleEventOptions (line 256) | function handleEventOptions(option: string[]) {
  function handleAttribute (line 273) | function handleAttribute(
  function handleChildren (line 405) | function handleChildren(
  function handleChangeElement (line 724) | function handleChangeElement(
  function createTextNode (line 760) | function createTextNode(text: any, options?: EleEventOptions): Ele<Text> {
  function mountElement (line 788) | function mountElement<T extends HTMLElement | SVGElement | Text | Comment>(
  function $$ (line 807) | function $$<T extends Element = HTMLElement>(
  function $_ (line 819) | function $_<T extends Element = HTMLElement>(
  function createElementBlock (line 848) | function createElementBlock(

FILE: src/utils/log.ts
  function log (line 7) | function log(...text: any[]) {
  function error (line 15) | function error(...text: any[]) {
  function info (line 23) | function info(...text: any[]) {
  function printColor (line 32) | function printColor(color: string, ...text: any[]) {

FILE: src/utils/push.ts
  type TemplateType (line 6) | type TemplateType = 'html' | 'txt' | 'json' | 'markdown' | 'cloudMonitor';
  type PushOptions (line 11) | type PushOptions = {
  type ModalOptions (line 22) | type ModalOptions = {
  type ModalType (line 34) | type ModalType = 'info' | 'warn' | 'fail' | 'success';
  function getProgressHTML (line 42) | function getProgressHTML(title: string, current: number, total: number) {
  function getHighlightHTML (line 80) | function getHighlightHTML(text: string | number) {
  function getImgHTML (line 89) | function getImgHTML(src: string) {
  function createModal (line 113) | function createModal(options: ModalOptions) {
  function pushMessage (line 270) | async function pushMessage(options: PushOptions) {
  function pushModal (line 280) | async function pushModal(

FILE: src/utils/random.ts
  type Point (line 4) | type Point = { x: number; y: number };
  type Bounds (line 9) | type Bounds = { x: number; y: number; width: number; height: number };
  function createRandomPoint (line 15) | function createRandomPoint(bounds: Bounds): Point {
  function createRandomPath (line 35) | function createRandomPath(start: Point, end: Point, steps: number) {
  function generateNumAsChar (line 64) | function generateNumAsChar(): string {
  function generateUpperAsChar (line 71) | function generateUpperAsChar(): string {
  function generateLowerAsChar (line 78) | function generateLowerAsChar(): string {
  function generateMix (line 86) | function generateMix(length: number = 6): string {

FILE: src/utils/time.ts
  function formatDateNum (line 6) | function formatDateNum(num: number) {
  function formatDateTime (line 19) | function formatDateTime(time: Date | string | number = Date.now()) {
  function isLate (line 61) | function isLate({ hour, minute }: { hour: number; minute: number }) {
  function isNow (line 74) | function isNow({ hour, minute }: { hour: number; minute: number }) {

FILE: src/utils/utils.ts
  function setCookie (line 11) | function setCookie(
  function getCookie (line 30) | function getCookie(name: string) {
  function delCookie (line 51) | function delCookie(name: string, domain: string) {
  function debounce (line 65) | function debounce<T extends (...args: any) => any>(callback: T, delay: n...
  function hasMobile (line 81) | function hasMobile() {
  function sleep (line 103) | function sleep(time: number) {
  function studyPauseLock (line 120) | function studyPauseLock(callback?: (msg: boolean) => void) {
  function load (line 154) | function load(

FILE: tech-study.js
  function initLogo (line 172) | function initLogo() {
  function initTaskConfig (line 178) | function initTaskConfig() {
  function initSettings (line 207) | function initSettings() {
  function initFontSize (line 236) | function initFontSize() {
  function initMaxRead (line 253) | function initMaxRead() {
  function initMaxWatch (line 265) | function initMaxWatch() {
  function initThemeColor (line 277) | function initThemeColor() {
  function renderTip (line 305) | function renderTip() {
  function renderExamBtn (line 353) | async function renderExamBtn() {
  function renderPanel (line 364) | async function renderPanel() {
  function renderFrame (line 373) | function renderFrame() {
  function getAnswer (line 383) | async function getAnswer(question) {
  function saveAnswer (line 419) | async function saveAnswer(question, answer) {
  function getNewsList (line 452) | async function getNewsList() {
  function getVideoList (line 471) | async function getVideoList() {
  function getExamPaper (line 490) | async function getExamPaper(pageNo) {
  function generateQRCode (line 516) | async function generateQRCode() {
  function loginWithQRCode (line 536) | async function loginWithQRCode(qrCode) {
  function getSign (line 564) | async function getSign() {
  function secureCheck (line 586) | async function secureCheck(data) {
  function pushPlus (line 609) | async function pushPlus(token, title, content, template, toToken) {
  function getUserInfo (line 643) | async function getUserInfo() {
  function getTotalScore (line 660) | async function getTotalScore() {
  function getTodayScore (line 679) | async function getTodayScore() {
  function getTaskList (line 698) | async function getTaskList() {
  constant URL_CONFIG (line 734) | const URL_CONFIG = {
  constant API_CONFIG (line 747) | const API_CONFIG = {
  function triggerRef (line 882) | function triggerRef(target, newVal, oldVal) {
  function trigger (line 948) | function trigger(target, key, newVal, oldVal) {
  class Ref (line 982) | class Ref {
    method constructor (line 987) | constructor(val, shallow = false) {
    method toJSON (line 1019) | toJSON() {
  function createElementNode (line 1353) | function createElementNode(tagName, props, attrs, children, options) {
  function createNSElementNode (line 1415) | function createNSElementNode(tagName, props, attrs, children, options) {
  function handleProps (line 1476) | function handleProps(ele, props) {
  function handleAttributes (line 1493) | function handleAttributes(ele, attrs, subscribe, unsubscribe) {
  function handleEventOptions (line 1506) | function handleEventOptions(option) {
  function handleAttribute (line 1522) | function handleAttribute(ele, key, value, subscribe, unsubscribe) {
  function handleChildren (line 1648) | function handleChildren(ele, children, subscribe, unsubscribe) {
  function handleChangeElement (line 1913) | function handleChangeElement(newEle, oldEle, comment, subscribe, unsubsc...
  function createTextNode (line 1941) | function createTextNode(text, options) {
  function mountElement (line 1968) | function mountElement(eleOptions, parent = document.body) {
  function $$ (line 1983) | function $$(selector, parent = document) {
  function $_ (line 1991) | function $_(selector, parent = document, timeout) {
  function createElementBlock (line 2015) | function createElementBlock(eles) {
  function log (line 2026) | function log(...text) {
  function error (line 2033) | function error(...text) {
  function info (line 2040) | function info(...text) {
  function printColor (line 2048) | function printColor(color, ...text) {
  function getProgressHTML (line 2060) | function getProgressHTML(title, current, total) {
  function getHighlightHTML (line 2098) | function getHighlightHTML(text) {
  function getImgHTML (line 2107) | function getImgHTML(src) {
  function createModal (line 2131) | function createModal(options) {
  function pushMessage (line 2280) | async function pushMessage(options) {
  function pushModal (line 2290) | async function pushModal(options, fromToken, toToken) {
  function createRandomPoint (line 2311) | function createRandomPoint(bounds) {
  function createRandomPath (line 2330) | function createRandomPath(start, end, steps) {
  function generateNumAsChar (line 2357) | function generateNumAsChar() {
  function generateUpperAsChar (line 2364) | function generateUpperAsChar() {
  function generateLowerAsChar (line 2371) | function generateLowerAsChar() {
  function generateMix (line 2379) | function generateMix(length = 6) {
  function formatDateNum (line 2402) | function formatDateNum(num) {
  function formatDateTime (line 2414) | function formatDateTime(time = Date.now()) {
  function isLate (line 2454) | function isLate({ hour, minute }) {
  function isNow (line 2466) | function isNow({ hour, minute }) {
  function setCookie (line 2480) | function setCookie(name, value, expires, domain) {
  function getCookie (line 2493) | function getCookie(name) {
  function delCookie (line 2513) | function delCookie(name, domain) {
  function debounce (line 2526) | function debounce(callback, delay) {
  function hasMobile (line 2541) | function hasMobile() {
  function sleep (line 2558) | function sleep(time) {
  function studyPauseLock (line 2574) | function studyPauseLock(callback) {
  function load (line 2607) | function load(match, callback) {
  function getNextButton (line 2788) | function getNextButton() {
  function handleSlideVerify (line 2808) | function handleSlideVerify() {
  function handleChoiceBtn (line 2951) | function handleChoiceBtn(answers) {
  function handleSingleChoiceRand (line 3005) | function handleSingleChoiceRand() {
  function handleMutiplyChoiceRand (line 3021) | function handleMutiplyChoiceRand() {
  function handleBlankInputRand (line 3083) | async function handleBlankInputRand() {
  function examPauseLock (line 3102) | function examPauseLock(callback) {
  function doingExam (line 3140) | async function doingExam(type) {
  function doExamPractice (line 3491) | async function doExamPractice() {
  function doExamPaper (line 3521) | async function doExamPaper() {
  function initExam (line 3551) | async function initExam() {
  function findExamPaper (line 3563) | async function findExamPaper() {
  function initMainListener (line 3597) | function initMainListener() {
  function initChildListener (line 3611) | function initChildListener() {
  function openFrame (line 3627) | async function openFrame(url, title) {
  function closeFrame (line 3650) | function closeFrame() {
  function handleCloseFrame (line 3664) | function handleCloseFrame() {
  function waitFrameClose (line 3672) | function waitFrameClose() {
  function waitFrameLoaded (line 3684) | function waitFrameLoaded(iframe) {
  function openWin (line 3694) | function openWin(url) {
  function closeWin (line 3704) | function closeWin() {
  function handleCloseWin (line 3710) | function handleCloseWin() {
  function waitWinClose (line 3724) | function waitWinClose(newPage) {
  function closeTaskWin (line 3734) | function closeTaskWin() {
  function handleCloseTaskWin (line 3746) | function handleCloseTaskWin() {
  function waitTaskWin (line 3758) | async function waitTaskWin(url, title) {
  function getQRCode (line 3788) | async function getQRCode() {
  function checkQRCode (line 3808) | async function checkQRCode(code) {
  function tryLogin (line 3835) | async function tryLogin(checkCode) {
  function handleLogin (line 3852) | async function handleLogin() {
  function handleLogout (line 3971) | function handleLogout() {
  function handleNews (line 4005) | async function handleNews() {
  function handleVideo (line 4023) | async function handleVideo() {
  function reading (line 4074) | async function reading(type) {
  function getNews (line 4170) | async function getNews() {
  function getVideos (line 4204) | async function getVideos() {
  function readNews (line 4239) | async function readNews() {
  function watchVideo (line 4289) | async function watchVideo() {
  function refreshScheduleTask (line 4344) | async function refreshScheduleTask() {
  function clearScheduleTask (line 4381) | function clearScheduleTask() {
  function createTip (line 4387) | function createTip(text, delay = 2, countShow = false, callback) {
  function refreshUserInfo (line 4491) | async function refreshUserInfo() {
  function refreshScoreInfo (line 4519) | async function refreshScoreInfo() {
  function refreshTaskList (line 4542) | async function refreshTaskList() {
  function Tip (line 4587) | function Tip({ text, count, show, delayShow, countShow, callback, }) {
  function Hr (line 4621) | function Hr({ text }) {
  function Select (line 4630) | function Select({ data, maxlength, placeholder = '', onchange, onblur, v...
  function ExamBtn (line 4735) | function ExamBtn() {
  function Frame (line 4787) | function Frame() {
  function LoginItem (line 4959) | function LoginItem() {
  function InfoItem (line 4998) | function InfoItem() {
  function ScoreItem (line 5043) | function ScoreItem() {
  function NormalItem (line 5123) | function NormalItem({ title, tip, checked, onchange, }) {
  function TaskItem (line 5147) | function TaskItem({ title, tip, checked, currentScore, dayMaxScore, onch...
  function TaskList (line 5184) | function TaskList() {
  function TaskBtn (line 5243) | function TaskBtn() {
  function ScheduleList (line 5433) | function ScheduleList() {
  function TimeInput (line 5513) | function TimeInput({ hour, minute, onchange, }) {
  function SettingsPanel (line 5576) | function SettingsPanel({ show }) {
  function Panel (line 5926) | function Panel() {
Condensed preview — 56 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (972K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 47,
    "preview": "# These are supported funding model platforms\n\n"
  },
  {
    "path": ".gitignore",
    "chars": 12,
    "preview": "node_modules"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2022 Xu22Web\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.md",
    "chars": 3032,
    "preview": "# tech-study-js\n\n### 原仓库\n\n> https://github.com/TechXueXi/techxuexi-js\n\n基于原作者的原仓库,自行改进和完善\n\n### 描述 Description\n\n- 灵活且貌似轻量的"
  },
  {
    "path": "bin/index.ts",
    "chars": 9864,
    "preview": "import chalk from 'chalk';\nimport fs from 'fs';\nimport ora from 'ora';\nimport path from 'path';\nimport rollup from 'roll"
  },
  {
    "path": "cache.json",
    "chars": 207896,
    "preview": "{\"src\\\\api\\\\answer.ts\":{\"timeStamp\":\"2023-03-07T02:25:45.829Z\",\"data\":\"/* 答案 API */\\r\\n/**\\r\\n * @description 获取答案\\r\\n *"
  },
  {
    "path": "package.json",
    "chars": 770,
    "preview": "{\n  \"name\": \"tech-study\",\n  \"version\": \"1.0.0\",\n  \"description\": \"a flexible and light tampermonkey plugin for xuexiqian"
  },
  {
    "path": "src/api/answer.ts",
    "chars": 1814,
    "preview": "import API_CONFIG from '../config/api';\nimport { log } from '../utils/log';\n/* 答案 API */\n\n/**\n * @description 获取答案\n */\na"
  },
  {
    "path": "src/api/data.ts",
    "chars": 1520,
    "preview": "import API_CONFIG from '../config/api';\nimport { NewsVideoList } from '../types';\n/* 数据 API */\n\n/**\n * @description 获取新闻"
  },
  {
    "path": "src/api/login.ts",
    "chars": 1971,
    "preview": "import API_CONFIG from '../config/api';\n\n/**\n * @description 生成二维码\n */\nasync function generateQRCode() {\n  try {\n    // "
  },
  {
    "path": "src/api/push.ts",
    "chars": 831,
    "preview": "import API_CONFIG from '../config/api';\n/* 推送 API */\n\n/**\n * @description 推送\n */\nasync function pushPlus(\n  token: strin"
  },
  {
    "path": "src/api/user.ts",
    "chars": 1596,
    "preview": "import API_CONFIG from '../config/api';\n\n/**\n * @description 用户信息\n */\ntype UserInfo = {\n  avatarMediaUrl?: string;\n  nic"
  },
  {
    "path": "src/component/ExamBtn.ts",
    "chars": 1614,
    "preview": "import { examPause, settings } from '../shared';\nimport { SettingType } from '../types';\nimport { watchEffect, watchEffe"
  },
  {
    "path": "src/component/Frame.ts",
    "chars": 10769,
    "preview": "import { closeFrame, closeTaskWin, closeWin } from '../controller/frame';\nimport { frame, login, running, settings, task"
  },
  {
    "path": "src/component/Hr.ts",
    "chars": 543,
    "preview": "import { createElementNode, createTextNode } from '../utils/element';\n\n/**\n * @description 分隔符\n * @returns\n */\nfunction "
  },
  {
    "path": "src/component/InfoItem.ts",
    "chars": 2249,
    "preview": "import { handleLogout } from '../controller/login';\nimport { refreshUserInfo } from '../controller/user';\nimport { login"
  },
  {
    "path": "src/component/LoginItem.ts",
    "chars": 1896,
    "preview": "import { handleLogin } from '../controller/login';\nimport { login, loginQRCodeShow, settings } from '../shared';\nimport "
  },
  {
    "path": "src/component/NoramlItem.ts",
    "chars": 914,
    "preview": "import { createElementNode, createTextNode } from '../utils/element';\n\n/**\n * @description 设置普通项\n * @returns\n */\nfunctio"
  },
  {
    "path": "src/component/Panel.ts",
    "chars": 15627,
    "preview": "import { doExamPaper } from '../controller/exam';\nimport { createTip } from '../controller/tip';\nimport { frame, login, "
  },
  {
    "path": "src/component/ScheduleList.ts",
    "chars": 7723,
    "preview": "import { refreshScheduleTask } from '../controller/schedule';\nimport { createTip } from '../controller/tip';\nimport { sc"
  },
  {
    "path": "src/component/ScoreItem.ts",
    "chars": 5543,
    "preview": "import { refreshScoreInfo } from '../controller/user';\nimport { login, taskConfig, todayScore, totalScore } from '../sha"
  },
  {
    "path": "src/component/Select.ts",
    "chars": 5088,
    "preview": "import {\n  reactive,\n  Ref,\n  ref,\n  shallowRef,\n  watchEffectRef,\n  watchRef,\n  watch,\n} from '../utils/composition';\ni"
  },
  {
    "path": "src/component/SettingsPanel.ts",
    "chars": 17631,
    "preview": "import { version } from '../config/version';\nimport { clearScheduleTask, refreshScheduleTask } from '../controller/sched"
  },
  {
    "path": "src/component/TaskBtn.ts",
    "chars": 7353,
    "preview": "import { doExamPractice } from '../controller/exam';\nimport { closeFrame } from '../controller/frame';\nimport { readNews"
  },
  {
    "path": "src/component/TaskItem.ts",
    "chars": 2301,
    "preview": "import { Ref, watchEffectRef } from '../utils/composition';\nimport { createElementNode, createTextNode } from '../utils/"
  },
  {
    "path": "src/component/TaskList.ts",
    "chars": 2394,
    "preview": "import { createTip } from '../controller/tip';\nimport { refreshTaskList } from '../controller/user';\nimport { login, tas"
  },
  {
    "path": "src/component/TimeInput.ts",
    "chars": 2121,
    "preview": "import { Ref, ref, watchEffectRef } from '../utils/composition';\nimport { createElementNode, createTextNode } from '../u"
  },
  {
    "path": "src/component/Tip.ts",
    "chars": 1502,
    "preview": "import { Ref, watchEffectRef, watchRef } from '../utils/composition';\nimport { createElementNode, createTextNode } from "
  },
  {
    "path": "src/config/api.ts",
    "chars": 1700,
    "preview": "/**\n * @description api配置\n */\nconst API_CONFIG = {\n  // 用户信息\n  userInfo: 'https://pc-api.xuexi.cn/open/api/user/info',\n "
  },
  {
    "path": "src/config/compile.ts",
    "chars": 708,
    "preview": "import ts from 'typescript';\nimport { ModuleFormat } from 'rollup';\nimport terser from '@rollup/plugin-terser';\n/**\n * @"
  },
  {
    "path": "src/config/script.ts",
    "chars": 1604,
    "preview": "import { version } from './version';\n\n/**\n * @description 脚本配置\n */\nconst SCRIPT_CONFIG = {\n  /**\n   * @description 脚本名\n "
  },
  {
    "path": "src/config/task.ts",
    "chars": 342,
    "preview": "/* task·配置 */\n/**\n * @description 单次最大新闻数\n */\nconst maxNewsNum = 6;\n/**\n * @description 单次最大视频数\n */\nconst maxVideoNum = "
  },
  {
    "path": "src/config/url.ts",
    "chars": 350,
    "preview": "/**\n * @description url配置\n */\nconst URL_CONFIG = {\n  // 主页正则\n  home: /^https\\:\\/\\/www\\.xuexi\\.cn(\\/(index\\.html)?)?$/,\n "
  },
  {
    "path": "src/config/version.ts",
    "chars": 74,
    "preview": "/**\n * @description 版本号\n */\nconst version = '1.7.5';\n\nexport { version };\n"
  },
  {
    "path": "src/controller/exam.ts",
    "chars": 21856,
    "preview": "import { getAnswer, saveAnswer } from '../api/answer';\nimport { getExamPaper } from '../api/data';\nimport URL_CONFIG fro"
  },
  {
    "path": "src/controller/frame.ts",
    "chars": 3864,
    "preview": "import URL_CONFIG from '../config/url';\nimport { frame, id, page, settings } from '../shared';\nimport { SettingType } fr"
  },
  {
    "path": "src/controller/login.ts",
    "chars": 6164,
    "preview": "import {\n  generateQRCode,\n  getSign,\n  loginWithQRCode,\n  secureCheck,\n} from '../api/login';\nimport API_CONFIG from '."
  },
  {
    "path": "src/controller/readAndWatch.ts",
    "chars": 8720,
    "preview": "import { getNewsList, getVideoList } from '../api/data';\nimport { maxNewsNum, maxVideoNum } from '../config/task';\nimpor"
  },
  {
    "path": "src/controller/schedule.ts",
    "chars": 1274,
    "preview": "import { login, scheduleList } from '../shared';\nimport { log } from '../utils/log';\nimport { isLate, isNow } from '../u"
  },
  {
    "path": "src/controller/tip.ts",
    "chars": 2370,
    "preview": "import { Tip } from '../component/Tip';\nimport { ref } from '../utils/composition';\nimport { $$, mountElement } from '.."
  },
  {
    "path": "src/controller/user.ts",
    "chars": 3014,
    "preview": "import {\n  getTaskList,\n  getTodayScore,\n  getTotalScore,\n  getUserInfo,\n} from '../api/user';\nimport { login, taskConfi"
  },
  {
    "path": "src/css/index.css",
    "chars": 18538,
    "preview": "* {\n  -webkit-tap-highlight-color: transparent;\n}\n:root {\n  --themeColor: #fa3333;\n  --scale: 1;\n  font-size: calc(10px "
  },
  {
    "path": "src/index.js",
    "chars": 210769,
    "preview": "const css = '* {  -webkit-tap-highlight-color: transparent;}:root {  --themeColor: #fa3333;  --scale: 1;  font-size: cal"
  },
  {
    "path": "src/index.ts",
    "chars": 9355,
    "preview": "import {} from './api/answer';\nimport {} from './api/data';\nimport {} from './api/login';\nimport {} from './api/push';\ni"
  },
  {
    "path": "src/shared/index.ts",
    "chars": 2976,
    "preview": "/* 变量 */\n\nimport { Schedule, Settings, TaskStatusType, TaskType } from '../types';\nimport { reactive, ref, shallowReacti"
  },
  {
    "path": "src/types/index.ts",
    "chars": 886,
    "preview": "/**\n * @description 任务类型\n */\nenum TaskType {\n  LOGIN,\n  READ,\n  WATCH,\n  PRACTICE,\n}\n\n/**\n * @description 设置类型\n */\nenum "
  },
  {
    "path": "src/utils/composition.ts",
    "chars": 12296,
    "preview": "// 当前订阅\nlet currentSub: ((newVal?: any, oldVal?: any) => any) | undefined;\n\n// 订阅\nconst subscription = new WeakMap<\n  ob"
  },
  {
    "path": "src/utils/element.ts",
    "chars": 20520,
    "preview": "import { isRef, ref, Ref, watch, watchEffect, watchRef } from './composition';\n\ntype BaseEleChild = Ele<HTMLElement | SV"
  },
  {
    "path": "src/utils/log.ts",
    "chars": 742,
    "preview": "import { formatDateTime } from './time';\n\n/**\n * @description 打印日志\n * @param text\n */\nfunction log(...text: any[]) {\n  p"
  },
  {
    "path": "src/utils/push.ts",
    "chars": 7125,
    "preview": "import { pushPlus } from '../api/push';\nimport { formatDateTime } from './time';\n/**\n * @description 消息模板类型\n */\ntype Tem"
  },
  {
    "path": "src/utils/random.ts",
    "chars": 2162,
    "preview": "/**\n * @description 点\n */\ntype Point = { x: number; y: number };\n\n/**\n * @description 范围\n */\ntype Bounds = { x: number; "
  },
  {
    "path": "src/utils/time.ts",
    "chars": 1999,
    "preview": "/**\n * @description 格式化日期时间数字\n * @param num\n * @returns\n */\nfunction formatDateNum(num: number) {\n  return num < 10 ? `0"
  },
  {
    "path": "src/utils/utils.ts",
    "chars": 3657,
    "preview": "import { log } from './log';\n\n/* 工具函数 */\n\n/**\n * @description 设置cookie\n * @param name\n * @param value\n * @param expires\n"
  },
  {
    "path": "tech-study.js",
    "chars": 211906,
    "preview": "// ==UserScript==\n// @name   不学习何以强国\n// @namespace   http://tampermonkey.net/\n// @version   1.7.5\n// @description   有趣的 "
  },
  {
    "path": "tsconfig.json",
    "chars": 11084,
    "preview": "{\n  \"compilerOptions\": {\n    /* Visit https://aka.ms/tsconfig to read more about this file */\n\n    /* Projects */\n    //"
  },
  {
    "path": "types/global.d.ts",
    "chars": 142,
    "preview": "declare global {\n  module '*?raw' {\n    const text: string;\n    export default text;\n  }\n  function md5(value: string): "
  }
]

About this extraction

This page contains the full source code of the Xu22Web/tech-study-js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 56 files (861.2 KB), approximately 243.7k tokens, and a symbol index with 429 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!