diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5bd64e --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +/logs/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +src/main/resources/static/favicon.ico +src/main/resources/static/index.html +src/main/resources/static/assets/401-88639a9f.js +src/main/resources/static/assets/401-a61ddb94.gif +src/main/resources/static/assets/401-d244ab29.css +src/main/resources/static/assets/404_cloud-98e7ac66.png +src/main/resources/static/assets/404-7ca1fe28.css +src/main/resources/static/assets/404-7d365cf6.js +src/main/resources/static/assets/404-538aa4d7.png +src/main/resources/static/assets/BarChart-4fa92581.js +src/main/resources/static/assets/BarChart.vue_vue_type_script_setup_true_lang-2e88c556.js +src/main/resources/static/assets/editor-1fcae90e.js +src/main/resources/static/assets/editor-501cf061.css +src/main/resources/static/assets/FunnelChart-54cd9c1c.js +src/main/resources/static/assets/index-0c0fc4d5.js +src/main/resources/static/assets/index-0c6de5c4.css +src/main/resources/static/assets/index-0e353610.js +src/main/resources/static/assets/index-0fdbb0c3.css +src/main/resources/static/assets/index-2a2e686f.js +src/main/resources/static/assets/index-2d8a94de.js +src/main/resources/static/assets/index-3c8b576d.js +src/main/resources/static/assets/index-3e4e0c0c.css +src/main/resources/static/assets/index-3ea31a03.css +src/main/resources/static/assets/index-4a40522c.js +src/main/resources/static/assets/index-4af6c095.js +src/main/resources/static/assets/index-4b2c54eb.css +src/main/resources/static/assets/index-4e9a4a3c.css +src/main/resources/static/assets/index-4e36f11e.css +src/main/resources/static/assets/index-5b909b30.js +src/main/resources/static/assets/index-6cfbc6e9.js +src/main/resources/static/assets/index-6d369684.js +src/main/resources/static/assets/index-9cdf102e.js +src/main/resources/static/assets/index-013c92bf.css +src/main/resources/static/assets/index-45b24ee3.js +src/main/resources/static/assets/index-58b7edbc.css +src/main/resources/static/assets/index-97e2e5d7.js +src/main/resources/static/assets/index-336eb0cd.js +src/main/resources/static/assets/index-1124a30a.js +src/main/resources/static/assets/index-5736aae9.js +src/main/resources/static/assets/index-23665d45.js +src/main/resources/static/assets/index-35744cd0.css +src/main/resources/static/assets/index-49408471.js +src/main/resources/static/assets/index-a7bce641.css +src/main/resources/static/assets/index-adef366f.js +src/main/resources/static/assets/index-dd0c8cf0.css +src/main/resources/static/assets/index-dd267244.js +src/main/resources/static/assets/index-de40e6e9.css +src/main/resources/static/assets/index-f9a887b2.css +src/main/resources/static/assets/index1-f104bedb.js +src/main/resources/static/assets/index2-8960589f.js +src/main/resources/static/assets/indicator-5b15d0d1.png +src/main/resources/static/assets/lbcz_sc-0ed76926.js +src/main/resources/static/assets/lbcz_td-b5984317.js +src/main/resources/static/assets/lbcz_xg-aaaefba0.js +src/main/resources/static/assets/logo-03d6d6da.png +src/main/resources/static/assets/page.vue_vue_type_script_setup_true_lang-b61c9f0c.js +src/main/resources/static/assets/personalCenter-035ae2fa.js +src/main/resources/static/assets/personalCenter-ad68cb91.css +src/main/resources/static/assets/PieChart-24d7f015.js +src/main/resources/static/assets/PieChart.vue_vue_type_script_setup_true_lang-d4bdc783.js +src/main/resources/static/assets/RadarChart-f3f7d950.js +src/main/resources/static/assets/RadarChart.vue_vue_type_script_setup_true_lang-c1ab7800.js +src/main/resources/static/assets/resize-76420810.js +src/main/resources/static/assets/rsaEncrypt-96cab0ea.js +src/main/resources/static/assets/sortable.esm-be94e56d.js +src/main/resources/static/assets/tagsView-23de2998.js +src/main/resources/static/assets/top_tx-3cab94c6.png +src/main/resources/static/assets/u287-9a3328bc.gif +src/main/resources/static/assets/uploader-769f4f98.js +src/main/resources/static/assets/uploader-ccfd78f9.css diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..5c7d4f1 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# Basic pre-commit hook initialized at repo root. +# Add your checks below (lint/test/etc.). + +echo "husky pre-commit: no checks configured yet" +exit 0 \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..02d942d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "PlatformApplication", + "request": "launch", + "mainClass": "com.yfd.platform.PlatformApplication", + "projectName": "" + }, + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ca2a05f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:1 +FROM eclipse-temurin:17-jre-alpine + +LABEL maintainer="platform-team" + +ENV TZ=Asia/Shanghai \ + LANG=zh_CN.UTF-8 \ + JAVA_OPTS="-Dfile.encoding=UTF-8" \ + SPRING_PROFILES_ACTIVE=dev + +RUN apk add --no-cache tzdata && \ + ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +WORKDIR /app + +# 注意:在构建镜像前先执行 `mvn -DskipTests package` 生成 WAR +COPY target/platform-1.0.war /app/app.war + +EXPOSE 8093 + +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.war --spring.profiles.active=$SPRING_PROFILES_ACTIVE"] \ No newline at end of file diff --git a/docs/前后端整合.md b/docs/前后端整合.md new file mode 100644 index 0000000..4bab797 --- /dev/null +++ b/docs/前后端整合.md @@ -0,0 +1,52 @@ +# 前后端一体化构建与打包说明 + +本文档说明如何在 Maven 构建流程中,先编译前端(pnpm),再编译后端(Java),并在 `package` 阶段将两者一起打包,使生成的可运行 `jar/war` 直接可以访问前端首页 `index.html`。 + +## 前置要求 +- 已安装 JDK(推荐 17+)和 Maven(3.6.3+)。 +- 已安装 Node.js(推荐 18+)与 pnpm(全局):`npm i -g pnpm`。 +- 当前项目结构: + - 后端(Spring Boot):`app/pom.xml`,源码在 `app/src/main/java` 与资源在 `app/src/main/resources`。 + - 前端(Vite/Vue):位于 `app/frontend`,构建输出目录默认是 `app/frontend/dist`。 + +## 构建流程概览 +在 `pom.xml` 中集成了以下步骤: +1. `generate-resources` 阶段:进入 `frontend` 目录,执行 `pnpm install --frozen-lockfile` 与 `pnpm run build`。 +2. `process-resources` 阶段:将前端构建产物从 `frontend/dist` 复制到后端资源目录 `src/main/resources/static`。 +3. 随后执行后端 Java 编译与打包,最终产出可运行的 `jar/war`。 + +这样在运行后端时,Spring Boot 会从类路径的 `static` 目录自动提供 `index.html` 作为欢迎页入口(访问根路径 `/` 即可看到前端页面)。 + +## 关键 POM 配置(摘要) +已经在 `app/pom.xml` 的 `` 中加入: +- `exec-maven-plugin`:在 Java 编译前执行 pnpm 安装与构建。 +- `maven-resources-plugin`:复制前端构建产物到 `src/main/resources/static`。 + +无需手动改动目录,只需按下述命令执行即可。 + +## 常用命令 +- 开发运行(跳过测试):`mvn -DskipTests spring-boot:run` + - 如需指定开发配置:`mvn -DskipTests -Dspring-boot.run.profiles=dev spring-boot:run` +- 构建打包(跳过测试):`mvn -DskipTests package` + - 构建过程中会自动执行前端构建并拷贝到后端资源。 + +## 运行与访问 +- 运行可执行包(以 `war` 为例,`artifactId=platform`,`version=1.0`): + - 默认运行:`java -jar target/platform-1.0.war` + - 指定 dev 配置:`java -Dspring.profiles.active=dev -jar target/platform-1.0.war` +- 访问: + - 若启用 `dev` 配置并端口为 `8093`:`http://localhost:8093/` + - 若使用默认端口(例如 `18080`):`http://localhost:18080/` + +> 说明:Spring Boot 会自动将 `classpath:/static/index.html` 作为欢迎页,访问根路径即可进入前端入口页面。 + +## 注意事项 +- 请确保 `pnpm` 可在构建机/开发机的环境变量中直接调用。 +- 若前端构建输出目录(`dist`)有自定义,请同步更新 POM 中的复制路径。 +- 如前端使用路由的 `history` 模式并期望后端兜底到 `index.html`,可按需添加后端控制器或使用前端服务器侧配置(当前方案以静态欢迎页为主)。 + +## 故障排查 +- 构建失败(找不到 `pnpm`):确认已安装并可在命令行执行 `pnpm -v`。 +- 页面乱码:已在 POM 配置统一 JVM 编码为 `UTF-8`;在 Windows PowerShell 终端也应设置为 `UTF-8`。 + +完成上述配置后,只需使用 Maven 的标准命令,即可实现前后端一体化构建和打包运行。 \ No newline at end of file diff --git a/docs/技术文档.md b/docs/技术文档.md new file mode 100644 index 0000000..57b8448 --- /dev/null +++ b/docs/技术文档.md @@ -0,0 +1,161 @@ +# 项目技术文档 + +> 项目:ProjectFrameWork2025(模块:`app` / Java 后端) +> 运行环境:Windows,Java 21(兼容 17+),Spring Boot 3.x + +## 概述 +- 平台型后端服务,采用 `Spring Boot 3.x`,区分 `dev` / `server` 两种运行配置。 +- 数据访问使用 `MyBatis-Plus 3.5.6` 与 `MyBatis 3.5.16`,连接池为 `Druid`。 +- 任务调度使用 `Quartz 2.3.2`;API 文档采用 `springdoc-openapi` / Swagger UI。 +- 支持 WAR 包运行,亦可容器化部署;默认开发端口 `8093`(`server` 可使用 `8090`)。 + +## 目录结构 +- 仓库根(当前工作目录):`D:\Trae_space\ProjectFrameWork2025\app` +- 主要结构: +``` +app/ +├── .gitignore +├── Dockerfile +├── frontend/ # 前端说明或资源 +│ └── readme.md +├── pom.xml # Maven 构建管理 +└── src/ + ├── main/ + │ ├── java/ # 业务代码(入口类在 com.yfd.platform.*) + │ └── resources/ # 配置与静态资源 + └── test/ + └── java/ # 测试代码 +``` + +## 快速开始 +- 前置要求: + - 安装 `JDK 21`(兼容 17+),`Maven 3.9+`,`Git`。 + - Windows 终端执行 `chcp 65001`,确保 UTF-8 编码输出。 +- 构建后端: + - `mvn clean package -DskipTests` +- 本地运行(dev): + - `java -jar target/platform-1.0.war --spring.profiles.active=dev` +- 运行(server): + - `java -jar target/platform-1.0.war --spring.profiles.active=server` +- API 文档: + - 默认访问 `http://localhost:8093/swagger-ui/index.html`(以实际配置为准) + +## 配置说明 +- Profile 切换: + - 通过 `--spring.profiles.active=` 激活环境。 +- 关键属性: + - `file-space.system`:文件根路径,需在激活的 profile 中配置。 + - `spring.datasource.druid.*`:数据库连接参数与池化配置。 + - `server.port`:端口(`dev` 默认 8093,`server` 可使用 8090)。 +- VS Code/终端编码建议: + - 启动参数加入 `-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8`。 + - 终端执行 `chcp 65001`,PowerShell 输出设置为 UTF-8。 + +## 依赖与版本 +- `Spring Boot 3.x` +- `MyBatis-Plus 3.5.6`(依赖 `MyBatis 3.5.16+`,需有 `Configuration.parsePendingResultMaps(boolean)`) +- `Druid` 数据源 +- `Quartz 2.3.2` +- `springdoc-openapi` / Swagger UI +- 日志:`Logback`(UTF-8 输出,`logback-spring.xml`) + +## 数据库配置 +- MySQL 连接示例: +```yaml +spring: + datasource: + druid: + master: + url: jdbc:mysql://:3306/?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&useSSL=false&allowPublicKeyRetrieval=true + username: + password: +``` +- 远程授权建议: +```sql +CREATE USER 'appuser'@'%' IDENTIFIED BY 'StrongPassword!'; +GRANT ALL PRIVILEGES ON .* TO 'appuser'@'%'; +FLUSH PRIVILEGES; +``` +- Druid 健壮性: +```yaml +spring: + datasource: + druid: + initial-size: 0 + test-on-borrow: false + test-while-idle: true + validation-query: SELECT 1 +``` + +## 日志 +- 控制台与文件统一 UTF-8 输出: + - 控制台 `ConsoleAppender`,文件 `RollingFileAppender`(按日滚动,保留 30 天)。 + - 推荐日志格式:`%d [%thread] %-5level %logger{50} - %msg%n`。 + +## 定时任务 +- 默认 `RAMJobStore`(非集群、内存存储)。 +- 如需持久化与集群,改用 `JdbcJobStore` 并配置数据源与表结构。 + +## 安全与鉴权 +- 使用 `JWT` 过滤器进行鉴权(如 `jwtAuthenticationTokenFilter`)。 +- 敏感配置(`jwt.secret`、数据库密码)建议通过环境变量或外部密钥管理。 +- 生产环境必须启用 HTTPS 并使用强密钥。 + +## API 文档 +- 启用 `swagger-ui.enabled: true`。 +- 访问路径通常为 `http://:/swagger-ui/index.html`。 + +## Docker 部署 +- `Dockerfile` 已暴露端口 `8093`: + - 构建镜像:`docker build -t projectframework2025-app:latest .` + - 运行(开发环境): + - `docker run -d --name platform-app -p 8093:8093 -e SPRING_PROFILES_ACTIVE=dev projectframework2025-app:latest` + - 运行(服务器环境): + - `docker run -d --name platform-app -p 8090:8090 -e SPRING_PROFILES_ACTIVE=server projectframework2025-app:latest` +- 如需挂载文件空间: + - `-v D:/data/file-space:/data/file-space -e FILE_SPACE_SYSTEM=/data/file-space` + +## 前端 +- 前端说明见 `frontend/readme.md`。如需联调,请统一跨域与鉴权策略。 + +## Git 使用 +- 远程仓库:`http://121.37.111.42:3000/ThbTech/ProjectFrameWork2025.git` +- 主分支:`main` +- 常用命令: + - `git pull`、`git add .`、`git commit -m ""`、`git push` +- 提交署名: + - `git config user.name ""` + - `git config user.email ""` + +## 常见问题 +- 中文乱码: + - 执行 `chcp 65001`,确保 `logback-spring.xml` 使用 UTF-8。 +- `NoSuchMethodError`(MyBatis): + - 升级 `MyBatis` 至 `3.5.16+` 并与 MP 版本匹配。 +- 数据库 `Access denied`: + - 校验账户密码与远程授权;必要时新建业务账户并放开 `3306`。 +- 端口占用/启动失败: + - 检查 `server.port` 与冲突端口;查看应用日志定位根因。 + +## 运维与监控 +- 建议接入 `Spring Boot Actuator`:`/actuator/health`。 +- 配合日志轮转与集中采集(ELK/Vector),区分环境日志路径。 +- 监控数据库与线程池指标(Druid/Quartz)。 + +## CI/CD 建议 +- 在 CI 阶段执行: + - `mvn -B -DskipTests clean package` + - 单元测试与安全扫描(依赖检查、代码质量) +- 在 CD 阶段: + - 推送镜像到私有仓库,环境变量注入敏感信息。 + +## 变更日志(示例模板) +- `feat:` 新增功能说明 +- `fix:` 缺陷修复说明 +- `docs:` 文档更新说明 +- `refactor:` 重构说明 +- `perf:` 性能优化说明 + +--- + +如需扩展专题文档(接口规范、部署拓扑、参数字典等),建议在 `docs/` 目录继续维护并与版本管理同步。 \ No newline at end of file diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 0000000..3c3960b --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] # 表示所有文件适用 +charset = utf-8 # 设置文件字符集为 utf-8 +indent_style = space # 缩进风格(tab | space) +indent_size = 2 # 缩进大小 +end_of_line = lf # 控制换行类型(lf | cr | crlf) +trim_trailing_whitespace = true # 去除行首的任意空白字符 +insert_final_newline = true # 始终在文件末尾插入一个新行 + +[*.md] # 表示仅 md 文件适用以下规则 +max_line_length = off +trim_trailing_whitespace = false \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 0000000..282c646 --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1,8 @@ +## 开发环境 + +# 变量必须以 VITE_ 为前缀才能暴露给外部读取 +NODE_ENV='development' + +VITE_APP_TITLE = '公司开发平台框架' +VITE_APP_PORT = 3000 +VITE_APP_BASE_API = '/dev-api' diff --git a/frontend/.env.production b/frontend/.env.production new file mode 100644 index 0000000..728f41a --- /dev/null +++ b/frontend/.env.production @@ -0,0 +1,6 @@ +## 生产环境 +NODE_ENV='production' + +VITE_APP_TITLE = 'NewFrameWork2023-WEB' +VITE_APP_PORT = 3000 +VITE_APP_BASE_API = '/prod-api' diff --git a/frontend/.env.staging b/frontend/.env.staging new file mode 100644 index 0000000..c192ef8 --- /dev/null +++ b/frontend/.env.staging @@ -0,0 +1,6 @@ +## 模拟环境 +NODE_ENV='staging' + +VITE_APP_TITLE = 'NewFrameWork2023-WEB' +VITE_APP_PORT = 3000 +VITE_APP_BASE_API = '/prod--api' diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 0000000..46d4b17 --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,16 @@ +*.sh +node_modules +*.md +*.woff +*.ttf +.vscode +.idea +dist +/public +/docs +.husky +.local +/bin +.eslintrc.js +prettier.config.js +src/assets \ No newline at end of file diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 0000000..d7878cc --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,32 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true + }, + globals: { + defineProps: 'readonly', + defineEmits: 'readonly', + defineExpose: 'readonly', + DialogType: "readonly", + OptionType: "readonly", + }, + parser: 'vue-eslint-parser', + extends: [ + 'eslint:recommended', + 'plugin:vue/vue3-essential', + 'plugin:@typescript-eslint/recommended' + ], + parserOptions: { + ecmaVersion: 'latest', + parser: '@typescript-eslint/parser', + sourceType: 'module' + }, + plugins: ['vue', '@typescript-eslint'], + rules: { + 'vue/multi-word-component-names': 'off', + '@typescript-eslint/no-empty-function': 'off', // 关闭空方法检查 + '@typescript-eslint/no-explicit-any': 'off', // 关闭any类型的警告 + 'vue/no-v-model-argument': 'off' + } +}; diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..1da66c1 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,17 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock \ No newline at end of file diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..b3db441 --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1,3 @@ +registry=https://registry.npmjs.org +fetch-retries=5 +strict-peer-dependencies=false \ No newline at end of file diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..d251d2e --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,9 @@ +/dist/* +.local +.output.js +/node_modules/** + +**/*.svg +**/*.sh + +/public/* \ No newline at end of file diff --git a/frontend/.prettierrc.js b/frontend/.prettierrc.js new file mode 100644 index 0000000..7a42426 --- /dev/null +++ b/frontend/.prettierrc.js @@ -0,0 +1,36 @@ +/** + * 代码格式化配置 + */ +module.exports = { + // 指定每个缩进级别的空格数 + tabWidth: 2, + // 使用制表符而不是空格缩进行 + useTabs: false, + // 在语句末尾打印分号 + semi: true, + // 使用单引号而不是双引号 + singleQuote: true, + // 更改引用对象属性的时间 可选值"" + quoteProps: 'as-needed', + // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none + trailingComma: 'none', + // 在对象文字中的括号之间打印空格 + bracketSpacing: true, + // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x + arrowParens: 'avoid', + // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 + rangeStart: 0, + rangeEnd: Infinity, + // 指定要使用的解析器,不需要写文件开头的 @prettier + requirePragma: false, + // 不需要自动在文件开头插入 @prettier + insertPragma: false, + // 换行设置 always\never\preserve + proseWrap: 'never', + // 指定HTML文件的全局空格敏感度 css\strict\ignore + htmlWhitespaceSensitivity: 'css', + // Vue文件脚本和样式标签缩进 + vueIndentScriptAndStyle: false, + // 换行符使用 lf 结尾是 可选值"" + endOfLine: 'lf' +}; diff --git a/frontend/LICENSE b/frontend/LICENSE new file mode 100644 index 0000000..2660254 --- /dev/null +++ b/frontend/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 有来开源组织 + +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. diff --git a/frontend/commitlint.config.js b/frontend/commitlint.config.js new file mode 100644 index 0000000..efff054 --- /dev/null +++ b/frontend/commitlint.config.js @@ -0,0 +1,26 @@ +module.exports = { + // 继承的规则 + extends: ['@commitlint/config-conventional'], + // 定义规则类型 + rules: { + // type 类型定义,表示 git 提交的 type 必须在以下类型范围内 + 'type-enum': [ + 2, + 'always', + [ + 'feat', // 新功能 feature + 'fix', // 修复 bug + 'docs', // 文档注释 + 'style', // 代码格式(不影响代码运行的变动) + 'refactor', // 重构(既不增加新功能,也不是修复bug) + 'perf', // 性能优化 + 'test', // 增加测试 + 'chore', // 构建过程或辅助工具的变动 + 'revert', // 回退 + 'build' // 打包 + ] + ], + // subject 大小写不做校验 + 'subject-case': [0] + } +}; diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..c48809f --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,15 @@ + + + + + + + + + 公司开发平台框架 + + +
+ + + diff --git a/frontend/npminstall-debug.log b/frontend/npminstall-debug.log new file mode 100644 index 0000000..05326b7 --- /dev/null +++ b/frontend/npminstall-debug.log @@ -0,0 +1,208 @@ +{ + root: 'D:\\Trae_space\\ProjectFrameWork2025\\app\\frontend', + registry: 'https://registry.npmmirror.com', + pkgs: [], + production: false, + cacheStrict: false, + cacheDir: 'C:\\Users\\13910\\.npminstall_tarball', + env: { + npm_config_registry: 'https://registry.npmmirror.com', + npm_config_argv: '{"remain":[],"cooked":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\13910\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com"],"original":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\13910\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com"]}', + npm_config_user_agent: 'npminstall/7.9.0 npm/? node/v20.19.2 win32 x64', + npm_config_cache: 'C:\\Users\\13910\\.npminstall_tarball', + NODE: 'C:\\Program Files\\nodejs\\node.exe', + npm_node_execpath: 'C:\\Program Files\\nodejs\\node.exe', + npm_execpath: 'C:\\Users\\13910\\AppData\\Roaming\\npm\\node_modules\\cnpm\\node_modules\\npminstall\\bin\\install.js', + npm_config_userconfig: 'C:\\Users\\13910\\.cnpmrc', + npm_config_disturl: 'https://cdn.npmmirror.com/binaries/node', + npm_config_r: 'https://registry.npmmirror.com', + COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com', + EDGEDRIVER_CDNURL: 'https://npmmirror.com/mirrors/edgedriver', + NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node', + NVM_NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node', + PHANTOMJS_CDNURL: 'https://cdn.npmmirror.com/binaries/phantomjs', + CHROMEDRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/chromedriver', + OPERADRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/operadriver', + CYPRESS_DOWNLOAD_PATH_TEMPLATE: 'https://cdn.npmmirror.com/binaries/cypress/${version}/${platform}-${arch}/cypress.zip', + ELECTRON_MIRROR: 'https://cdn.npmmirror.com/binaries/electron/', + ELECTRON_BUILDER_BINARIES_MIRROR: 'https://cdn.npmmirror.com/binaries/electron-builder-binaries/', + SASS_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-sass', + SWC_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-swc', + NWJS_URLBASE: 'https://cdn.npmmirror.com/binaries/nwjs/v', + PUPPETEER_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PUPPETEER_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PUPPETEER_CHROME_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PUPPETEER_CHROME_HEADLESS_SHELL_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PLAYWRIGHT_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/playwright', + SENTRYCLI_CDNURL: 'https://cdn.npmmirror.com/binaries/sentry-cli', + SAUCECTL_INSTALL_BINARY_MIRROR: 'https://cdn.npmmirror.com/binaries/saucectl', + RE2_DOWNLOAD_MIRROR: 'https://cdn.npmmirror.com/binaries/node-re2', + RE2_DOWNLOAD_SKIP_PATH: 'true', + PRISMA_ENGINES_MIRROR: 'https://cdn.npmmirror.com/binaries/prisma', + npm_config_better_sqlite3_binary_host: 'https://cdn.npmmirror.com/binaries/better-sqlite3', + npm_config_keytar_binary_host: 'https://cdn.npmmirror.com/binaries/keytar', + npm_config_sharp_binary_host: 'https://cdn.npmmirror.com/binaries/sharp', + npm_config_sharp_libvips_binary_host: 'https://cdn.npmmirror.com/binaries/sharp-libvips', + npm_config_robotjs_binary_host: 'https://cdn.npmmirror.com/binaries/robotjs', + npm_config_gl_binary_host: 'https://cdn.npmmirror.com/binaries/gl', + RIPGREP_PREBUILT_BINARIES_MIRROR: 'https://registry.npmmirror.com/-/binary/ripgrep-prebuilt', + npm_rootpath: 'D:\\Trae_space\\ProjectFrameWork2025\\app\\frontend', + INIT_CWD: 'D:\\Trae_space\\ProjectFrameWork2025\\app\\frontend' + }, + binaryMirrors: { + ENVS: { + COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com', + EDGEDRIVER_CDNURL: 'https://npmmirror.com/mirrors/edgedriver', + NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node', + NVM_NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node', + PHANTOMJS_CDNURL: 'https://cdn.npmmirror.com/binaries/phantomjs', + CHROMEDRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/chromedriver', + OPERADRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/operadriver', + CYPRESS_DOWNLOAD_PATH_TEMPLATE: 'https://cdn.npmmirror.com/binaries/cypress/${version}/${platform}-${arch}/cypress.zip', + ELECTRON_MIRROR: 'https://cdn.npmmirror.com/binaries/electron/', + ELECTRON_BUILDER_BINARIES_MIRROR: 'https://cdn.npmmirror.com/binaries/electron-builder-binaries/', + SASS_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-sass', + SWC_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-swc', + NWJS_URLBASE: 'https://cdn.npmmirror.com/binaries/nwjs/v', + PUPPETEER_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PUPPETEER_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PUPPETEER_CHROME_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PUPPETEER_CHROME_HEADLESS_SHELL_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing', + PLAYWRIGHT_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/playwright', + SENTRYCLI_CDNURL: 'https://cdn.npmmirror.com/binaries/sentry-cli', + SAUCECTL_INSTALL_BINARY_MIRROR: 'https://cdn.npmmirror.com/binaries/saucectl', + RE2_DOWNLOAD_MIRROR: 'https://cdn.npmmirror.com/binaries/node-re2', + RE2_DOWNLOAD_SKIP_PATH: 'true', + PRISMA_ENGINES_MIRROR: 'https://cdn.npmmirror.com/binaries/prisma', + npm_config_better_sqlite3_binary_host: 'https://cdn.npmmirror.com/binaries/better-sqlite3', + npm_config_keytar_binary_host: 'https://cdn.npmmirror.com/binaries/keytar', + npm_config_sharp_binary_host: 'https://cdn.npmmirror.com/binaries/sharp', + npm_config_sharp_libvips_binary_host: 'https://cdn.npmmirror.com/binaries/sharp-libvips', + npm_config_robotjs_binary_host: 'https://cdn.npmmirror.com/binaries/robotjs', + npm_config_gl_binary_host: 'https://cdn.npmmirror.com/binaries/gl', + RIPGREP_PREBUILT_BINARIES_MIRROR: 'https://registry.npmmirror.com/-/binary/ripgrep-prebuilt' + }, + '@ali/s2': { host: 'https://cdn.npmmirror.com/binaries/looksgood-s2' }, + sharp: { replaceHostFiles: [Array], replaceHostMap: [Object] }, + '@tensorflow/tfjs-node': { + replaceHostFiles: [Array], + replaceHostRegExpMap: [Object], + replaceHostMap: [Object] + }, + cypress: { + host: 'https://cdn.npmmirror.com/binaries/cypress', + newPlatforms: [Object] + }, + 'utf-8-validate': { + host: 'https://cdn.npmmirror.com/binaries/utf-8-validate/v{version}' + }, + xprofiler: { + remote_path: './xprofiler/v{version}/', + host: 'https://cdn.npmmirror.com/binaries' + }, + leveldown: { host: 'https://cdn.npmmirror.com/binaries/leveldown/v{version}' }, + couchbase: { host: 'https://cdn.npmmirror.com/binaries/couchbase/v{version}' }, + gl: { host: 'https://cdn.npmmirror.com/binaries/gl/v{version}' }, + sqlite3: { + host: 'https://cdn.npmmirror.com/binaries/sqlite3', + remote_path: 'v{version}' + }, + '@journeyapps/sqlcipher': { host: 'https://cdn.npmmirror.com/binaries' }, + grpc: { + host: 'https://cdn.npmmirror.com/binaries', + remote_path: '{name}/v{version}' + }, + 'grpc-tools': { host: 'https://cdn.npmmirror.com/binaries' }, + wrtc: { + host: 'https://cdn.npmmirror.com/binaries', + remote_path: '{name}/v{version}' + }, + fsevents: { host: 'https://cdn.npmmirror.com/binaries/fsevents' }, + nodejieba: { host: 'https://cdn.npmmirror.com/binaries/nodejieba' }, + canvas: { + host: 'https://cdn.npmmirror.com/binaries/canvas', + remote_path: 'v{version}' + }, + 'skia-canvas': { host: 'https://cdn.npmmirror.com/binaries/skia-canvas' }, + 'flow-bin': { + replaceHost: 'https://github.com/facebook/flow/releases/download/v', + host: 'https://cdn.npmmirror.com/binaries/flow/v' + }, + 'jpegtran-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/jpegtran-bin' + }, + 'cwebp-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/cwebp-bin' + }, + 'zopflipng-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/zopflipng-bin' + }, + 'optipng-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/optipng-bin' + }, + mozjpeg: { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/mozjpeg-bin' + }, + gifsicle: { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/gifsicle-bin' + }, + 'pngquant-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/pngquant-bin', + replaceHostMap: [Object] + }, + 'pngcrush-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/pngcrush-bin' + }, + 'jpeg-recompress-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/jpeg-recompress-bin' + }, + 'advpng-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/advpng-bin' + }, + 'pngout-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/pngout-bin' + }, + 'jpegoptim-bin': { + replaceHost: [Array], + host: 'https://cdn.npmmirror.com/binaries/jpegoptim-bin' + }, + argon2: { host: 'https://cdn.npmmirror.com/binaries/argon2' }, + 'ali-zeromq': { host: 'https://cdn.npmmirror.com/binaries/ali-zeromq' }, + 'ali-usb_ctl': { host: 'https://cdn.npmmirror.com/binaries/ali-usb_ctl' }, + 'gdal-async': { host: 'https://cdn.npmmirror.com/binaries/node-gdal-async' }, + 'libpg-query': { host: 'https://cdn.npmmirror.com/binaries' } + }, + forbiddenLicenses: null, + flatten: false, + proxy: undefined, + prune: false, + disableFallbackStore: false, + workspacesMap: Map(0) {}, + enableWorkspace: false, + workspaceRoot: 'D:\\Trae_space\\ProjectFrameWork2025\\app\\frontend', + isWorkspaceRoot: true, + isWorkspacePackage: false, + offline: false, + strictSSL: true, + ignoreScripts: false, + foregroundScripts: false, + ignoreOptionalDependencies: false, + detail: false, + forceLinkLatest: false, + trace: false, + engineStrict: false, + registryOnly: false, + client: false, + autoFixVersion: [Function: autoFixVersion] +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..7b20367 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,66 @@ +{ + "name": "NewFrameWork2023-WEB", + "version": "1.2.0", + "scripts": { + "dev": "vite serve --mode development", + "build:prod": "vue-tsc --noEmit && vite build --mode production", + "build:mvn": "vite build --mode production", + "serve": "vite preview", + "lint": "eslint src/**/*.{ts,js,vue} --fix", + "prettier": "prettier --write ." + }, + "dependencies": { + "@element-plus/icons-vue": "^2.0.10", + "@types/js-cookie": "^3.0.2", + "@vueuse/core": "^9.1.1", + "@wangeditor/editor": "^5.0.0", + "@wangeditor/editor-for-vue": "^5.1.10", + "axios": "^1.2.0", + "better-scroll": "^2.4.2", + "default-passive-events": "^2.0.0", + "echarts": "^5.2.2", + "element-plus": "^2.2.27", + "js-base64": "^3.7.5", + "js-cookie": "^3.0.1", + "jsencrypt": "^3.3.2", + "nprogress": "^0.2.0", + "path-browserify": "^1.0.1", + "path-to-regexp": "^6.2.0", + "pinia": "^2.0.12", + "screenfull": "^6.0.0", + "sortablejs": "^1.14.0", + "vue": "^3.2.40", + "vue-i18n": "^9.1.9", + "vue-router": "^4.1.6", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "@commitlint/cli": "^16.2.3", + "@commitlint/config-conventional": "^16.2.1", + "@types/node": "^16.11.7", + "@types/nprogress": "^0.2.0", + "@types/path-browserify": "^1.0.0", + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "@vitejs/plugin-vue": "^4.0.0", + "autoprefixer": "^10.4.13", + "eslint": "^8.14.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-vue": "^8.6.0", + "fast-glob": "^3.2.11", + "husky": "^7.0.4", + "postcss": "^8.4.20", + "prettier": "^2.6.2", + "sass": "^1.53.0", + "tailwindcss": "^3.2.4", + "typescript": "^4.7.4", + "vite": "^4.0.3", + "vite-plugin-svg-icons": "^2.0.1", + "vue-tsc": "^0.35.0" + }, + "repository": "https://gitee.com/youlaiorg/vue3-element-admin.git", + "author": "有来开源组织", + "license": "MIT", + "__npminstall_done": false +} diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml new file mode 100644 index 0000000..f2b2b91 --- /dev/null +++ b/frontend/pnpm-lock.yaml @@ -0,0 +1,6776 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@element-plus/icons-vue': + specifier: ^2.0.10 + version: 2.3.2(vue@3.5.24(typescript@4.9.5)) + '@types/js-cookie': + specifier: ^3.0.2 + version: 3.0.6 + '@vueuse/core': + specifier: ^9.1.1 + version: 9.13.0(vue@3.5.24(typescript@4.9.5)) + '@wangeditor/editor': + specifier: ^5.0.0 + version: 5.1.23 + '@wangeditor/editor-for-vue': + specifier: ^5.1.10 + version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.24(typescript@4.9.5)) + axios: + specifier: ^1.2.0 + version: 1.13.2 + better-scroll: + specifier: ^2.4.2 + version: 2.5.1 + default-passive-events: + specifier: ^2.0.0 + version: 2.0.0 + echarts: + specifier: ^5.2.2 + version: 5.6.0 + element-plus: + specifier: ^2.2.27 + version: 2.11.7(vue@3.5.24(typescript@4.9.5)) + js-base64: + specifier: ^3.7.5 + version: 3.7.8 + js-cookie: + specifier: ^3.0.1 + version: 3.0.5 + jsencrypt: + specifier: ^3.3.2 + version: 3.5.4 + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + path-browserify: + specifier: ^1.0.1 + version: 1.0.1 + path-to-regexp: + specifier: ^6.2.0 + version: 6.3.0 + pinia: + specifier: ^2.0.12 + version: 2.3.1(typescript@4.9.5)(vue@3.5.24(typescript@4.9.5)) + screenfull: + specifier: ^6.0.0 + version: 6.0.2 + sortablejs: + specifier: ^1.14.0 + version: 1.15.6 + vue: + specifier: ^3.2.40 + version: 3.5.24(typescript@4.9.5) + vue-i18n: + specifier: ^9.1.9 + version: 9.14.5(vue@3.5.24(typescript@4.9.5)) + vue-router: + specifier: ^4.1.6 + version: 4.6.3(vue@3.5.24(typescript@4.9.5)) + vuedraggable: + specifier: ^2.24.3 + version: 2.24.3 + devDependencies: + '@commitlint/cli': + specifier: ^16.2.3 + version: 16.3.0 + '@commitlint/config-conventional': + specifier: ^16.2.1 + version: 16.2.4 + '@types/node': + specifier: ^16.11.7 + version: 16.18.126 + '@types/nprogress': + specifier: ^0.2.0 + version: 0.2.3 + '@types/path-browserify': + specifier: ^1.0.0 + version: 1.0.3 + '@typescript-eslint/eslint-plugin': + specifier: ^5.19.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.19.0 + version: 5.62.0(eslint@8.57.1)(typescript@4.9.5) + '@vitejs/plugin-vue': + specifier: ^4.0.0 + version: 4.6.2(vite@4.5.14(@types/node@16.18.126)(sass@1.93.3))(vue@3.5.24(typescript@4.9.5)) + autoprefixer: + specifier: ^10.4.13 + version: 10.4.21(postcss@8.5.6) + eslint: + specifier: ^8.14.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^8.5.0 + version: 8.10.2(eslint@8.57.1) + eslint-plugin-prettier: + specifier: ^4.0.0 + version: 4.2.5(eslint-config-prettier@8.10.2(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8) + eslint-plugin-vue: + specifier: ^8.6.0 + version: 8.7.1(eslint@8.57.1) + fast-glob: + specifier: ^3.2.11 + version: 3.3.3 + husky: + specifier: ^7.0.4 + version: 7.0.4 + postcss: + specifier: ^8.4.20 + version: 8.5.6 + prettier: + specifier: ^2.6.2 + version: 2.8.8 + sass: + specifier: ^1.53.0 + version: 1.93.3 + tailwindcss: + specifier: ^3.2.4 + version: 3.4.18 + typescript: + specifier: ^4.7.4 + version: 4.9.5 + vite: + specifier: ^4.0.3 + version: 4.5.14(@types/node@16.18.126)(sass@1.93.3) + vite-plugin-svg-icons: + specifier: ^2.0.1 + version: 2.0.1(vite@4.5.14(@types/node@16.18.126)(sass@1.93.3)) + vue-tsc: + specifier: ^0.35.0 + version: 0.35.2(typescript@4.9.5) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@better-scroll/core@2.5.1': + resolution: {integrity: sha512-koKOuYA55dQ04FJRIVUpMGDr1hbCfWmfX0MGp1hKagkQSWSRpwblqACiwtggVauoj9aaJRJZ9hDsTM4weaavlg==} + + '@better-scroll/indicators@2.5.1': + resolution: {integrity: sha512-Hk+Y00pR6fTsu6C9HGg1yYZtsu1gAcTgcs4C9aM5h6fQANX/T2YIYrOSjZmdL+js2PTcXJWZS8VM4Xjoi1PbfQ==} + + '@better-scroll/infinity@2.5.1': + resolution: {integrity: sha512-GKHrrasIh0KlGzhASHDo5hEEBJcDFpP4XaZGPH9Ey8+QBH6/O1ykAXS2ixkVAOTkBrv+KgFXoCUr4oN1xWeM+g==} + + '@better-scroll/mouse-wheel@2.5.1': + resolution: {integrity: sha512-DGnrirRMY6zMM7xwgx09D/cA9A//3J1/uDkq8iBVEyE5p0sEr/keQpjEfFHGkBRa505BnbBwdbN6f5lugEDSPw==} + + '@better-scroll/movable@2.5.1': + resolution: {integrity: sha512-8bLPRY15bbK4K5+tjrtdaKsFFKmJx72wRdg+xz3xQGFcTD940HFkJiORSOcz8Ufue7eOJfcmreQJBw6XY+TqTw==} + + '@better-scroll/nested-scroll@2.5.1': + resolution: {integrity: sha512-3cRsARxf9tq1VWBq7YAaET0xGAmgY1ERMmnXDo2gHFrmsJoNOionlpAeHdZvKQp2jG7JrzJ1O27nGCXf40gnkw==} + + '@better-scroll/observe-dom@2.5.1': + resolution: {integrity: sha512-TCMGFLRfpXBPIwtUV/efliUmfmrhSNI7NXdSyjdWjsLOS7dh3eFkmcom5ERVWMaXVELSmujGXLqobT+dT0C/jg==} + + '@better-scroll/observe-image@2.5.1': + resolution: {integrity: sha512-0Lhfj83o8EESwOxr8bfStCzNOokTm3KB7JeyMS8u/xl+3tyTuls9889cyAukYk4Yly1cS49pCGfj2P8YOiwtUg==} + + '@better-scroll/pull-down@2.5.1': + resolution: {integrity: sha512-Y6XcGu2NlevPg3k9VBRRFvpmfoTA+rO96JGdog2qKHclIPNXnsVwsIHtZfAm9weE/f9UuC4BnB+VUFRlucfupg==} + + '@better-scroll/pull-up@2.5.1': + resolution: {integrity: sha512-1hu3xSMxdB8T391KffpNZ7g93lMwZEHjfb1F1Y4KvIkciDt8nXqkGpqrZF+YwR+EJTgYcWqUO8kgmI6XXu7Pkg==} + + '@better-scroll/scroll-bar@2.5.1': + resolution: {integrity: sha512-i6r60pWG/ztkFK2j5Gj54I0LJb2jGh5TWJNQBoW0gUkp28B+0JvBFTwZn9tF7beZCBorKR7Hvvu4O9A1TJy94Q==} + + '@better-scroll/shared-utils@2.5.1': + resolution: {integrity: sha512-AplkfSjXVYP9LZiD6JsKgmgQJ/mG4uuLmBuwLz8W5OsYc7AYTfN8kw6GqZ5OwCGoXkVhBGyd8NeC4xwYItp0aw==} + + '@better-scroll/slide@2.5.1': + resolution: {integrity: sha512-aDOrfsmjAcz6DXN7mDX3tPieAn195R43Yn9e3waI19TIEok/mQlI1a/kb5quqWOoxkiaZQ8xe3vx5ZTj9C+F6Q==} + + '@better-scroll/wheel@2.5.1': + resolution: {integrity: sha512-fYLcEvkh88Z/2L+P5/+SGMunuc+HzAjGOiORIa/x21qb/knO2RFH4A/V1Rt3OIW4QluWzuFnU6jJRPlsQVZ4fg==} + + '@better-scroll/zoom@2.5.1': + resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==} + + '@commitlint/cli@16.3.0': + resolution: {integrity: sha512-P+kvONlfsuTMnxSwWE1H+ZcPMY3STFaHb2kAacsqoIkNx66O0T7sTpBxpxkMrFPyhkJiLJnJWMhk4bbvYD3BMA==} + engines: {node: '>=v12'} + hasBin: true + + '@commitlint/config-conventional@16.2.4': + resolution: {integrity: sha512-av2UQJa3CuE5P0dzxj/o/B9XVALqYzEViHrMXtDrW9iuflrqCStWBAioijppj9URyz6ONpohJKAtSdgAOE0gkA==} + engines: {node: '>=v12'} + + '@commitlint/config-validator@16.2.1': + resolution: {integrity: sha512-hogSe0WGg7CKmp4IfNbdNES3Rq3UEI4XRPB8JL4EPgo/ORq5nrGTVzxJh78omibNuB8Ho4501Czb1Er1MoDWpw==} + engines: {node: '>=v12'} + + '@commitlint/ensure@16.2.1': + resolution: {integrity: sha512-/h+lBTgf1r5fhbDNHOViLuej38i3rZqTQnBTk+xEg+ehOwQDXUuissQ5GsYXXqI5uGy+261ew++sT4EA3uBJ+A==} + engines: {node: '>=v12'} + + '@commitlint/execute-rule@16.2.1': + resolution: {integrity: sha512-oSls82fmUTLM6cl5V3epdVo4gHhbmBFvCvQGHBRdQ50H/690Uq1Dyd7hXMuKITCIdcnr9umyDkr8r5C6HZDF3g==} + engines: {node: '>=v12'} + + '@commitlint/format@16.2.1': + resolution: {integrity: sha512-Yyio9bdHWmNDRlEJrxHKglamIk3d6hC0NkEUW6Ti6ipEh2g0BAhy8Od6t4vLhdZRa1I2n+gY13foy+tUgk0i1Q==} + engines: {node: '>=v12'} + + '@commitlint/is-ignored@16.2.4': + resolution: {integrity: sha512-Lxdq9aOAYCOOOjKi58ulbwK/oBiiKz+7Sq0+/SpFIEFwhHkIVugvDvWjh2VRBXmRC/x5lNcjDcYEwS/uYUvlYQ==} + engines: {node: '>=v12'} + + '@commitlint/lint@16.2.4': + resolution: {integrity: sha512-AUDuwOxb2eGqsXbTMON3imUGkc1jRdtXrbbohiLSCSk3jFVXgJLTMaEcr39pR00N8nE9uZ+V2sYaiILByZVmxQ==} + engines: {node: '>=v12'} + + '@commitlint/load@16.3.0': + resolution: {integrity: sha512-3tykjV/iwbkv2FU9DG+NZ/JqmP0Nm3b7aDwgCNQhhKV5P74JAuByULkafnhn+zsFGypG1qMtI5u+BZoa9APm0A==} + engines: {node: '>=v12'} + + '@commitlint/message@16.2.1': + resolution: {integrity: sha512-2eWX/47rftViYg7a3axYDdrgwKv32mxbycBJT6OQY/MJM7SUfYNYYvbMFOQFaA4xIVZt7t2Alyqslbl6blVwWw==} + engines: {node: '>=v12'} + + '@commitlint/parse@16.2.1': + resolution: {integrity: sha512-2NP2dDQNL378VZYioLrgGVZhWdnJO4nAxQl5LXwYb08nEcN+cgxHN1dJV8OLJ5uxlGJtDeR8UZZ1mnQ1gSAD/g==} + engines: {node: '>=v12'} + + '@commitlint/read@16.2.1': + resolution: {integrity: sha512-tViXGuaxLTrw2r7PiYMQOFA2fueZxnnt0lkOWqKyxT+n2XdEMGYcI9ID5ndJKXnfPGPppD0w/IItKsIXlZ+alw==} + engines: {node: '>=v12'} + + '@commitlint/resolve-extends@16.2.1': + resolution: {integrity: sha512-NbbCMPKTFf2J805kwfP9EO+vV+XvnaHRcBy6ud5dF35dxMsvdJqke54W3XazXF1ZAxC4a3LBy4i/GNVBAthsEg==} + engines: {node: '>=v12'} + + '@commitlint/rules@16.2.4': + resolution: {integrity: sha512-rK5rNBIN2ZQNQK+I6trRPK3dWa0MtaTN4xnwOma1qxa4d5wQMQJtScwTZjTJeallFxhOgbNOgr48AMHkdounVg==} + engines: {node: '>=v12'} + + '@commitlint/to-lines@16.2.1': + resolution: {integrity: sha512-9/VjpYj5j1QeY3eiog1zQWY6axsdWAc0AonUUfyZ7B0MVcRI0R56YsHAfzF6uK/g/WwPZaoe4Lb1QCyDVnpVaQ==} + engines: {node: '>=v12'} + + '@commitlint/top-level@16.2.1': + resolution: {integrity: sha512-lS6GSieHW9y6ePL73ied71Z9bOKyK+Ib9hTkRsB8oZFAyQZcyRwq2w6nIa6Fngir1QW51oKzzaXfJL94qwImyw==} + engines: {node: '>=v12'} + + '@commitlint/types@16.2.1': + resolution: {integrity: sha512-7/z7pA7BM0i8XvMSBynO7xsB3mVQPUZbVn6zMIlp/a091XJ3qAXRXc+HwLYhiIdzzS5fuxxNIHZMGHVD4HJxdA==} + engines: {node: '>=v12'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + + '@element-plus/icons-vue@2.3.2': + resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==} + peerDependencies: + vue: ^3.2.0 + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@intlify/core-base@9.14.5': + resolution: {integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==} + engines: {node: '>= 16'} + + '@intlify/message-compiler@9.14.5': + resolution: {integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==} + engines: {node: '>= 16'} + + '@intlify/shared@9.14.5': + resolution: {integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==} + engines: {node: '>= 16'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@sxzz/popperjs-es@2.11.7': + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + + '@transloadit/prettier-bytes@0.0.7': + resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + + '@types/js-cookie@3.0.6': + resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + + '@types/minimist@1.2.5': + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + + '@types/node@16.18.126': + resolution: {integrity: sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/path-browserify@1.0.3': + resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/svgo@2.6.4': + resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} + + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/type-utils@5.62.0': + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@uppy/companion-client@2.2.2': + resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==} + + '@uppy/core@2.3.4': + resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==} + + '@uppy/store-default@2.1.1': + resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==} + + '@uppy/utils@4.1.3': + resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==} + + '@uppy/xhr-upload@2.1.3': + resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==} + peerDependencies: + '@uppy/core': ^2.3.3 + + '@vitejs/plugin-vue@4.6.2': + resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 || ^5.0.0 + vue: ^3.2.25 + + '@volar/code-gen@0.35.2': + resolution: {integrity: sha512-MoZHuNnPfUWnCNkQUI5+U+gvLTxrU+XlCTusdNOTFYUUAa+M68MH0RxFIS9Ybj4uAUWTcZx0Ow1q5t/PZozo+Q==} + + '@volar/source-map@0.35.2': + resolution: {integrity: sha512-PFHh9wN/qMkOWYyvmB8ckvIzolrpNOvK5EBdxxdTpiPJhfYjW82rMDBnYf6RxCe7yQxrUrmve6BWVO7flxWNVQ==} + + '@volar/vue-code-gen@0.35.2': + resolution: {integrity: sha512-8H6P8EtN06eSVGjtcJhGqZzFIg6/nWoHVOlnhc5vKqC7tXwpqPbyMQae0tO7pLBd5qSb/dYU5GQcBAHsi2jgyA==} + deprecated: 'WARNING: This project has been renamed to @vue/language-core. Install using @vue/language-core instead.' + + '@volar/vue-typescript@0.35.2': + resolution: {integrity: sha512-PZI6Urb+Vr5Dvgf9xysM8X7TP09inWDy1wjDtprBoBhxS7r0Dg3V0qZuJa7sSGz7M0QMa5R/CBaZPhlxFCfJBw==} + deprecated: 'WARNING: This project has been renamed to @vue/typescript. Install using @vue/typescript instead.' + + '@vue/compiler-core@3.5.24': + resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} + + '@vue/compiler-dom@3.5.24': + resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} + + '@vue/compiler-sfc@3.5.24': + resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==} + + '@vue/compiler-ssr@3.5.24': + resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/reactivity@3.5.24': + resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==} + + '@vue/runtime-core@3.5.24': + resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==} + + '@vue/runtime-dom@3.5.24': + resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==} + + '@vue/server-renderer@3.5.24': + resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==} + peerDependencies: + vue: 3.5.24 + + '@vue/shared@3.5.24': + resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/shared@9.13.0': + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + + '@wangeditor/basic-modules@1.1.7': + resolution: {integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/code-highlight@1.0.3': + resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/core@1.1.19': + resolution: {integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==} + peerDependencies: + '@uppy/core': ^2.1.1 + '@uppy/xhr-upload': ^2.0.3 + dom7: ^3.0.0 + is-hotkey: ^0.2.0 + lodash.camelcase: ^4.3.0 + lodash.clonedeep: ^4.5.0 + lodash.debounce: ^4.0.8 + lodash.foreach: ^4.5.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + lodash.toarray: ^4.4.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/editor-for-vue@5.1.12': + resolution: {integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==} + peerDependencies: + '@wangeditor/editor': '>=5.1.0' + vue: ^3.0.5 + + '@wangeditor/editor@5.1.23': + resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==} + + '@wangeditor/list-module@1.0.5': + resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/table-module@1.1.4': + resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/upload-image-module@1.0.2': + resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==} + peerDependencies: + '@uppy/core': ^2.0.3 + '@uppy/xhr-upload': ^2.0.3 + '@wangeditor/basic-modules': 1.x + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.foreach: ^4.5.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/video-module@1.1.4': + resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==} + peerDependencies: + '@uppy/core': ^2.1.4 + '@uppy/xhr-upload': ^2.0.7 + '@wangeditor/core': 1.x + dom7: ^3.0.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array-unique@0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + assign-symbols@1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base@0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + + baseline-browser-mapping@2.8.25: + resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} + hasBin: true + + better-scroll@2.5.1: + resolution: {integrity: sha512-OiF3cQroRfTzf+CRQH2z1G52ZAlNHINI6lCAvDmyFu0o0nRuTaV9F+fmBGIU2BL5p5IplUQ4E7sYa1TLfZarzQ==} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.27.0: + resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cache-base@1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase-keys@6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001754: + resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} + + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + class-utils@0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + collection-visit@1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + conventional-changelog-angular@5.0.13: + resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} + engines: {node: '>=10'} + + conventional-changelog-conventionalcommits@4.6.3: + resolution: {integrity: sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==} + engines: {node: '>=10'} + + conventional-commits-parser@3.2.4: + resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} + engines: {node: '>=10'} + hasBin: true + + copy-descriptor@0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig-typescript-loader@2.0.2: + resolution: {integrity: sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=7' + typescript: '>=3' + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + dargs@7.0.0: + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + default-passive-events@2.0.0: + resolution: {integrity: sha512-eMtt76GpDVngZQ3ocgvRcNCklUMwID1PaNbCNxfpDXuiOXttSh0HzBbda1HU9SIUsDc02vb7g9+3I5tlqe/qMQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + define-property@0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-serializer@0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom7@3.0.0: + resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==} + + domelementtype@1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@2.4.2: + resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domutils@1.7.0: + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + echarts@5.6.0: + resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + + electron-to-chromium@1.5.248: + resolution: {integrity: sha512-zsur2yunphlyAO4gIubdJEXCK6KOVvtpiuDfCIqbM9FjcnMYiyn0ICa3hWfPr0nc41zcLWobgy1iL7VvoOyA2Q==} + + element-plus@2.11.7: + resolution: {integrity: sha512-Bh47wuzsqaNBNDkbtlOlZER1cGcOB8GsXp/+C9b95MOrk0wvoHUV4NKKK7xMkfYNFYdYysQ752oMhnExgAL6+g==} + peerDependencies: + vue: ^3.2.0 + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + entities@1.1.2: + resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@8.10.2: + resolution: {integrity: sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@4.2.5: + resolution: {integrity: sha512-9Ni+xgemM2IWLq6aXEpP2+V/V30GeA/46Ar629vcMqVPodFFWC9skHu/D1phvuqtS8bJCFnNf01/qcmqYEwNfg==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + + eslint-plugin-vue@8.7.1: + resolution: {integrity: sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-utils@3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + expand-brackets@2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + + extglob@2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fragment-cache@0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-value@2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + + git-raw-commits@2.0.11: + resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} + engines: {node: '>=10'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-dirs@0.1.1: + resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@1.0.0: + resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==} + engines: {node: '>=0.10.0'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-value@0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + html-void-elements@2.0.1: + resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} + + htmlparser2@3.10.1: + resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + husky@7.0.4: + resolution: {integrity: sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==} + engines: {node: '>=12'} + hasBin: true + + i18next@20.6.1: + resolution: {integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-descriptor@0.1.7: + resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hotkey@0.2.0: + resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-text-path@1.0.1: + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + js-base64@2.6.4: + resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} + + js-base64@3.7.8: + resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsencrypt@3.5.4: + resolution: {integrity: sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + + kind-of@5.1.0: + resolution: {integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==} + engines: {node: '>=0.10.0'} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + loader-utils@1.4.2: + resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} + engines: {node: '>=4.0.0'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash-unified@1.0.3: + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} + peerDependencies: + '@types/lodash-es': '*' + lodash: '*' + lodash-es: '*' + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.foreach@4.5.0: + resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash.toarray@4.4.0: + resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + + map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + + map-visit@1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + meow@8.1.2: + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} + + merge-options@1.0.1: + resolution: {integrity: sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==} + engines: {node: '>=4'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@3.1.0: + resolution: {integrity: sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==} + engines: {node: '>=0.10.0'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-match@1.0.2: + resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mixin-deep@1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + namespace-emitter@2.0.1: + resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanomatch@1.2.13: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nprogress@0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-copy@0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object-visit@1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + pascalcase@0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pinia@2.3.1: + resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==} + peerDependencies: + typescript: '>=4.4.4' + vue: ^2.7.0 || ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + posix-character-classes@0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-prefix-selector@1.16.1: + resolution: {integrity: sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==} + peerDependencies: + postcss: '>4 <9' + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@5.2.18: + resolution: {integrity: sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==} + engines: {node: '>=0.12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + posthtml-parser@0.2.1: + resolution: {integrity: sha512-nPC53YMqJnc/+1x4fRYFfm81KV2V+G9NZY+hTohpYg64Ay7NemWWcV4UWuy/SgMupqQ3kJ88M/iRfZmSnxT+pw==} + + posthtml-rename-id@1.0.12: + resolution: {integrity: sha512-UKXf9OF/no8WZo9edRzvuMenb6AD5hDLzIepJW+a4oJT+T/Lx7vfMYWT4aWlGNQh0WMhnUx1ipN9OkZ9q+ddEw==} + + posthtml-render@1.4.0: + resolution: {integrity: sha512-W1779iVHGfq0Fvh2PROhCe2QhB8mEErgqzo1wpIt36tCgChafP+hbXIhLDOM8ePJrZcFs0vkNEtdibEWVqChqw==} + engines: {node: '>=10'} + + posthtml-svg-mode@1.0.3: + resolution: {integrity: sha512-hEqw9NHZ9YgJ2/0G7CECOeuLQKZi8HjWLkBaSVtOWjygQ9ZD8P7tqeowYs7WrFdKsWEKG7o+IlsPY8jrr0CJpQ==} + + posthtml@0.9.2: + resolution: {integrity: sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==} + engines: {node: '>=0.10.0'} + + preact@10.27.2: + resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + query-string@4.3.4: + resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==} + engines: {node: '>=0.10.0'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + + read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regex-not@1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-global@1.0.0: + resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==} + engines: {node: '>=8'} + + resolve-url@0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-regex@1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + + sass@1.93.3: + resolution: {integrity: sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==} + engines: {node: '>=14.0.0'} + hasBin: true + + screenfull@6.0.2: + resolution: {integrity: sha512-AQdy8s4WhNvUZ6P8F6PB21tSPIYKniic+Ogx0AacBMjKP1GUHN2E9URxQHtCusiwxudnCKkdy4GrHXPPJSkCCw==} + engines: {node: ^14.13.1 || >=16.0.0} + + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@7.3.7: + resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + set-value@2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slate-history@0.66.0: + resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==} + peerDependencies: + slate: '>=0.65.3' + + slate@0.72.8: + resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==} + + snabbdom@3.6.3: + resolution: {integrity: sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==} + engines: {node: '>=12.17.0'} + + snapdragon-node@2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + + sortablejs@1.10.2: + resolution: {integrity: sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==} + + sortablejs@1.15.6: + resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-resolve@0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + + source-map-url@0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + split-string@3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + + ssr-window@3.0.0: + resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + static-extend@0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + strict-uri-encode@1.1.0: + resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + + supports-color@3.2.3: + resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==} + engines: {node: '>=0.8.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-baker@1.7.0: + resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==} + + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + text-extensions@1.9.0: + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + to-object-path@0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-regex@3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + + traverse@0.6.11: + resolution: {integrity: sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==} + engines: {node: '>= 0.4'} + + trim-newlines@3.0.1: + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.18.1: + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typedarray.prototype.slice@1.0.5: + resolution: {integrity: sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==} + engines: {node: '>= 0.4'} + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + union-value@1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unset-value@1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urix@0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + + use@3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-plugin-svg-icons@2.0.1: + resolution: {integrity: sha512-6ktD+DhV6Rz3VtedYvBKKVA2eXF+sAQVaKkKLDSqGUfnhqXl3bj5PPkVTl3VexfTuZy66PmINi8Q6eFnVfRUmA==} + peerDependencies: + vite: '>=2.0.0' + + vite@4.5.14: + resolution: {integrity: sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-eslint-parser@8.3.0: + resolution: {integrity: sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-i18n@9.14.5: + resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==} + engines: {node: '>= 16'} + peerDependencies: + vue: ^3.0.0 + + vue-router@4.6.3: + resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==} + peerDependencies: + vue: ^3.5.0 + + vue-tsc@0.35.2: + resolution: {integrity: sha512-aqY16VlODHzqtKGUkqdumNpH+s5ABCkufRyvMKQlL/mua+N2DfSVnHufzSNNUMr7vmOO0YsNg27jsspBMq4iGA==} + hasBin: true + peerDependencies: + typescript: '*' + + vue@3.5.24: + resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vuedraggable@2.24.3: + resolution: {integrity: sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wildcard@1.1.2: + resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/runtime@7.28.4': {} + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@better-scroll/core@2.5.1': + dependencies: + '@better-scroll/shared-utils': 2.5.1 + + '@better-scroll/indicators@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/infinity@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/mouse-wheel@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/movable@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/nested-scroll@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/observe-dom@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/observe-image@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/pull-down@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/pull-up@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/scroll-bar@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/shared-utils@2.5.1': {} + + '@better-scroll/slide@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/wheel@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/zoom@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@commitlint/cli@16.3.0': + dependencies: + '@commitlint/format': 16.2.1 + '@commitlint/lint': 16.2.4 + '@commitlint/load': 16.3.0 + '@commitlint/read': 16.2.1 + '@commitlint/types': 16.2.1 + lodash: 4.17.21 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + + '@commitlint/config-conventional@16.2.4': + dependencies: + conventional-changelog-conventionalcommits: 4.6.3 + + '@commitlint/config-validator@16.2.1': + dependencies: + '@commitlint/types': 16.2.1 + ajv: 6.12.6 + + '@commitlint/ensure@16.2.1': + dependencies: + '@commitlint/types': 16.2.1 + lodash: 4.17.21 + + '@commitlint/execute-rule@16.2.1': {} + + '@commitlint/format@16.2.1': + dependencies: + '@commitlint/types': 16.2.1 + chalk: 4.1.2 + + '@commitlint/is-ignored@16.2.4': + dependencies: + '@commitlint/types': 16.2.1 + semver: 7.3.7 + + '@commitlint/lint@16.2.4': + dependencies: + '@commitlint/is-ignored': 16.2.4 + '@commitlint/parse': 16.2.1 + '@commitlint/rules': 16.2.4 + '@commitlint/types': 16.2.1 + + '@commitlint/load@16.3.0': + dependencies: + '@commitlint/config-validator': 16.2.1 + '@commitlint/execute-rule': 16.2.1 + '@commitlint/resolve-extends': 16.2.1 + '@commitlint/types': 16.2.1 + '@types/node': 16.18.126 + chalk: 4.1.2 + cosmiconfig: 7.1.0 + cosmiconfig-typescript-loader: 2.0.2(@types/node@16.18.126)(cosmiconfig@7.1.0)(typescript@4.9.5) + lodash: 4.17.21 + resolve-from: 5.0.0 + typescript: 4.9.5 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + + '@commitlint/message@16.2.1': {} + + '@commitlint/parse@16.2.1': + dependencies: + '@commitlint/types': 16.2.1 + conventional-changelog-angular: 5.0.13 + conventional-commits-parser: 3.2.4 + + '@commitlint/read@16.2.1': + dependencies: + '@commitlint/top-level': 16.2.1 + '@commitlint/types': 16.2.1 + fs-extra: 10.1.0 + git-raw-commits: 2.0.11 + + '@commitlint/resolve-extends@16.2.1': + dependencies: + '@commitlint/config-validator': 16.2.1 + '@commitlint/types': 16.2.1 + import-fresh: 3.3.1 + lodash: 4.17.21 + resolve-from: 5.0.0 + resolve-global: 1.0.0 + + '@commitlint/rules@16.2.4': + dependencies: + '@commitlint/ensure': 16.2.1 + '@commitlint/message': 16.2.1 + '@commitlint/to-lines': 16.2.1 + '@commitlint/types': 16.2.1 + execa: 5.1.1 + + '@commitlint/to-lines@16.2.1': {} + + '@commitlint/top-level@16.2.1': + dependencies: + find-up: 5.0.0 + + '@commitlint/types@16.2.1': + dependencies: + chalk: 4.1.2 + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@ctrl/tinycolor@3.6.1': {} + + '@element-plus/icons-vue@2.3.2(vue@3.5.24(typescript@4.9.5))': + dependencies: + vue: 3.5.24(typescript@4.9.5) + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@intlify/core-base@9.14.5': + dependencies: + '@intlify/message-compiler': 9.14.5 + '@intlify/shared': 9.14.5 + + '@intlify/message-compiler@9.14.5': + dependencies: + '@intlify/shared': 9.14.5 + source-map-js: 1.2.1 + + '@intlify/shared@9.14.5': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@sxzz/popperjs-es@2.11.7': {} + + '@transloadit/prettier-bytes@0.0.7': {} + + '@trysound/sax@0.2.0': {} + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/event-emitter@0.3.5': {} + + '@types/js-cookie@3.0.6': {} + + '@types/json-schema@7.0.15': {} + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.20 + + '@types/lodash@4.17.20': {} + + '@types/minimist@1.2.5': {} + + '@types/node@16.18.126': {} + + '@types/normalize-package-data@2.4.4': {} + + '@types/nprogress@0.2.3': {} + + '@types/parse-json@4.0.2': {} + + '@types/path-browserify@1.0.3': {} + + '@types/semver@7.7.1': {} + + '@types/svgo@2.6.4': + dependencies: + '@types/node': 16.18.126 + + '@types/web-bluetooth@0.0.16': {} + + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.9.5) + debug: 4.4.3 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare-lite: 1.4.0 + semver: 7.7.3 + tsutils: 3.21.0(typescript@4.9.5) + optionalDependencies: + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) + debug: 4.4.3 + eslint: 8.57.1 + optionalDependencies: + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.9.5) + debug: 4.4.3 + eslint: 8.57.1 + tsutils: 3.21.0(typescript@4.9.5) + optionalDependencies: + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/typescript-estree@5.62.0(typescript@4.9.5)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.3 + tsutils: 3.21.0(typescript@4.9.5) + optionalDependencies: + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@4.9.5)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.7.1 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) + eslint: 8.57.1 + eslint-scope: 5.1.1 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.3.0': {} + + '@uppy/companion-client@2.2.2': + dependencies: + '@uppy/utils': 4.1.3 + namespace-emitter: 2.0.1 + + '@uppy/core@2.3.4': + dependencies: + '@transloadit/prettier-bytes': 0.0.7 + '@uppy/store-default': 2.1.1 + '@uppy/utils': 4.1.3 + lodash.throttle: 4.1.1 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 3.3.11 + preact: 10.27.2 + + '@uppy/store-default@2.1.1': {} + + '@uppy/utils@4.1.3': + dependencies: + lodash.throttle: 4.1.1 + + '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)': + dependencies: + '@uppy/companion-client': 2.2.2 + '@uppy/core': 2.3.4 + '@uppy/utils': 4.1.3 + nanoid: 3.3.11 + + '@vitejs/plugin-vue@4.6.2(vite@4.5.14(@types/node@16.18.126)(sass@1.93.3))(vue@3.5.24(typescript@4.9.5))': + dependencies: + vite: 4.5.14(@types/node@16.18.126)(sass@1.93.3) + vue: 3.5.24(typescript@4.9.5) + + '@volar/code-gen@0.35.2': + dependencies: + '@volar/source-map': 0.35.2 + + '@volar/source-map@0.35.2': {} + + '@volar/vue-code-gen@0.35.2': + dependencies: + '@volar/code-gen': 0.35.2 + '@volar/source-map': 0.35.2 + '@vue/compiler-core': 3.5.24 + '@vue/compiler-dom': 3.5.24 + '@vue/shared': 3.5.24 + + '@volar/vue-typescript@0.35.2': + dependencies: + '@volar/code-gen': 0.35.2 + '@volar/source-map': 0.35.2 + '@volar/vue-code-gen': 0.35.2 + '@vue/compiler-sfc': 3.5.24 + '@vue/reactivity': 3.5.24 + + '@vue/compiler-core@3.5.24': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.24 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.24': + dependencies: + '@vue/compiler-core': 3.5.24 + '@vue/shared': 3.5.24 + + '@vue/compiler-sfc@3.5.24': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-core': 3.5.24 + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.24': + dependencies: + '@vue/compiler-dom': 3.5.24 + '@vue/shared': 3.5.24 + + '@vue/devtools-api@6.6.4': {} + + '@vue/reactivity@3.5.24': + dependencies: + '@vue/shared': 3.5.24 + + '@vue/runtime-core@3.5.24': + dependencies: + '@vue/reactivity': 3.5.24 + '@vue/shared': 3.5.24 + + '@vue/runtime-dom@3.5.24': + dependencies: + '@vue/reactivity': 3.5.24 + '@vue/runtime-core': 3.5.24 + '@vue/shared': 3.5.24 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@4.9.5))': + dependencies: + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 + vue: 3.5.24(typescript@4.9.5) + + '@vue/shared@3.5.24': {} + + '@vueuse/core@9.13.0(vue@3.5.24(typescript@4.9.5))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.5.24(typescript@4.9.5)) + vue-demi: 0.14.10(vue@3.5.24(typescript@4.9.5)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/shared@9.13.0(vue@3.5.24(typescript@4.9.5))': + dependencies: + vue-demi: 0.14.10(vue@3.5.24(typescript@4.9.5)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + is-url: 1.2.4 + lodash.throttle: 4.1.1 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + prismjs: 1.30.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@types/event-emitter': 0.3.5 + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + dom7: 3.0.0 + event-emitter: 0.3.5 + html-void-elements: 2.0.1 + i18next: 20.6.1 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.11 + scroll-into-view-if-needed: 2.2.31 + slate: 0.72.8 + slate-history: 0.66.0(slate@0.72.8) + snabbdom: 3.6.3 + + '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.24(typescript@4.9.5))': + dependencies: + '@wangeditor/editor': 5.1.23 + vue: 3.5.24(typescript@4.9.5) + + '@wangeditor/editor@5.1.23': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + lodash.foreach: 4.5.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@2.2.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@4.1.3: {} + + arg@5.0.2: {} + + argparse@2.0.1: {} + + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-ify@1.0.0: {} + + array-union@2.1.0: {} + + array-unique@0.3.2: {} + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + arrify@1.0.1: {} + + assign-symbols@1.0.0: {} + + async-function@1.0.0: {} + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + atob@2.1.2: {} + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + caniuse-lite: 1.0.30001754 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + base@0.11.2: + dependencies: + cache-base: 1.0.1 + class-utils: 0.3.6 + component-emitter: 1.3.1 + define-property: 1.0.0 + isobject: 3.0.1 + mixin-deep: 1.3.2 + pascalcase: 0.1.1 + + baseline-browser-mapping@2.8.25: {} + + better-scroll@2.5.1: + dependencies: + '@better-scroll/core': 2.5.1 + '@better-scroll/indicators': 2.5.1 + '@better-scroll/infinity': 2.5.1 + '@better-scroll/mouse-wheel': 2.5.1 + '@better-scroll/movable': 2.5.1 + '@better-scroll/nested-scroll': 2.5.1 + '@better-scroll/observe-dom': 2.5.1 + '@better-scroll/observe-image': 2.5.1 + '@better-scroll/pull-down': 2.5.1 + '@better-scroll/pull-up': 2.5.1 + '@better-scroll/scroll-bar': 2.5.1 + '@better-scroll/slide': 2.5.1 + '@better-scroll/wheel': 2.5.1 + '@better-scroll/zoom': 2.5.1 + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bluebird@3.7.2: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@2.3.2: + dependencies: + arr-flatten: 1.1.0 + array-unique: 0.3.2 + extend-shallow: 2.0.1 + fill-range: 4.0.0 + isobject: 3.0.1 + repeat-element: 1.1.4 + snapdragon: 0.8.2 + snapdragon-node: 2.1.1 + split-string: 3.1.0 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.27.0: + dependencies: + baseline-browser-mapping: 2.8.25 + caniuse-lite: 1.0.30001754 + electron-to-chromium: 1.5.248 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.27.0) + + cache-base@1.0.1: + dependencies: + collection-visit: 1.0.0 + component-emitter: 1.3.1 + get-value: 2.0.6 + has-value: 1.0.0 + isobject: 3.0.1 + set-value: 2.0.1 + to-object-path: 0.3.0 + union-value: 1.0.1 + unset-value: 1.0.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + camelcase-keys@6.2.2: + dependencies: + camelcase: 5.3.1 + map-obj: 4.3.0 + quick-lru: 4.0.1 + + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001754: {} + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + class-utils@0.3.6: + dependencies: + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@2.1.2: {} + + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@4.1.1: {} + + commander@7.2.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + component-emitter@1.3.1: {} + + compute-scroll-into-view@1.0.20: {} + + concat-map@0.0.1: {} + + conventional-changelog-angular@5.0.13: + dependencies: + compare-func: 2.0.0 + q: 1.5.1 + + conventional-changelog-conventionalcommits@4.6.3: + dependencies: + compare-func: 2.0.0 + lodash: 4.17.21 + q: 1.5.1 + + conventional-commits-parser@3.2.4: + dependencies: + JSONStream: 1.3.5 + is-text-path: 1.0.1 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + + copy-descriptor@0.1.1: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig-typescript-loader@2.0.2(@types/node@16.18.126)(cosmiconfig@7.1.0)(typescript@4.9.5): + dependencies: + '@types/node': 16.18.126 + cosmiconfig: 7.1.0 + ts-node: 10.9.2(@types/node@16.18.126)(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + create-require@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + csstype@3.1.3: {} + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + dargs@7.0.0: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dayjs@1.11.19: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize-keys@1.1.1: + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + + decamelize@1.2.0: {} + + decode-uri-component@0.2.2: {} + + deep-is@0.1.4: {} + + default-passive-events@2.0.0: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + define-property@0.2.5: + dependencies: + is-descriptor: 0.1.7 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + define-property@2.0.2: + dependencies: + is-descriptor: 1.0.3 + isobject: 3.0.1 + + delayed-stream@1.0.0: {} + + detect-libc@1.0.3: + optional: true + + didyoumean@1.2.2: {} + + diff@4.0.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-serializer@0.2.2: + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom7@3.0.0: + dependencies: + ssr-window: 3.0.0 + + domelementtype@1.3.1: {} + + domelementtype@2.3.0: {} + + domhandler@2.4.2: + dependencies: + domelementtype: 1.3.1 + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domutils@1.7.0: + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + echarts@5.6.0: + dependencies: + tslib: 2.3.0 + zrender: 5.6.1 + + electron-to-chromium@1.5.248: {} + + element-plus@2.11.7(vue@3.5.24(typescript@4.9.5)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.2(vue@3.5.24(typescript@4.9.5)) + '@floating-ui/dom': 1.7.4 + '@popperjs/core': '@sxzz/popperjs-es@2.11.7' + '@types/lodash': 4.17.20 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 9.13.0(vue@3.5.24(typescript@4.9.5)) + async-validator: 4.2.5 + dayjs: 1.11.19 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.5.24(typescript@4.9.5) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + entities@1.1.2: {} + + entities@2.2.0: {} + + entities@4.5.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@8.10.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-prettier@4.2.5(eslint-config-prettier@8.10.2(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8): + dependencies: + eslint: 8.57.1 + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + optionalDependencies: + eslint-config-prettier: 8.10.2(eslint@8.57.1) + + eslint-plugin-vue@8.7.1(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + eslint-utils: 3.0.0(eslint@8.57.1) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 6.1.2 + semver: 7.7.3 + vue-eslint-parser: 8.3.0(eslint@8.57.1) + transitivePeerDependencies: + - supports-color + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-utils@3.0.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 2.1.0 + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + espree@9.6.1: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + expand-brackets@2.1.4: + dependencies: + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + posix-character-classes: 0.1.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + ext@1.7.0: + dependencies: + type: 2.7.3 + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extend-shallow@3.0.2: + dependencies: + assign-symbols: 1.0.0 + is-extendable: 1.0.1 + + extglob@2.0.4: + dependencies: + array-unique: 0.3.2 + define-property: 1.0.0 + expand-brackets: 2.1.4 + extend-shallow: 2.0.1 + fragment-cache: 0.2.1 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@4.0.0: + dependencies: + extend-shallow: 2.0.1 + is-number: 3.0.0 + repeat-string: 1.6.1 + to-regex-range: 2.1.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + for-in@1.0.2: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fraction.js@4.3.7: {} + + fragment-cache@0.2.1: + dependencies: + map-cache: 0.2.2 + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-value@2.0.6: {} + + git-raw-commits@2.0.11: + dependencies: + dargs: 7.0.0 + lodash: 4.17.21 + meow: 8.1.2 + split2: 3.2.2 + through2: 4.0.2 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-dirs@0.1.1: + dependencies: + ini: 1.3.8 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + hard-rejection@2.1.0: {} + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-bigints@1.1.0: {} + + has-flag@1.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-value@0.3.1: + dependencies: + get-value: 2.0.6 + has-values: 0.1.4 + isobject: 2.1.0 + + has-value@1.0.0: + dependencies: + get-value: 2.0.6 + has-values: 1.0.0 + isobject: 3.0.1 + + has-values@0.1.4: {} + + has-values@1.0.0: + dependencies: + is-number: 3.0.0 + kind-of: 4.0.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + html-void-elements@2.0.1: {} + + htmlparser2@3.10.1: + dependencies: + domelementtype: 1.3.1 + domhandler: 2.4.2 + domutils: 1.7.0 + entities: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + + human-signals@2.1.0: {} + + husky@7.0.4: {} + + i18next@20.6.1: + dependencies: + '@babel/runtime': 7.28.4 + + ignore@5.3.2: {} + + image-size@0.5.5: {} + + immer@9.0.21: {} + + immutable@5.1.4: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@1.1.6: {} + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-descriptor@0.1.7: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-extendable@0.1.1: {} + + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hotkey@0.2.0: {} + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@1.1.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-plain-object@5.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-text-path@1.0.1: + dependencies: + text-extensions: 1.9.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-url@1.2.4: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-windows@1.0.2: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + + isobject@3.0.1: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} + + js-base64@2.6.4: {} + + js-base64@3.7.8: {} + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsencrypt@3.5.4: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kind-of@4.0.0: + dependencies: + is-buffer: 1.1.6 + + kind-of@5.1.0: {} + + kind-of@6.0.3: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + loader-utils@1.4.2: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 1.0.2 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash-es@4.17.21: {} + + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21): + dependencies: + '@types/lodash-es': 4.17.12 + lodash: 4.17.21 + lodash-es: 4.17.21 + + lodash.camelcase@4.3.0: {} + + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.foreach@4.5.0: {} + + lodash.isequal@4.5.0: {} + + lodash.merge@4.6.2: {} + + lodash.throttle@4.1.1: {} + + lodash.toarray@4.4.0: {} + + lodash@4.17.21: {} + + lru-cache@10.4.3: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-error@1.3.6: {} + + map-cache@0.2.2: {} + + map-obj@1.0.1: {} + + map-obj@4.3.0: {} + + map-visit@1.0.0: + dependencies: + object-visit: 1.0.1 + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.14: {} + + memoize-one@6.0.0: {} + + meow@8.1.2: + dependencies: + '@types/minimist': 1.2.5 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.1 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 3.0.3 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.1 + type-fest: 0.18.1 + yargs-parser: 20.2.9 + + merge-options@1.0.1: + dependencies: + is-plain-obj: 1.1.0 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@3.1.0: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + braces: 2.3.2 + define-property: 1.0.0 + extend-shallow: 2.0.1 + extglob: 2.0.4 + fragment-cache: 0.2.1 + kind-of: 5.1.0 + nanomatch: 1.2.13 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-match@1.0.2: + dependencies: + wildcard: 1.1.2 + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + min-indent@1.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist-options@4.1.0: + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + mixin-deep@1.3.2: + dependencies: + for-in: 1.0.2 + is-extendable: 1.0.1 + + ms@2.0.0: {} + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + namespace-emitter@2.0.1: {} + + nanoid@3.3.11: {} + + nanomatch@1.2.13: + dependencies: + arr-diff: 4.0.0 + array-unique: 0.3.2 + define-property: 2.0.2 + extend-shallow: 3.0.2 + fragment-cache: 0.2.1 + is-windows: 1.0.2 + kind-of: 6.0.3 + object.pick: 1.3.0 + regex-not: 1.0.2 + snapdragon: 0.8.2 + to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color + + natural-compare-lite@1.4.0: {} + + natural-compare@1.4.0: {} + + next-tick@1.1.0: {} + + node-addon-api@7.1.1: + optional: true + + node-releases@2.0.27: {} + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.11 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.16.1 + semver: 7.7.3 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-wheel-es@1.2.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-copy@0.1.0: + dependencies: + copy-descriptor: 0.1.1 + define-property: 0.2.5 + kind-of: 3.2.2 + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object-visit@1.0.1: + dependencies: + isobject: 3.0.1 + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + pascalcase@0.1.1: {} + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@6.3.0: {} + + path-type@4.0.0: {} + + pathe@0.2.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pinia@2.3.1(typescript@4.9.5)(vue@3.5.24(typescript@4.9.5)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.24(typescript@4.9.5) + vue-demi: 0.14.10(vue@3.5.24(typescript@4.9.5)) + optionalDependencies: + typescript: 4.9.5 + transitivePeerDependencies: + - '@vue/composition-api' + + pirates@4.0.7: {} + + posix-character-classes@0.1.1: {} + + possible-typed-array-names@1.1.0: {} + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.11 + + postcss-js@4.1.0(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.6 + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-prefix-selector@1.16.1(postcss@5.2.18): + dependencies: + postcss: 5.2.18 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@5.2.18: + dependencies: + chalk: 1.1.3 + js-base64: 2.6.4 + source-map: 0.5.7 + supports-color: 3.2.3 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + posthtml-parser@0.2.1: + dependencies: + htmlparser2: 3.10.1 + isobject: 2.1.0 + + posthtml-rename-id@1.0.12: + dependencies: + escape-string-regexp: 1.0.5 + + posthtml-render@1.4.0: {} + + posthtml-svg-mode@1.0.3: + dependencies: + merge-options: 1.0.1 + posthtml: 0.9.2 + posthtml-parser: 0.2.1 + posthtml-render: 1.4.0 + + posthtml@0.9.2: + dependencies: + posthtml-parser: 0.2.1 + posthtml-render: 1.4.0 + + preact@10.27.2: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@2.8.8: {} + + prismjs@1.30.0: {} + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + q@1.5.1: {} + + query-string@4.3.4: + dependencies: + object-assign: 4.1.1 + strict-uri-encode: 1.1.0 + + queue-microtask@1.2.3: {} + + quick-lru@4.0.1: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-pkg-up@7.0.1: + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + + read-pkg@5.2.0: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regex-not@1.0.2: + dependencies: + extend-shallow: 3.0.2 + safe-regex: 1.1.0 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-global@1.0.0: + dependencies: + global-dirs: 0.1.1 + + resolve-url@0.2.1: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + ret@0.1.15: {} + + reusify@1.1.0: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-regex@1.1.0: + dependencies: + ret: 0.1.15 + + sass@1.93.3: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.4 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + screenfull@6.0.2: {} + + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + + semver@5.7.2: {} + + semver@7.3.7: + dependencies: + lru-cache: 6.0.0 + + semver@7.7.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + set-value@2.0.1: + dependencies: + extend-shallow: 2.0.1 + is-extendable: 0.1.1 + is-plain-object: 2.0.4 + split-string: 3.1.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slate-history@0.66.0(slate@0.72.8): + dependencies: + is-plain-object: 5.0.0 + slate: 0.72.8 + + slate@0.72.8: + dependencies: + immer: 9.0.21 + is-plain-object: 5.0.0 + tiny-warning: 1.0.3 + + snabbdom@3.6.3: {} + + snapdragon-node@2.1.1: + dependencies: + define-property: 1.0.0 + isobject: 3.0.1 + snapdragon-util: 3.0.1 + + snapdragon-util@3.0.1: + dependencies: + kind-of: 3.2.2 + + snapdragon@0.8.2: + dependencies: + base: 0.11.2 + debug: 2.6.9 + define-property: 0.2.5 + extend-shallow: 2.0.1 + map-cache: 0.2.2 + source-map: 0.5.7 + source-map-resolve: 0.5.3 + use: 3.1.1 + transitivePeerDependencies: + - supports-color + + sortablejs@1.10.2: {} + + sortablejs@1.15.6: {} + + source-map-js@1.2.1: {} + + source-map-resolve@0.5.3: + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.2 + resolve-url: 0.2.1 + source-map-url: 0.4.1 + urix: 0.1.0 + + source-map-url@0.4.1: {} + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.22 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + split-string@3.1.0: + dependencies: + extend-shallow: 3.0.2 + + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + + ssr-window@3.0.0: {} + + stable@0.1.8: {} + + static-extend@0.1.2: + dependencies: + define-property: 0.2.5 + object-copy: 0.1.0 + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + strict-uri-encode@1.1.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-final-newline@2.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + + supports-color@2.0.0: {} + + supports-color@3.2.3: + dependencies: + has-flag: 1.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-baker@1.7.0: + dependencies: + bluebird: 3.7.2 + clone: 2.1.2 + he: 1.2.0 + image-size: 0.5.5 + loader-utils: 1.4.2 + merge-options: 1.0.1 + micromatch: 3.1.0 + postcss: 5.2.18 + postcss-prefix-selector: 1.16.1(postcss@5.2.18) + posthtml-rename-id: 1.0.12 + posthtml-svg-mode: 1.0.3 + query-string: 4.3.4 + traverse: 0.6.11 + transitivePeerDependencies: + - supports-color + + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.1.1 + stable: 0.1.8 + + tailwindcss@3.4.18: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.11 + sucrase: 3.35.0 + transitivePeerDependencies: + - tsx + - yaml + + text-extensions@1.9.0: {} + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + + through@2.3.8: {} + + tiny-warning@1.0.3: {} + + to-object-path@0.3.0: + dependencies: + kind-of: 3.2.2 + + to-regex-range@2.1.1: + dependencies: + is-number: 3.0.0 + repeat-string: 1.6.1 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-regex@3.0.2: + dependencies: + define-property: 2.0.2 + extend-shallow: 3.0.2 + regex-not: 1.0.2 + safe-regex: 1.1.0 + + traverse@0.6.11: + dependencies: + gopd: 1.2.0 + typedarray.prototype.slice: 1.0.5 + which-typed-array: 1.1.19 + + trim-newlines@3.0.1: {} + + ts-interface-checker@0.1.13: {} + + ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 16.18.126 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tslib@1.14.1: {} + + tslib@2.3.0: {} + + tsutils@3.21.0(typescript@4.9.5): + dependencies: + tslib: 1.14.1 + typescript: 4.9.5 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.18.1: {} + + type-fest@0.20.2: {} + + type-fest@0.6.0: {} + + type-fest@0.8.1: {} + + type@2.7.3: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typedarray.prototype.slice@1.0.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-proto: 1.0.1 + math-intrinsics: 1.1.0 + typed-array-buffer: 1.0.3 + typed-array-byte-offset: 1.0.4 + + typescript@4.9.5: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + union-value@1.0.1: + dependencies: + arr-union: 3.1.0 + get-value: 2.0.6 + is-extendable: 0.1.1 + set-value: 2.0.1 + + universalify@2.0.1: {} + + unset-value@1.0.0: + dependencies: + has-value: 0.3.1 + isobject: 3.0.1 + + update-browserslist-db@1.1.4(browserslist@4.27.0): + dependencies: + browserslist: 4.27.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + urix@0.1.0: {} + + use@3.1.1: {} + + util-deprecate@1.0.2: {} + + v8-compile-cache-lib@3.0.1: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + vary@1.1.2: {} + + vite-plugin-svg-icons@2.0.1(vite@4.5.14(@types/node@16.18.126)(sass@1.93.3)): + dependencies: + '@types/svgo': 2.6.4 + cors: 2.8.5 + debug: 4.4.3 + etag: 1.8.1 + fs-extra: 10.1.0 + pathe: 0.2.0 + svg-baker: 1.7.0 + svgo: 2.8.0 + vite: 4.5.14(@types/node@16.18.126)(sass@1.93.3) + transitivePeerDependencies: + - supports-color + + vite@4.5.14(@types/node@16.18.126)(sass@1.93.3): + dependencies: + esbuild: 0.18.20 + postcss: 8.5.6 + rollup: 3.29.5 + optionalDependencies: + '@types/node': 16.18.126 + fsevents: 2.3.3 + sass: 1.93.3 + + vue-demi@0.14.10(vue@3.5.24(typescript@4.9.5)): + dependencies: + vue: 3.5.24(typescript@4.9.5) + + vue-eslint-parser@8.3.0(eslint@8.57.1): + dependencies: + debug: 4.4.3 + eslint: 8.57.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + lodash: 4.17.21 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + vue-i18n@9.14.5(vue@3.5.24(typescript@4.9.5)): + dependencies: + '@intlify/core-base': 9.14.5 + '@intlify/shared': 9.14.5 + '@vue/devtools-api': 6.6.4 + vue: 3.5.24(typescript@4.9.5) + + vue-router@4.6.3(vue@3.5.24(typescript@4.9.5)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.24(typescript@4.9.5) + + vue-tsc@0.35.2(typescript@4.9.5): + dependencies: + '@volar/vue-typescript': 0.35.2 + typescript: 4.9.5 + + vue@3.5.24(typescript@4.9.5): + dependencies: + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-sfc': 3.5.24 + '@vue/runtime-dom': 3.5.24 + '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@4.9.5)) + '@vue/shared': 3.5.24 + optionalDependencies: + typescript: 4.9.5 + + vuedraggable@2.24.3: + dependencies: + sortablejs: 1.10.2 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wildcard@1.1.2: {} + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yaml@1.10.2: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + zrender@5.6.1: + dependencies: + tslib: 2.3.0 diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/frontend/public/favicon.ico differ diff --git a/frontend/readme.md b/frontend/readme.md new file mode 100644 index 0000000..dfaf36f --- /dev/null +++ b/frontend/readme.md @@ -0,0 +1 @@ +这里放置前端项目的readme文件 \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..9fb00e0 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/frontend/src/api/auth/index.ts b/frontend/src/api/auth/index.ts new file mode 100644 index 0000000..2a98388 --- /dev/null +++ b/frontend/src/api/auth/index.ts @@ -0,0 +1,36 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { LoginData, TokenResult, VerifyCode } from './types'; + +/** + * + * @param data {LoginForm} + * @returns + */ +export function loginApi(data: LoginData): AxiosPromise { + return request({ + url: '/user/login', + method: 'post', + params: data + }); +} + +/** + * 注销 + */ +export function logoutApi() { + return request({ + url: '/user/logout', + method: 'post' + }); +} + +/** + * 获取图片验证码 + */ +export function getCaptcha(): AxiosPromise { + return request({ + url: '/user/code?t=' + new Date().getTime().toString(), + method: 'get' + }); +} diff --git a/frontend/src/api/auth/types.ts b/frontend/src/api/auth/types.ts new file mode 100644 index 0000000..f6b18ef --- /dev/null +++ b/frontend/src/api/auth/types.ts @@ -0,0 +1,34 @@ +/** + * 登录数据类型 + */ +export interface LoginData { + username: string; + password: string; + code: string; + uuid: string; + /** + * 验证码Code + */ + //verifyCode: string; + /** + * 验证码Code服务端缓存key(UUID) + */ + // verifyCodeKey: string; +} + +/** + * Token响应类型 + */ +export interface TokenResult { + token: string; + refreshToken: string; + expires: number; +} + +/** + * 验证码类型 + */ +export interface VerifyCode { + verifyCodeImg: string; + verifyCodeKey: string; +} diff --git a/frontend/src/api/dept/index.ts b/frontend/src/api/dept/index.ts new file mode 100644 index 0000000..4ab23f1 --- /dev/null +++ b/frontend/src/api/dept/index.ts @@ -0,0 +1,139 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { DeptForm, DeptQuery, Dept } from './types'; + + +//获取组织架构 + +export function getTreelist(queryParams:any) { + return request({ + url: '/system/organization/getOrgTree', + method: 'POST', + params: queryParams + }); +} +//新增企业或部门 + +export function addTreelist(queryParams:any) { + return request({ + url: '/system/organization/addOrg', + method: 'POST', + data: queryParams + }); +} +// 修改企业部门 + +export function updataTreelist(queryParams:any) { + return request({ + url: '/system/organization/updateById', + method: 'POST', + data: queryParams + }); +} +//删除企业 +export function delTreelist(queryParams:any) { + return request({ + url: '/system/organization/deleteById', + method: 'POST', + params: queryParams + }); +} +//获取部门信息 +export function gettableData(queryParams:any) { + return request({ + url: '/system/organization/getOrganizationById', + method: 'POST', + params: queryParams + }); +} +//部门是否有效 +export function deptIsVaild(queryParams:any) { + return request({ + url: '/system/organization/setIsValid', + method: 'POST', + params: queryParams + }); +} + +//修改部门信息 +export function reviseDepartment(queryParams:any) { + return request({ + url: '/system/organization/updateById', + method: 'POST', + data: queryParams + }); +} + +/** + * 部门树形表格 + * + * @param queryParams + */ +export function listDepartments(queryParams?: DeptQuery): AxiosPromise { + return request({ + url: '/api/v1/dept', + method: 'get', + params: queryParams + }); +} + +/** + * 部门下拉列表 + */ +export function listDeptOptions(): AxiosPromise { + return request({ + url: '/api/v1/dept/options', + method: 'get' + }); +} + +/** + * 获取部门详情 + * + * @param id + */ +export function getDeptForm(id: string): AxiosPromise { + return request({ + url: '/api/v1/dept/' + id + '/form', + method: 'get' + }); +} + +/** + * 新增部门 + * + * @param data + */ +export function addDept(data: DeptForm) { + return request({ + url: '/api/v1/dept', + method: 'post', + data: data + }); +} + +/** + * 修改部门 + * + * @param id + * @param data + */ +export function updateDept(id: string, data: DeptForm) { + return request({ + url: '/api/v1/dept/' + id, + method: 'put', + data: data + }); +} + +/** + * 删除部门 + * + * @param ids + */ +export function deleteDept(ids: string) { + return request({ + url: '/api/v1/dept/' + ids, + method: 'delete' + }); +} diff --git a/frontend/src/api/dept/types.ts b/frontend/src/api/dept/types.ts new file mode 100644 index 0000000..b99f819 --- /dev/null +++ b/frontend/src/api/dept/types.ts @@ -0,0 +1,34 @@ +/** + * 部门查询参数 + */ +export interface DeptQuery { + keywords: string | undefined; + status: number | undefined; +} + +/** + * 部门类型 + */ +export interface Dept { + id: string; + name: string; + parentId: string; + treePath: string; + sort: number; + status: number; + leader?: string; + mobile?: string; + email?: string; + children: Dept[]; +} + +/** + * 部门表单类型 + */ +export interface DeptForm { + id?: string; + parentId: string; + name: string; + sort: number; + status: number; +} diff --git a/frontend/src/api/dict/index.ts b/frontend/src/api/dict/index.ts new file mode 100644 index 0000000..7730676 --- /dev/null +++ b/frontend/src/api/dict/index.ts @@ -0,0 +1,242 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { + DictQuery, + DictPageResult, + DictTypeForm, + DictItemQuery, + DictItemPageResult, + DictItemForm +} from './types'; +// 查询字典 +export function getTreelist(params:any) { + return request({ + url: '/system/dictionary/dictList', + method: 'get', + params: params + }); +} + +// 新增字典 +export function addDict(params:any) { + return request({ + url: '/system/dictionary/addDict', + method: 'post', + data: params + }); +} + +// 修改字典 +export function updateDict(params:any) { + return request({ + url: '/system/dictionary/updateDict', + method: 'post', + data: params + }); +} +// 删除字典 +export function deleteById(params:any) { + return request({ + url: '/system/dictionary/deleteById', + method: 'post', + params: params + }); +} +// 字典排序 +export function changeDictOrder(params:any) { + return request({ + url: '/system/dictionary/changeDictOrder', + method: 'post', + params: params + }); +} +// 查询字典项 +export function getDictItemById(params:any) { + return request({ + url: '/system/dictionaryItems/page', + method: 'get', + params: params + }); +} + +// 新增字典项 +export function addDictionaryItem(params:any) { + return request({ + url: '/system/dictionaryItems/addDictionaryItem', + method: 'post', + data: params + }); +} + +// 修改字典项 +export function updateDictionaryItem(params:any) { + return request({ + url: '/system/dictionaryItems/updateDictionaryItem', + method: 'post', + data: params + }); +} +// 删除字典项 +export function deleteDictItemById(params:any) { + return request({ + url: '/system/dictionaryItems/deleteDictItemById', + method: 'post', + params: params + }); +} +// 批量删除字典项 +export function deleteDictItemByIds(params:any) { + return request({ + url: '/system/dictionaryItems/deleteDictItemByIds', + method: 'post', + params: params + }); +} + +// 字典项排序 +export function changeItemOrder(params:any) { + return request({ + url: '/system/dictionaryItems/changeItemOrder', + method: 'post', + params: params + }); +} +/** + * 获取字典类型分页列表 + * + * @param queryParams + */ +export function listDictTypePages( + queryParams: DictQuery +): AxiosPromise { + return request({ + url: '/api/v1/dict/types/pages', + method: 'get', + params: queryParams + }); +} + +/** + * 获取字典类型表单数据 + * + * @param id + */ +export function getDictTypeForm(id: number): AxiosPromise { + return request({ + url: '/api/v1/dict/types/' + id + '/form', + method: 'get' + }); +} + +/** + * 新增字典类型 + * + * @param data + */ +export function addDictType(data: DictTypeForm) { + return request({ + url: '/api/v1/dict/types', + method: 'post', + data: data + }); +} + +/** + * 修改字典类型 + * + * @param id + * @param data + */ +export function updateDictType(id: number, data: DictTypeForm) { + return request({ + url: '/api/v1/dict/types/' + id, + method: 'put', + data: data + }); +} + +/** + * 删除字典类型 + */ +export function deleteDictTypes(ids: string) { + return request({ + url: '/api/v1/dict/types/' + ids, + method: 'delete' + }); +} + +/** + * 获取字典类型的数据项 + * + * @param typeCode 字典类型编码 + */ +export function getDictionaries(typeCode: string): AxiosPromise { + return request({ + url: '/api/v1/dict/types/' + typeCode + '/items', + method: 'get' + }); +} + +/** + * 获取字典项分页列表 + */ +export function listDictItemPages( + queryParams: DictItemQuery +): AxiosPromise { + return request({ + url: '/api/v1/dict/items/pages', + method: 'get', + params: queryParams + }); +} + +/** + * 获取字典数据项表单数据 + * + * @param id + */ +export function getDictItemData(id: number): AxiosPromise { + return request({ + url: '/api/v1/dict/items/' + id + '/form', + method: 'get' + }); +} + +/** + * 新增字典项 + * + * @param data + */ +export function saveDictItem(data: DictItemForm) { + return request({ + url: '/api/v1/dict/items', + method: 'post', + data: data + }); +} + +/** + * 修改字典项 + * + * @param id + * @param data + */ +export function updateDictItem(id: number, data: DictItemForm) { + return request({ + url: '/api/v1/dict/items/' + id, + method: 'put', + data: data + }); +} + +/** + * 批量删除字典数据项 + * + * @param ids 字典项ID,多个以英文逗号(,)分割 + */ +export function deleteDictItems(ids: string) { + return request({ + url: '/api/v1/dict/items/' + ids, + method: 'delete' + }); +} diff --git a/frontend/src/api/dict/types.ts b/frontend/src/api/dict/types.ts new file mode 100644 index 0000000..3e301f2 --- /dev/null +++ b/frontend/src/api/dict/types.ts @@ -0,0 +1,84 @@ +/** + * 字典查询参数 + */ +export interface DictQuery extends PageQuery { + /** + * 字典名称 + */ + name?: string; +} + +/** + * 字典类型 + */ +export interface Dict { + id: number; + code: string; + name: string; + status: number; + remark: string; +} + +/** + * 字典分页项类型声明 + */ +export type DictPageResult = PageResult; + +/** + * 字典表单类型声明 + */ +export interface DictTypeForm { + id: number | undefined; + name: string; + code: string; + status: number; + remark: string; +} + +/** + * 字典项查询参数类型声明 + */ +export interface DictItemQuery extends PageQuery { + /** + * 字典项名称 + */ + name?: string; + /** + * 字典类型编码 + */ + typeCode?: string; +} + +/** + * 字典数据项类型 + */ +export interface DictItem { + id: number; + name: string; + value: string; + typeCode: string; + sort: number; + status: number; + defaulted: number; + remark?: string; +} + +/** + * 字典分页项类型声明 + */ +export type DictItemPageResult = PageResult; + +/** + * 字典表单类型声明 + */ +export interface DictItemForm { + id?: number; + typeCode?: string; + typeName?: string; + name: string; + code: string; + value: string; + status: number; + sort: number; + remark: string; +} diff --git a/frontend/src/api/file/index.ts b/frontend/src/api/file/index.ts new file mode 100644 index 0000000..350ce7d --- /dev/null +++ b/frontend/src/api/file/index.ts @@ -0,0 +1,34 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { FileInfo } from './types'; + +/** + * 上传文件 + * + * @param file + */ +export function uploadFileApi(file: File): AxiosPromise { + const formData = new FormData(); + formData.append('file', file); + return request({ + url: '/api/v1/files', + method: 'post', + data: formData, + headers: { + 'Content-Type': 'multipart/form-data' + } + }); +} + +/** + * 删除文件 + * + * @param filePath 文件完整路径 + */ +export function deleteFileApi(filePath?: string) { + return request({ + url: '/api/v1/files', + method: 'delete', + params: { filePath: filePath } + }); +} diff --git a/frontend/src/api/file/types.ts b/frontend/src/api/file/types.ts new file mode 100644 index 0000000..22b2be5 --- /dev/null +++ b/frontend/src/api/file/types.ts @@ -0,0 +1,7 @@ +/** + * 文件API类型声明 + */ +export interface FileInfo { + name: string; + url: string; +} diff --git a/frontend/src/api/menu/index.ts b/frontend/src/api/menu/index.ts new file mode 100644 index 0000000..fae4404 --- /dev/null +++ b/frontend/src/api/menu/index.ts @@ -0,0 +1,159 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { MenuQuery, Menu, Resource, MenuForm } from './types'; + +//获取菜单表格 +export function getdata(queryParams:any) { + return request({ + url: '/system/menu/getMenuButtonTree', + method: 'post', + params: queryParams + }); +} +//新增目录 +export function addmenu(queryParams:any) { + return request({ + url: '/system/menu/addMenu', + method: 'post', + data: queryParams + }); +} +//修改目录 +export function editmenu(queryParams:any) { + return request({ + url: '/system/menu/updateById', + method: 'post', + data: queryParams + }); +} +//删除 +export function deltmenu(queryParams:any) { + return request({ + url: '/system/menu/deleteById', + method: 'post', + params: queryParams + }); +} +//排序 +export function moveOrderno(params:any) { + return request({ + url: '/system/menu/changeMenuOrder', + method: 'post', + params: params + }); +} +//上传图标之前获取ID + +//上传单个图标 +export function uploadIcon (data:any) { + return request({ + headers: { + 'Content-Type': 'multipart/form-data' + }, + url: '/system/menu/uploadIcon', + method: 'POST', + data + }); +} +//删除单个图标 +export function moveIcon(params:any) { + return request({ + url: '/system/menu/deleteIcon', + method: 'post', + params: params + }); +} + + +/** + * 获取路由列表 + */ +export function listRoutes() { + return request({ + url: 'system/menu/treeRoutes', + method: 'get', + }); +} + +/** + * 获取菜单表格列表 + * + * @param queryParams + */ +export function listMenus(queryParams: MenuQuery): AxiosPromise { + return request({ + url: '/api/v1/menus', + method: 'get', + params: queryParams + }); +} + +/** + * 获取菜单下拉树形列表 + */ +export function listMenuOptions(): AxiosPromise { + return request({ + url: '/api/v1/menus/options', + method: 'get' + }); +} + +/** + * 获取资源(菜单+权限)树形列表 + */ +export function listResources(): AxiosPromise { + return request({ + url: '/api/v1/menus/resources', + method: 'get' + }); +} + +/** + * 获取菜单详情 + * @param id + */ +export function getMenuDetail(id: string): AxiosPromise { + return request({ + url: '/api/v1/menus/' + id, + method: 'get' + }); +} + +/** + * 添加菜单 + * + * @param data + */ +export function addMenu(data: MenuForm) { + return request({ + url: '/api/v1/menus', + method: 'post', + data: data + }); +} + +/** + * 修改菜单 + * + * @param id + * @param data + */ +export function updateMenu(id: string, data: MenuForm) { + return request({ + url: '/api/v1/menus/' + id, + method: 'put', + data: data + }); +} + +/** + * 批量删除菜单 + * + * @param ids 菜单ID,多个以英文逗号(,)分割 + */ +export function deleteMenus(ids: string) { + return request({ + url: '/api/v1/menus/' + ids, + method: 'delete' + }); +} diff --git a/frontend/src/api/menu/types.ts b/frontend/src/api/menu/types.ts new file mode 100644 index 0000000..8de7056 --- /dev/null +++ b/frontend/src/api/menu/types.ts @@ -0,0 +1,105 @@ +/** + * 菜单查询参数类型声明 + */ +export interface MenuQuery { + keywords?: string; +} + +/** + * 菜单分页列表项声明 + */ + +export interface Menu { + id?: number; + parentId: number; + type?: string | 'CATEGORY' | 'MENU' | 'EXTLINK'; + createTime: string; + updateTime: string; + name: string; + icon: string; + component: string; + sort: number; + visible: number; + children: Menu[]; +} + +/** + * 菜单表单类型声明 + */ +export interface MenuForm { + /** + * 菜单ID + */ + id?: string; + /** + * 父菜单ID + */ + parentId: string; + /** + * 菜单名称 + */ + name: string; + /** + * 菜单是否可见(1:是;0:否;) + */ + visible: number; + icon?: string; + /** + * 排序 + */ + sort: number; + /** + * 组件路径 + */ + component?: string; + /** + * 路由路径 + */ + path: string; + /** + * 跳转路由路径 + */ + redirect?: string; + + /** + * 菜单类型 + */ + type: string; + + /** + * 权限标识 + */ + perm?: string; +} + +/** + * 资源(菜单+权限)类型 + */ +export interface Resource { + /** + * 菜单值 + */ + value: string; + /** + * 菜单文本 + */ + label: string; + /** + * 子菜单 + */ + children: Resource[]; +} + +/** + * 权限类型 + */ +export interface Permission { + /** + * 权限值 + */ + value: string; + /** + * 权限文本 + */ + label: string; +} diff --git a/frontend/src/api/message/index.ts b/frontend/src/api/message/index.ts new file mode 100644 index 0000000..0280648 --- /dev/null +++ b/frontend/src/api/message/index.ts @@ -0,0 +1,28 @@ +import request from '@/utils/request'; + +export function getMessageList(params:any) { + return request({ + url: '/system/message/getMessageList', + method: 'get', + params + }); +} +export function setMessageStatus(data:any) { + return request({ + url: '/system/message/setMessageStatus?id=' + data, + method: 'post' + }); +} +export function setAllMessageStatus() { + return request({ + url: '/system/message/setAllMessageStatus', + method: 'post' + }); +} +export function deleteMessageById(data:any) { + return request({ + url: '/system/message/deleteMessageById?id=' + data, + method: 'post', + data + }); +} \ No newline at end of file diff --git a/frontend/src/api/record/index.ts b/frontend/src/api/record/index.ts new file mode 100644 index 0000000..9f263b1 --- /dev/null +++ b/frontend/src/api/record/index.ts @@ -0,0 +1,18 @@ +import request from '@/utils/request'; + +//获取所有角色 +export function getLogList(params:any){ + return request({ + url: '/system/log/getLogList' , + method: 'post', + params: params + }); +} +export function exportExcel(queryParams: any) { + return request({ + url: '/system/log/exportExcel', + method: 'get', + params: queryParams, + responseType: 'arraybuffer' + }); +} diff --git a/frontend/src/api/role/index.ts b/frontend/src/api/role/index.ts new file mode 100644 index 0000000..e6420bf --- /dev/null +++ b/frontend/src/api/role/index.ts @@ -0,0 +1,196 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { RoleQuery, RoleForm } from './types'; + +//获取所有角色 +export function listRolePages(queryParams:any){ + return request({ + url: '/system/role/list' , + method: 'post', + params:queryParams + }); +} +//角色是否有效 +export function isvaildTo(queryParams:any){ + return request({ + url: '/system/role/setIsvaild' , + method: 'post', + params:queryParams + }); +} +//新增角色 +export function addDept(queryParams:any){ + return request({ + url:'/system/role/addRole' , + method: 'post', + data: queryParams + }); +} +//更新角色信息 +export function renewDept (queryParams:any){ + return request({ + url:'/system/role/updateById' , + method: 'post', + data: queryParams + }); +} +//单个删除角色 +export function deleDept (queryParams:any){ + return request({ + url:'/system/role/deleteById' , + method: 'post', + params: queryParams + }); +} + +//获取分配权限 +export function assignmentPer (queryParams:any){ + return request({ + url:'/system/menu/permissionAssignment' , + method: 'post', + params: queryParams + }); +} +//发出分配权限 +export function setMenuById (queryParams:any){ + return request({ + url:'/system/role/setMenuById' , + method: 'post', + params: queryParams + }); +} +////获取组织范围 +export function setOrgscope (queryParams:any){ + return request({ + url:'/system/organization/getOrgScopeTree' , + method: 'post', + params: queryParams + }); +} +//修改组织范围 +export function postOrgscope (queryParams:any){ + return request({ + url:'/system/role/setOrgscope' , + method: 'post', + params: queryParams + }); +} + + +//删除角色 +// export function delDept(queryParams:any){ +// return request({ +// url:'/system/role/addRole' , +// method: 'post', +// data: queryParams +// }); +// } + +/** + * 获取角色分页数据 + * + * @param queryParam + */ +// export function listRolePages( +// queryParams?: RoleQuery +// ): AxiosPromise { +// return request({ +// url: '/system/role/list', +// method: 'post', +// params: queryParams +// }); +// } + +/** + * 获取角色下拉数据 + * + * @param queryParams + */ +export function listRoleOptions( + queryParams?: RoleQuery +): AxiosPromise { + return request({ + url: '/api/v1/roles/options', + method: 'get', + params: queryParams + }); +} + +/** + * 获取角色拥有的资源ID集合 + * + * @param queryParams + */ +export function getRoleMenuIds(roleId: string): AxiosPromise { + return request({ + url: '/api/v1/roles/' + roleId + '/menuIds', + method: 'get' + }); +} + +/** + * 修改角色资源权限 + * + * @param queryParams + */ +export function updateRoleMenus( + roleId: string, + data: number[] +): AxiosPromise { + return request({ + url: '/api/v1/roles/' + roleId + '/menus', + method: 'put', + data: data + }); +} + +/** + * 获取角色详情 + * + * @param id + */ +export function getRoleDetail(id: number): AxiosPromise { + return request({ + url: '/api/v1/roles/' + id, + method: 'get' + }); +} + +/** + * 添加角色 + * + * @param data + */ +export function addRole(data: RoleForm) { + return request({ + url: '/api/v1/roles', + method: 'post', + data: data + }); +} + +/** + * 更新角色 + * + * @param id + * @param data + */ +export function updateRole(id: number, data: RoleForm) { + return request({ + url: '/api/v1/roles/' + id, + method: 'put', + data: data + }); +} + +/** + * 批量删除角色,多个以英文逗号(,)分割 + * + * @param ids + */ +export function deleteRoles(ids: string) { + return request({ + url: '/api/v1/roles/' + ids, + method: 'delete' + }); +} diff --git a/frontend/src/api/role/types.ts b/frontend/src/api/role/types.ts new file mode 100644 index 0000000..4684c3b --- /dev/null +++ b/frontend/src/api/role/types.ts @@ -0,0 +1,40 @@ +/** + * 角色查询参数类型 + */ +export interface RoleQuery extends PageQuery { + keywords?: string; +} + +/** + * 角色分页列表项 + */ +export interface Role { + id: string; + name: string; + code: string; + sort: number; + status: number; + deleted: number; + menuIds?: any; + permissionIds?: any; +} + +/** + * 角色分页项类型 + */ +export type RolePageResult = PageResult; + +/** + * 角色表单 + */ +export interface RoleForm { + id?: number; + name: string; + code: string; + sort: number; + status: number; + /** + * 数据权限 + */ + dataScope: number; +} diff --git a/frontend/src/api/taxkSetting/index.ts b/frontend/src/api/taxkSetting/index.ts new file mode 100644 index 0000000..28ec26d --- /dev/null +++ b/frontend/src/api/taxkSetting/index.ts @@ -0,0 +1,51 @@ +import request from '@/utils/request'; + +//获取表格内容 +export function getTaskList(params: any) { + return request({ + url: '/system/quartzjob/getQuartzJobList', + method: 'get', + params: params + }); +} +//新增表格内容 +export function addTaskList(params: any) { + return request({ + url: '/system/quartzjob/addQuartzJob', + method: 'post', + data: params + }); +} + +//删除定时任务 +export function delTaskList(params: any) { + return request({ + url: '/system/quartzjob/deleteQuartzJob', + method: 'post', + params: params + }); +} +//修改定时任务 +export function updataTaskList(params: any) { + return request({ + url: '/system/quartzjob/updateQuartzJob', + method: 'post', + data: params + }); +} +//定时任务是否有效 +export function setTaskList(params: any) { + return request({ + url: '/system/quartzjob/setQuartzStatus', + method: 'post', + params: params + }); +} +//拖拽 +export function changeItemOrder(params: any) { + return request({ + url: '/system/quartzjob/changeDictOrder', + method: 'post', + params: params + }); +} \ No newline at end of file diff --git a/frontend/src/api/user/index.ts b/frontend/src/api/user/index.ts new file mode 100644 index 0000000..367454c --- /dev/null +++ b/frontend/src/api/user/index.ts @@ -0,0 +1,238 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { UserForm, UserInfo, UserPageResult, UserQuery } from './types'; +//获取企业树 数据 +export function getTreelist(queryParams:any) { + return request({ + url: '/system/organization/getOrgTree', + method: 'POST', + params: queryParams + }); +} +//获取用户列表信息 +export function gettableData(queryParams:any) { + return request({ + url: '/system/user/queryUsers', + method: 'get', + params: queryParams + }); +} +//用户-禁用,启用 +export function DataStatus (queryParams:any) { + return request({ + url: '/system/user/setStatus', + method: 'POST', + params: queryParams + }); +} +//删除用户 +export function deltableData (queryParams:any) { + return request({ + url: '/system/user/deleteById', + method: 'POST', + params: queryParams + }); +} +//批量删除 +export function delChoise (queryParams:any) { + return request({ + url: '/system/user/deleteUserByIds', + method: 'POST', + params: queryParams + }); +} +//获取角色 +export function getRole (queryParams:any) { + return request({ + url: '/system/role/list', + method: 'POST', + params: queryParams + }); +} +//新建用户 +export function addUsers (queryParams:any,roleids:any) { + return request({ + url: '/system/user/addUser?'+'roleids='+roleids, + method: 'POST', + data: queryParams, + }); +} +//更改用户 +export function updataUser (queryParams:any,roleids:any) { + return request({ + url: '/system/user/updateUser?'+'roleids='+roleids, + method: 'POST', + data: queryParams, + }); +} +//更改用户 +export function updatePersonalInfo (queryParams:any) { + return request({ + url: '/user/updatePersonalInfo', + method: 'POST', + data: queryParams, + }); +} +//更改头像 +export function updateAvatar (data:any) { + return request({ + headers: { + 'Content-Type': 'multipart/form-data' + }, + url: '/system/user/updateAvatar', + method: 'POST', + data + }); +} + + +//重置密码 +export function setpass (queryParams:any) { + return request({ + url: '/system/user/resetPassword', + method: 'POST', + params: queryParams, + }); +} + +export function updatePassword (queryParams:any) { + return request({ + url: '/user/updatePassword', + method: 'GET', + params: queryParams, + }); +} + +/** + * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) + */ +export function getUserInfo(): AxiosPromise { + return request({ + url: '/user/me', + method: 'get' + }); +} + +/** + * 获取用户分页列表 + * + * @param queryParams + */ +export function listUserPages( + queryParams: UserQuery +): AxiosPromise { + return request({ + url: '/api/v1/users/pages', + method: 'get', + params: queryParams + }); +} + +/** + * 获取用户表单详情 + * + * @param userId + */ +export function getUserForm(userId: number): AxiosPromise { + return request({ + url: '/api/v1/users/' + userId + '/form', + method: 'get' + }); +} + +/** + * 添加用户 + * + * @param data + */ +export function addUser(data: any) { + return request({ + url: '/api/v1/users', + method: 'post', + data: data + }); +} + +/** + * 修改用户 + * + * @param id + * @param data + */ +export function updateUser(id: number, data: UserForm) { + return request({ + url: '/api/v1/users/' + id, + method: 'put', + data: data + }); +} + +/** + * 修改用户状态 + * + * @param id + * @param status + */ +export function updateUserStatus(id: number, status: number) { + return request({ + url: '/api/v1/users/' + id + '/status', + method: 'patch', + params: { status: status } + }); +} + +/** + * 修改用户密码 + * + * @param id + * @param password + */ +export function updateUserPassword(id: number, password: string) { + return request({ + url: '/api/v1/users/' + id + '/password', + method: 'patch', + params: { password: password } + }); +} + +/** + * 删除用户 + * + * @param ids + */ +export function deleteUsers(ids: string) { + return request({ + url: '/api/v1/users/' + ids, + method: 'delete' + }); +} + +/** + * 下载用户导入模板 + * + * @returns + */ +export function downloadTemplate() { + return request({ + url: '/api/v1/users/template', + method: 'get', + responseType: 'arraybuffer' + }); +} + +/** + * 导出用户 + * + * @param queryParams + * @returns + */ +export function exportUser(queryParams: UserQuery) { + return request({ + url: '/api/v1/users/_export', + method: 'get', + params: queryParams, + responseType: 'arraybuffer' + }); +} + + diff --git a/frontend/src/api/user/types.ts b/frontend/src/api/user/types.ts new file mode 100644 index 0000000..07b734b --- /dev/null +++ b/frontend/src/api/user/types.ts @@ -0,0 +1,67 @@ +/** + * 登录用户信息 + */ +export interface UserInfo { + permissions: string[]; + userInfo: any; + nickname: string; + avatar: string; + roles: string[]; + perms: string[]; +} + +/** + * 用户查询参数 + */ +export interface UserQuery extends PageQuery { + keywords: string; + status: number; + deptId: number; +} + +/** + * 用户分页列表项声明 + */ +export interface UserType { + id: string; + username: string; + nickname: string; + mobile: string; + gender: number; + avatar: string; + email: string; + status: number; + deptName: string; + roleNames: string; + createTime: string; +} + +/** + * 用户分页项类型声明 + */ +export type UserPageResult = PageResult; + +/** + * 用户表单类型声明 + */ +export interface UserForm { + id: number | undefined; + deptId: number; + username: string; + nickname: string; + password: string; + mobile: string; + email: string; + gender: number; + status: number; + remark: string; + roleIds: number[]; +} + +/** + * 用户导入表单类型声明 + */ +export interface UserImportData { + deptId: number; + roleIds: number[]; +} diff --git a/frontend/src/assets/401_images/401.gif b/frontend/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/frontend/src/assets/401_images/401.gif differ diff --git a/frontend/src/assets/404_images/404.png b/frontend/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/frontend/src/assets/404_images/404.png differ diff --git a/frontend/src/assets/404_images/404_cloud.png b/frontend/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/frontend/src/assets/404_images/404_cloud.png differ diff --git a/frontend/src/assets/MenuIcon/bq_gb.png b/frontend/src/assets/MenuIcon/bq_gb.png new file mode 100644 index 0000000..092115b Binary files /dev/null and b/frontend/src/assets/MenuIcon/bq_gb.png differ diff --git a/frontend/src/assets/MenuIcon/bq_gb1.png b/frontend/src/assets/MenuIcon/bq_gb1.png new file mode 100644 index 0000000..fd22718 Binary files /dev/null and b/frontend/src/assets/MenuIcon/bq_gb1.png differ diff --git a/frontend/src/assets/MenuIcon/czan_xz.png b/frontend/src/assets/MenuIcon/czan_xz.png new file mode 100644 index 0000000..fe03405 Binary files /dev/null and b/frontend/src/assets/MenuIcon/czan_xz.png differ diff --git a/frontend/src/assets/MenuIcon/dh_jt.png b/frontend/src/assets/MenuIcon/dh_jt.png new file mode 100644 index 0000000..e1ace57 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_jt.png differ diff --git a/frontend/src/assets/MenuIcon/dh_jt1.png b/frontend/src/assets/MenuIcon/dh_jt1.png new file mode 100644 index 0000000..3055566 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_jt1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_qy.png b/frontend/src/assets/MenuIcon/dh_qy.png new file mode 100644 index 0000000..e7e0563 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_qy.png differ diff --git a/frontend/src/assets/MenuIcon/dh_qy1.png b/frontend/src/assets/MenuIcon/dh_qy1.png new file mode 100644 index 0000000..85742ed Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_qy1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_qz.png b/frontend/src/assets/MenuIcon/dh_qz.png new file mode 100644 index 0000000..54ce7e4 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_qz.png differ diff --git a/frontend/src/assets/MenuIcon/dh_qz1.png b/frontend/src/assets/MenuIcon/dh_qz1.png new file mode 100644 index 0000000..36632cd Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_qz1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_sq.png b/frontend/src/assets/MenuIcon/dh_sq.png new file mode 100644 index 0000000..adf9805 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_sq.png differ diff --git a/frontend/src/assets/MenuIcon/dh_sq1.png b/frontend/src/assets/MenuIcon/dh_sq1.png new file mode 100644 index 0000000..57e1514 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_sq1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_sy.png b/frontend/src/assets/MenuIcon/dh_sy.png new file mode 100644 index 0000000..248c590 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_sy.png differ diff --git a/frontend/src/assets/MenuIcon/dh_sy1.png b/frontend/src/assets/MenuIcon/dh_sy1.png new file mode 100644 index 0000000..d092df7 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_sy1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_sz.png b/frontend/src/assets/MenuIcon/dh_sz.png new file mode 100644 index 0000000..5fc1f6f Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_sz.png differ diff --git a/frontend/src/assets/MenuIcon/dh_sz1.png b/frontend/src/assets/MenuIcon/dh_sz1.png new file mode 100644 index 0000000..1a13ddd Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_sz1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_xm.png b/frontend/src/assets/MenuIcon/dh_xm.png new file mode 100644 index 0000000..a750b02 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_xm.png differ diff --git a/frontend/src/assets/MenuIcon/dh_xm1.png b/frontend/src/assets/MenuIcon/dh_xm1.png new file mode 100644 index 0000000..aead610 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_xm1.png differ diff --git a/frontend/src/assets/MenuIcon/dh_xx.png b/frontend/src/assets/MenuIcon/dh_xx.png new file mode 100644 index 0000000..1fb6180 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_xx.png differ diff --git a/frontend/src/assets/MenuIcon/dh_xx1.png b/frontend/src/assets/MenuIcon/dh_xx1.png new file mode 100644 index 0000000..535abe1 Binary files /dev/null and b/frontend/src/assets/MenuIcon/dh_xx1.png differ diff --git a/frontend/src/assets/MenuIcon/gb.png b/frontend/src/assets/MenuIcon/gb.png new file mode 100644 index 0000000..b276de2 Binary files /dev/null and b/frontend/src/assets/MenuIcon/gb.png differ diff --git a/frontend/src/assets/MenuIcon/grzx_tx.png b/frontend/src/assets/MenuIcon/grzx_tx.png new file mode 100644 index 0000000..0f21833 Binary files /dev/null and b/frontend/src/assets/MenuIcon/grzx_tx.png differ diff --git a/frontend/src/assets/MenuIcon/grzx_xg.png b/frontend/src/assets/MenuIcon/grzx_xg.png new file mode 100644 index 0000000..d6c8977 Binary files /dev/null and b/frontend/src/assets/MenuIcon/grzx_xg.png differ diff --git a/frontend/src/assets/MenuIcon/jscz_rl.png b/frontend/src/assets/MenuIcon/jscz_rl.png new file mode 100644 index 0000000..22a323d Binary files /dev/null and b/frontend/src/assets/MenuIcon/jscz_rl.png differ diff --git a/frontend/src/assets/MenuIcon/jscz_sc.png b/frontend/src/assets/MenuIcon/jscz_sc.png new file mode 100644 index 0000000..674cf09 Binary files /dev/null and b/frontend/src/assets/MenuIcon/jscz_sc.png differ diff --git a/frontend/src/assets/MenuIcon/jscz_sc1.png b/frontend/src/assets/MenuIcon/jscz_sc1.png new file mode 100644 index 0000000..cf2e0ca Binary files /dev/null and b/frontend/src/assets/MenuIcon/jscz_sc1.png differ diff --git a/frontend/src/assets/MenuIcon/jscz_scdc.png b/frontend/src/assets/MenuIcon/jscz_scdc.png new file mode 100644 index 0000000..f47a236 Binary files /dev/null and b/frontend/src/assets/MenuIcon/jscz_scdc.png differ diff --git a/frontend/src/assets/MenuIcon/jscz_xz.png b/frontend/src/assets/MenuIcon/jscz_xz.png new file mode 100644 index 0000000..8b2a892 Binary files /dev/null and b/frontend/src/assets/MenuIcon/jscz_xz.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_an.png b/frontend/src/assets/MenuIcon/lbcz_an.png new file mode 100644 index 0000000..3177faf Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_an.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_an1.png b/frontend/src/assets/MenuIcon/lbcz_an1.png new file mode 100644 index 0000000..81e544c Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_an1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_cd.png b/frontend/src/assets/MenuIcon/lbcz_cd.png new file mode 100644 index 0000000..25b8ecc Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_cd.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_cd1.png b/frontend/src/assets/MenuIcon/lbcz_cd1.png new file mode 100644 index 0000000..19aaa85 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_cd1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_czmm.png b/frontend/src/assets/MenuIcon/lbcz_czmm.png new file mode 100644 index 0000000..8f5b131 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_czmm.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_jt.png b/frontend/src/assets/MenuIcon/lbcz_jt.png new file mode 100644 index 0000000..68620fb Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_jt.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_jt1.png b/frontend/src/assets/MenuIcon/lbcz_jt1.png new file mode 100644 index 0000000..b36fe61 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_jt1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_qx.png b/frontend/src/assets/MenuIcon/lbcz_qx.png new file mode 100644 index 0000000..dd4d05e Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_qx.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_sc.png b/frontend/src/assets/MenuIcon/lbcz_sc.png new file mode 100644 index 0000000..2ca9d72 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_sc.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_sc1.png b/frontend/src/assets/MenuIcon/lbcz_sc1.png new file mode 100644 index 0000000..c831998 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_sc1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_sc2.png b/frontend/src/assets/MenuIcon/lbcz_sc2.png new file mode 100644 index 0000000..95ebdb6 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_sc2.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_td.png b/frontend/src/assets/MenuIcon/lbcz_td.png new file mode 100644 index 0000000..54ebc81 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_td.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_td1.png b/frontend/src/assets/MenuIcon/lbcz_td1.png new file mode 100644 index 0000000..3e5b329 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_td1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_td2.png b/frontend/src/assets/MenuIcon/lbcz_td2.png new file mode 100644 index 0000000..6d0a88c Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_td2.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_xg.png b/frontend/src/assets/MenuIcon/lbcz_xg.png new file mode 100644 index 0000000..0533913 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_xg.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_xg1.png b/frontend/src/assets/MenuIcon/lbcz_xg1.png new file mode 100644 index 0000000..0548fbc Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_xg1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_xg2.png b/frontend/src/assets/MenuIcon/lbcz_xg2.png new file mode 100644 index 0000000..ed5a146 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_xg2.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_zml.png b/frontend/src/assets/MenuIcon/lbcz_zml.png new file mode 100644 index 0000000..b352868 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_zml.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_zml1.png b/frontend/src/assets/MenuIcon/lbcz_zml1.png new file mode 100644 index 0000000..618b429 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_zml1.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_zyw.png b/frontend/src/assets/MenuIcon/lbcz_zyw.png new file mode 100644 index 0000000..76e14d1 Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_zyw.png differ diff --git a/frontend/src/assets/MenuIcon/lbcz_zz.png b/frontend/src/assets/MenuIcon/lbcz_zz.png new file mode 100644 index 0000000..36eb52a Binary files /dev/null and b/frontend/src/assets/MenuIcon/lbcz_zz.png differ diff --git a/frontend/src/assets/MenuIcon/top_qp.png b/frontend/src/assets/MenuIcon/top_qp.png new file mode 100644 index 0000000..14ce318 Binary files /dev/null and b/frontend/src/assets/MenuIcon/top_qp.png differ diff --git a/frontend/src/assets/MenuIcon/top_qp1.png b/frontend/src/assets/MenuIcon/top_qp1.png new file mode 100644 index 0000000..dda0cc9 Binary files /dev/null and b/frontend/src/assets/MenuIcon/top_qp1.png differ diff --git a/frontend/src/assets/MenuIcon/top_ss.png b/frontend/src/assets/MenuIcon/top_ss.png new file mode 100644 index 0000000..f053c8b Binary files /dev/null and b/frontend/src/assets/MenuIcon/top_ss.png differ diff --git a/frontend/src/assets/MenuIcon/top_tx.png b/frontend/src/assets/MenuIcon/top_tx.png new file mode 100644 index 0000000..dc301d3 Binary files /dev/null and b/frontend/src/assets/MenuIcon/top_tx.png differ diff --git a/frontend/src/assets/MenuIcon/top_zh.png b/frontend/src/assets/MenuIcon/top_zh.png new file mode 100644 index 0000000..79f945d Binary files /dev/null and b/frontend/src/assets/MenuIcon/top_zh.png differ diff --git a/frontend/src/assets/MenuIcon/ts.png b/frontend/src/assets/MenuIcon/ts.png new file mode 100644 index 0000000..f5787ca Binary files /dev/null and b/frontend/src/assets/MenuIcon/ts.png differ diff --git a/frontend/src/assets/MenuIcon/u117_mouseOver.png b/frontend/src/assets/MenuIcon/u117_mouseOver.png new file mode 100644 index 0000000..1d65625 Binary files /dev/null and b/frontend/src/assets/MenuIcon/u117_mouseOver.png differ diff --git a/frontend/src/assets/MenuIcon/u119.png b/frontend/src/assets/MenuIcon/u119.png new file mode 100644 index 0000000..1520447 Binary files /dev/null and b/frontend/src/assets/MenuIcon/u119.png differ diff --git a/frontend/src/assets/MenuIcon/u241.png b/frontend/src/assets/MenuIcon/u241.png new file mode 100644 index 0000000..250a19f Binary files /dev/null and b/frontend/src/assets/MenuIcon/u241.png differ diff --git a/frontend/src/assets/MenuIcon/u343.png b/frontend/src/assets/MenuIcon/u343.png new file mode 100644 index 0000000..36eb52a Binary files /dev/null and b/frontend/src/assets/MenuIcon/u343.png differ diff --git a/frontend/src/assets/MenuIcon/u697.png b/frontend/src/assets/MenuIcon/u697.png new file mode 100644 index 0000000..e0b432b Binary files /dev/null and b/frontend/src/assets/MenuIcon/u697.png differ diff --git a/frontend/src/assets/MenuIcon/u81.png b/frontend/src/assets/MenuIcon/u81.png new file mode 100644 index 0000000..5fc1f6f Binary files /dev/null and b/frontend/src/assets/MenuIcon/u81.png differ diff --git a/frontend/src/assets/MenuIcon/xqing.png b/frontend/src/assets/MenuIcon/xqing.png new file mode 100644 index 0000000..d54255b Binary files /dev/null and b/frontend/src/assets/MenuIcon/xqing.png differ diff --git a/frontend/src/assets/icons/advert.svg b/frontend/src/assets/icons/advert.svg new file mode 100644 index 0000000..5adcf43 --- /dev/null +++ b/frontend/src/assets/icons/advert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/brand.svg b/frontend/src/assets/icons/brand.svg new file mode 100644 index 0000000..e4b7cee --- /dev/null +++ b/frontend/src/assets/icons/brand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/bug.svg b/frontend/src/assets/icons/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/frontend/src/assets/icons/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/cascader.svg b/frontend/src/assets/icons/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/frontend/src/assets/icons/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/chart.svg b/frontend/src/assets/icons/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/frontend/src/assets/icons/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/client.svg b/frontend/src/assets/icons/client.svg new file mode 100644 index 0000000..ad4bc15 --- /dev/null +++ b/frontend/src/assets/icons/client.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/close.svg b/frontend/src/assets/icons/close.svg new file mode 100644 index 0000000..5b5057f --- /dev/null +++ b/frontend/src/assets/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/close_all.svg b/frontend/src/assets/icons/close_all.svg new file mode 100644 index 0000000..aa13cd7 --- /dev/null +++ b/frontend/src/assets/icons/close_all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/close_left.svg b/frontend/src/assets/icons/close_left.svg new file mode 100644 index 0000000..e5708ea --- /dev/null +++ b/frontend/src/assets/icons/close_left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/close_other.svg b/frontend/src/assets/icons/close_other.svg new file mode 100644 index 0000000..212e6c2 --- /dev/null +++ b/frontend/src/assets/icons/close_other.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/close_right.svg b/frontend/src/assets/icons/close_right.svg new file mode 100644 index 0000000..14d3cf3 --- /dev/null +++ b/frontend/src/assets/icons/close_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/coupon.svg b/frontend/src/assets/icons/coupon.svg new file mode 100644 index 0000000..2f952b2 --- /dev/null +++ b/frontend/src/assets/icons/coupon.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/dashboard.svg b/frontend/src/assets/icons/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/frontend/src/assets/icons/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/dict.svg b/frontend/src/assets/icons/dict.svg new file mode 100644 index 0000000..22a8278 --- /dev/null +++ b/frontend/src/assets/icons/dict.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/frontend/src/assets/icons/dict_item.svg b/frontend/src/assets/icons/dict_item.svg new file mode 100644 index 0000000..903109a --- /dev/null +++ b/frontend/src/assets/icons/dict_item.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/frontend/src/assets/icons/download.svg b/frontend/src/assets/icons/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/frontend/src/assets/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/drag.svg b/frontend/src/assets/icons/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/frontend/src/assets/icons/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/edit.svg b/frontend/src/assets/icons/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/frontend/src/assets/icons/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/exit-fullscreen.svg b/frontend/src/assets/icons/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/frontend/src/assets/icons/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/eye-open.svg b/frontend/src/assets/icons/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/frontend/src/assets/icons/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/eye.svg b/frontend/src/assets/icons/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/frontend/src/assets/icons/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/fullscreen.svg b/frontend/src/assets/icons/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/frontend/src/assets/icons/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/github.svg b/frontend/src/assets/icons/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/frontend/src/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/goods-list.svg b/frontend/src/assets/icons/goods-list.svg new file mode 100644 index 0000000..fcb971e --- /dev/null +++ b/frontend/src/assets/icons/goods-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/goods.svg b/frontend/src/assets/icons/goods.svg new file mode 100644 index 0000000..60c1c73 --- /dev/null +++ b/frontend/src/assets/icons/goods.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/guide.svg b/frontend/src/assets/icons/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/frontend/src/assets/icons/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/homepage.svg b/frontend/src/assets/icons/homepage.svg new file mode 100644 index 0000000..48f4e24 --- /dev/null +++ b/frontend/src/assets/icons/homepage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/lab.svg b/frontend/src/assets/icons/lab.svg new file mode 100644 index 0000000..d4d60aa --- /dev/null +++ b/frontend/src/assets/icons/lab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/language.svg b/frontend/src/assets/icons/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/frontend/src/assets/icons/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/link.svg b/frontend/src/assets/icons/link.svg new file mode 100644 index 0000000..d3f9e5a --- /dev/null +++ b/frontend/src/assets/icons/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/locationIcon.png b/frontend/src/assets/icons/locationIcon.png new file mode 100644 index 0000000..fc01013 Binary files /dev/null and b/frontend/src/assets/icons/locationIcon.png differ diff --git a/frontend/src/assets/icons/menu.svg b/frontend/src/assets/icons/menu.svg new file mode 100644 index 0000000..92c364c --- /dev/null +++ b/frontend/src/assets/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/message.svg b/frontend/src/assets/icons/message.svg new file mode 100644 index 0000000..ea1ddef --- /dev/null +++ b/frontend/src/assets/icons/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/money.svg b/frontend/src/assets/icons/money.svg new file mode 100644 index 0000000..60f7acf --- /dev/null +++ b/frontend/src/assets/icons/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/monitor.svg b/frontend/src/assets/icons/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/frontend/src/assets/icons/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/nested.svg b/frontend/src/assets/icons/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/frontend/src/assets/icons/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/number.svg b/frontend/src/assets/icons/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/frontend/src/assets/icons/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/order.svg b/frontend/src/assets/icons/order.svg new file mode 100644 index 0000000..8f2107e --- /dev/null +++ b/frontend/src/assets/icons/order.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/password.svg b/frontend/src/assets/icons/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/frontend/src/assets/icons/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/peoples.svg b/frontend/src/assets/icons/peoples.svg new file mode 100644 index 0000000..383b82d --- /dev/null +++ b/frontend/src/assets/icons/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/perm.svg b/frontend/src/assets/icons/perm.svg new file mode 100644 index 0000000..b38d065 --- /dev/null +++ b/frontend/src/assets/icons/perm.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/publish.svg b/frontend/src/assets/icons/publish.svg new file mode 100644 index 0000000..e9b489c --- /dev/null +++ b/frontend/src/assets/icons/publish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/qq.svg b/frontend/src/assets/icons/qq.svg new file mode 100644 index 0000000..98da395 --- /dev/null +++ b/frontend/src/assets/icons/qq.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/rabbitmq.svg b/frontend/src/assets/icons/rabbitmq.svg new file mode 100644 index 0000000..65aa198 --- /dev/null +++ b/frontend/src/assets/icons/rabbitmq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/rate.svg b/frontend/src/assets/icons/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/frontend/src/assets/icons/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/redis.svg b/frontend/src/assets/icons/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/frontend/src/assets/icons/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/refresh.svg b/frontend/src/assets/icons/refresh.svg new file mode 100644 index 0000000..1f549f1 --- /dev/null +++ b/frontend/src/assets/icons/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/role.svg b/frontend/src/assets/icons/role.svg new file mode 100644 index 0000000..c484b13 --- /dev/null +++ b/frontend/src/assets/icons/role.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/security.svg b/frontend/src/assets/icons/security.svg new file mode 100644 index 0000000..bcd9d2e --- /dev/null +++ b/frontend/src/assets/icons/security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/shopping.svg b/frontend/src/assets/icons/shopping.svg new file mode 100644 index 0000000..8d2b4bf --- /dev/null +++ b/frontend/src/assets/icons/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/size.svg b/frontend/src/assets/icons/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/frontend/src/assets/icons/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/skill.svg b/frontend/src/assets/icons/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/frontend/src/assets/icons/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/system.svg b/frontend/src/assets/icons/system.svg new file mode 100644 index 0000000..63feb20 --- /dev/null +++ b/frontend/src/assets/icons/system.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/theme.svg b/frontend/src/assets/icons/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/frontend/src/assets/icons/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/tree.svg b/frontend/src/assets/icons/tree.svg new file mode 100644 index 0000000..d40a414 --- /dev/null +++ b/frontend/src/assets/icons/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/user.svg b/frontend/src/assets/icons/user.svg new file mode 100644 index 0000000..e4c7b38 --- /dev/null +++ b/frontend/src/assets/icons/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/uv.svg b/frontend/src/assets/icons/uv.svg new file mode 100644 index 0000000..ca4c301 --- /dev/null +++ b/frontend/src/assets/icons/uv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/valid_code.svg b/frontend/src/assets/icons/valid_code.svg new file mode 100644 index 0000000..39bf478 --- /dev/null +++ b/frontend/src/assets/icons/valid_code.svg @@ -0,0 +1,9 @@ + + + + diff --git a/frontend/src/assets/icons/wechat.svg b/frontend/src/assets/icons/wechat.svg new file mode 100644 index 0000000..35de4bc --- /dev/null +++ b/frontend/src/assets/icons/wechat.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/images/linefeed.png b/frontend/src/assets/images/linefeed.png new file mode 100644 index 0000000..54ebc81 Binary files /dev/null and b/frontend/src/assets/images/linefeed.png differ diff --git a/frontend/src/assets/images/linefeed.png_mouseOver.png b/frontend/src/assets/images/linefeed.png_mouseOver.png new file mode 100644 index 0000000..3e5b329 Binary files /dev/null and b/frontend/src/assets/images/linefeed.png_mouseOver.png differ diff --git a/frontend/src/assets/images/u287.gif b/frontend/src/assets/images/u287.gif new file mode 100644 index 0000000..89bcf89 Binary files /dev/null and b/frontend/src/assets/images/u287.gif differ diff --git a/frontend/src/assets/images/u3127.png b/frontend/src/assets/images/u3127.png new file mode 100644 index 0000000..a77833e Binary files /dev/null and b/frontend/src/assets/images/u3127.png differ diff --git a/frontend/src/assets/images/u3139.png b/frontend/src/assets/images/u3139.png new file mode 100644 index 0000000..c6347d7 Binary files /dev/null and b/frontend/src/assets/images/u3139.png differ diff --git a/frontend/src/assets/index/indicator.png b/frontend/src/assets/index/indicator.png new file mode 100644 index 0000000..2f53da6 Binary files /dev/null and b/frontend/src/assets/index/indicator.png differ diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png new file mode 100644 index 0000000..f3d2503 Binary files /dev/null and b/frontend/src/assets/logo.png differ diff --git a/frontend/src/components.d.ts b/frontend/src/components.d.ts new file mode 100644 index 0000000..94e8b82 --- /dev/null +++ b/frontend/src/components.d.ts @@ -0,0 +1,9 @@ +// 全局组件类型声明 +import Pagination from '@/components/Pagination/index.vue'; + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Pagination: typeof Pagination; + } +} +export {}; diff --git a/frontend/src/components/Breadcrumb/index.vue b/frontend/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..7323114 --- /dev/null +++ b/frontend/src/components/Breadcrumb/index.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/frontend/src/components/GithubCorner/index.vue b/frontend/src/components/GithubCorner/index.vue new file mode 100644 index 0000000..c9a4b32 --- /dev/null +++ b/frontend/src/components/GithubCorner/index.vue @@ -0,0 +1,59 @@ + + + diff --git a/frontend/src/components/Hamburger/index.vue b/frontend/src/components/Hamburger/index.vue new file mode 100644 index 0000000..ec2a6c3 --- /dev/null +++ b/frontend/src/components/Hamburger/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/components/IconSelect/index.vue b/frontend/src/components/IconSelect/index.vue new file mode 100644 index 0000000..9670dcf --- /dev/null +++ b/frontend/src/components/IconSelect/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/frontend/src/components/LangSelect/index.vue b/frontend/src/components/LangSelect/index.vue new file mode 100644 index 0000000..88cdcd7 --- /dev/null +++ b/frontend/src/components/LangSelect/index.vue @@ -0,0 +1,44 @@ + + + diff --git a/frontend/src/components/Pagination/index.vue b/frontend/src/components/Pagination/index.vue new file mode 100644 index 0000000..7ae0c19 --- /dev/null +++ b/frontend/src/components/Pagination/index.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/frontend/src/components/Pagination/page.vue b/frontend/src/components/Pagination/page.vue new file mode 100644 index 0000000..da64123 --- /dev/null +++ b/frontend/src/components/Pagination/page.vue @@ -0,0 +1,131 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/components/RightPanel/index.vue b/frontend/src/components/RightPanel/index.vue new file mode 100644 index 0000000..dc09179 --- /dev/null +++ b/frontend/src/components/RightPanel/index.vue @@ -0,0 +1,160 @@ + + + + + + + diff --git a/frontend/src/components/Screenfull/index.vue b/frontend/src/components/Screenfull/index.vue new file mode 100644 index 0000000..7e9b837 --- /dev/null +++ b/frontend/src/components/Screenfull/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/frontend/src/components/SizeSelect/index.vue b/frontend/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..d9dcbc1 --- /dev/null +++ b/frontend/src/components/SizeSelect/index.vue @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/components/SvgIcon/index.vue b/frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..19d3cc9 --- /dev/null +++ b/frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/frontend/src/components/ThemePicker/index.vue b/frontend/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..c51ce19 --- /dev/null +++ b/frontend/src/components/ThemePicker/index.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/frontend/src/components/Upload/MultiUpload.vue b/frontend/src/components/Upload/MultiUpload.vue new file mode 100644 index 0000000..5c34226 --- /dev/null +++ b/frontend/src/components/Upload/MultiUpload.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/frontend/src/components/Upload/SingleUpload.vue b/frontend/src/components/Upload/SingleUpload.vue new file mode 100644 index 0000000..9937550 --- /dev/null +++ b/frontend/src/components/Upload/SingleUpload.vue @@ -0,0 +1,97 @@ + + + + + + + diff --git a/frontend/src/components/WangEditor/index.vue b/frontend/src/components/WangEditor/index.vue new file mode 100644 index 0000000..aeab4d1 --- /dev/null +++ b/frontend/src/components/WangEditor/index.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/frontend/src/directive/index.ts b/frontend/src/directive/index.ts new file mode 100644 index 0000000..a966376 --- /dev/null +++ b/frontend/src/directive/index.ts @@ -0,0 +1,2 @@ +export { hasPerm, hasRole } from './permission'; +export { deBounce } from './utils'; diff --git a/frontend/src/directive/permission/index.ts b/frontend/src/directive/permission/index.ts new file mode 100644 index 0000000..2b215dd --- /dev/null +++ b/frontend/src/directive/permission/index.ts @@ -0,0 +1,54 @@ +import { useUserStoreHook } from '@/store/modules/user'; +import { Directive, DirectiveBinding } from 'vue'; + +/** + * 按钮权限校验 + */ +export const hasPerm: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + // 「超级管理员」拥有所有的按钮权限 + const { roles, perms } = useUserStoreHook(); + if (roles.includes('超级管理员')) { + return true; + } + // 「其他角色」按钮权限校验 + const { value } = binding; + if (value) { + const requiredPerms = value; // DOM绑定需要的按钮权限标识 + const hasPerm = perms?.some(perm => { + return requiredPerms.includes(perm); + }); + + if (!hasPerm) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error( + "need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\"" + ); + } + } +}; + +/** + * 角色权限校验 + */ +export const hasRole: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + + if (value) { + const requiredRoles = value; // DOM绑定需要的角色编码 + const { roles } = useUserStoreHook(); + const hasRole = roles.some(perm => { + return requiredRoles.includes(perm); + }); + + if (!hasRole) { + el.parentNode && el.parentNode.removeChild(el); + } + } else { + throw new Error("need roles! Like v-has-role=\"['admin','test']\""); + } + } +}; diff --git a/frontend/src/directive/utils/index.ts b/frontend/src/directive/utils/index.ts new file mode 100644 index 0000000..9eeaef9 --- /dev/null +++ b/frontend/src/directive/utils/index.ts @@ -0,0 +1,15 @@ +import { Directive } from 'vue'; + +/** + * 按钮防抖 + */ +export const deBounce:Directive = { + mounted(el:HTMLElement) { + el.addEventListener('click', () => { + el.classList.add('is-disabled') + setTimeout(() => { + el.classList.remove('is-disabled') + }, 2000) + }) + } +} diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts new file mode 100644 index 0000000..bcddf3e --- /dev/null +++ b/frontend/src/env.d.ts @@ -0,0 +1,19 @@ +/// + +declare module '*.vue' { + import { DefineComponent } from 'vue'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any>; + export default component; +} + +// 环境变量 TypeScript的智能提示 +interface ImportMetaEnv { + VITE_APP_TITLE: string; + VITE_APP_PORT: string; + VITE_APP_BASE_API: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/frontend/src/lang/en.ts b/frontend/src/lang/en.ts new file mode 100644 index 0000000..63b4df7 --- /dev/null +++ b/frontend/src/lang/en.ts @@ -0,0 +1,29 @@ +export default { + // 路由国际化 + route: { + dashboard: 'Dashboard', + document: 'Document' + }, + // 登录页面国际化 + login: { + title: 'Jingyi system', + username: 'Username', + rulesUsername: 'enter one user name', + password: 'Password', + rulesPassword: 'Please input a password', + rulesPasswordPlace: 'The password cannot be less than 6 digits', + login: 'Login', + code: 'Verification Code', + copyright: '', + icp: '', + thirdPartyLogin: 'third-party login', + remember: 'Remember password' + }, + // 导航栏国际化 + navbar: { + dashboard: 'Dashboard', + logout: 'Logout', + document: 'Document', + gitee: 'Gitee' + } +}; diff --git a/frontend/src/lang/index.ts b/frontend/src/lang/index.ts new file mode 100644 index 0000000..5ca3f65 --- /dev/null +++ b/frontend/src/lang/index.ts @@ -0,0 +1,46 @@ +// 自定义国际化配置 +import { createI18n } from 'vue-i18n'; +import { localStorage } from '@/utils/localStorage'; + +// 本地语言包 +import enLocale from './en'; +import zhCnLocale from './zh-cn'; + +const messages = { + 'zh-cn': { + ...zhCnLocale + }, + en: { + ...enLocale + } +}; + +/** + * 获取当前系统使用语言字符串 + * + * @returns zh-cn|en ... + */ +export const getLanguage = () => { + // 本地缓存获取 + let language = localStorage.get('language'); + if (language) { + return language; + } + // 浏览器使用语言 + language = navigator.language.toLowerCase(); + const locales = Object.keys(messages); + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale; + } + } + return 'zh-cn'; +}; + +const i18n = createI18n({ + legacy: false, + locale: getLanguage(), + messages: messages +}); + +export default i18n; diff --git a/frontend/src/lang/zh-cn.ts b/frontend/src/lang/zh-cn.ts new file mode 100644 index 0000000..a52feb6 --- /dev/null +++ b/frontend/src/lang/zh-cn.ts @@ -0,0 +1,28 @@ +export default { + // 路由国际化 + route: { + dashboard: '首页', + document: '项目文档' + }, + // 登录页面国际化 + login: { + title: '公司开发平台框架', + username: '用户名', + rulesUsername: '请输入用户名', + password: '密码', + rulesPassword: '请输入密码', + rulesPasswordPlace: '密码不能少于6位', + login: '登 录', + code: '请输入验证码', + copyright: '', + icp: '', + thirdPartyLogin: '第三方登录', + remember: '记住密码' + }, + navbar: { + dashboard: '首页', + logout: '注销', + document: '项目文档', + gitee: '码云' + } +}; diff --git a/frontend/src/layout/components/AppMain.vue b/frontend/src/layout/components/AppMain.vue new file mode 100644 index 0000000..d83a57d --- /dev/null +++ b/frontend/src/layout/components/AppMain.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/frontend/src/layout/components/Navbar.vue b/frontend/src/layout/components/Navbar.vue new file mode 100644 index 0000000..ce6ab7a --- /dev/null +++ b/frontend/src/layout/components/Navbar.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/frontend/src/layout/components/Settings/index.vue b/frontend/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..605f23a --- /dev/null +++ b/frontend/src/layout/components/Settings/index.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/frontend/src/layout/components/Sidebar/Link.vue b/frontend/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..c592bbd --- /dev/null +++ b/frontend/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,37 @@ + + + diff --git a/frontend/src/layout/components/Sidebar/Logo.vue b/frontend/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..a46a32b --- /dev/null +++ b/frontend/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,33 @@ + + + diff --git a/frontend/src/layout/components/Sidebar/MixNav.vue b/frontend/src/layout/components/Sidebar/MixNav.vue new file mode 100644 index 0000000..55b8a7f --- /dev/null +++ b/frontend/src/layout/components/Sidebar/MixNav.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/frontend/src/layout/components/Sidebar/SidebarItem.vue b/frontend/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..e540a1f --- /dev/null +++ b/frontend/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,125 @@ + + + + diff --git a/frontend/src/layout/components/Sidebar/index.vue b/frontend/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..8fdf4f4 --- /dev/null +++ b/frontend/src/layout/components/Sidebar/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/frontend/src/layout/components/TagsView/ScrollPane.vue b/frontend/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..07f79c3 --- /dev/null +++ b/frontend/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/frontend/src/layout/components/TagsView/index.vue b/frontend/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..1a5e103 --- /dev/null +++ b/frontend/src/layout/components/TagsView/index.vue @@ -0,0 +1,356 @@ + + + + + diff --git a/frontend/src/layout/components/index.ts b/frontend/src/layout/components/index.ts new file mode 100644 index 0000000..4dca96e --- /dev/null +++ b/frontend/src/layout/components/index.ts @@ -0,0 +1,4 @@ +export { default as Navbar } from './Navbar.vue'; +export { default as AppMain } from './AppMain.vue'; +export { default as Settings } from './Settings/index.vue'; +export { default as TagsView } from './TagsView/index.vue'; diff --git a/frontend/src/layout/components/news.vue b/frontend/src/layout/components/news.vue new file mode 100644 index 0000000..165f0c3 --- /dev/null +++ b/frontend/src/layout/components/news.vue @@ -0,0 +1,257 @@ + + + + diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue new file mode 100644 index 0000000..af0c28d --- /dev/null +++ b/frontend/src/layout/index.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..b3a9a56 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,42 @@ +import { createApp, Directive } from 'vue'; +import App from './App.vue'; +import router from '@/router'; +import { setupStore } from '@/store'; + +import ElementPlus from 'element-plus'; +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import Pagination from '@/components/Pagination/index.vue'; +import '@/permission'; + +// 引入svg注册脚本 +import 'virtual:svg-icons-register'; + +// 国际化 +import i18n from '@/lang/index'; + +import '@/styles/index.scss'; +import 'element-plus/theme-chalk/index.css'; +//import 'element-plus/theme-chalk/dark/css-vars.css'; + +const app = createApp(App); +// 自定义指令 +import * as directive from '@/directive'; +Object.keys(directive).forEach(key => { + app.directive(key, (directive as { [key: string]: Directive })[key]); +}); +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +// 全局方法 +import { getDictionaries } from '@/api/dict'; +app.config.globalProperties.$getDictionaries = getDictionaries; + +// 全局挂载 +setupStore(app); +app + .component('Pagination', Pagination) + .use(router) + .use(ElementPlus) + .use(i18n) + .mount('#app'); diff --git a/frontend/src/permission.ts b/frontend/src/permission.ts new file mode 100644 index 0000000..467c7af --- /dev/null +++ b/frontend/src/permission.ts @@ -0,0 +1,63 @@ +import router from '@/router'; +import { RouteRecordRaw } from 'vue-router'; +import { useUserStoreHook } from '@/store/modules/user'; +import { usePermissionStoreHook } from '@/store/modules/permission'; + +import NProgress from 'nprogress'; +import 'nprogress/nprogress.css'; +NProgress.configure({ showSpinner: false }); // 进度条 + +const permissionStore = usePermissionStoreHook(); + +// 白名单路由 +const whiteList = ['/login']; + +router.beforeEach(async (to, from, next) => { + NProgress.start(); + const userStore = useUserStoreHook(); + if (userStore.Token) { + // 登录成功,跳转到首页 + if (to.path === '/login') { + next({ path: '/' }); + NProgress.done(); + } else { + const hasGetUserInfo = userStore.roles.length > 0; + // const hasGetUserInfo = true; + if (hasGetUserInfo) { + if (to.matched.length === 0) { + from.name ? next({ name: from.name as any }) : next('/401'); + } else { + next(); + } + } else { + try { + const { roles } = await userStore.getInfo(); + const accessRoutes: RouteRecordRaw[] = + await permissionStore.generateRoutes(roles); + + accessRoutes.forEach((route: any) => { + router.addRoute(route); + }); + next({ ...to, replace: true }); + } catch (error) { + // 移除 token 并跳转登录页 + await userStore.resetToken(); + next(`/login?redirect=${to.path}`); + NProgress.done(); + } + } + } + } else { + // 未登录可以访问白名单页面 + if (whiteList.indexOf(to.path) !== -1) { + next(); + } else { + next(`/login?redirect=${to.path}`); + NProgress.done(); + } + } +}); + +router.afterEach(() => { + NProgress.done(); +}); diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 0000000..d45a4cf --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,77 @@ +import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; +import { usePermissionStoreHook } from '@/store/modules/permission'; + +export const Layout = () => import('@/layout/index.vue'); + +// 静态路由 +export const constantRoutes: RouteRecordRaw[] = [ + { + path: '/redirect', + component: Layout, + meta: { hidden: true }, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect/index.vue') + } + ] + }, + { + path: '/login', + component: () => import('@/views/login/index.vue'), + meta: { hidden: true } + }, + { + path: '/404', + component: () => import('@/views/error-page/404.vue'), + meta: { hidden: true } + }, + + { + path: '/', + component: Layout, + redirect: '/dashboard', + children: [ + { + path: 'dashboard', + opturl: '/dashboard', + component: () => import('@/views/dashboard/index.vue'), + name: '首页', + icon: 'homepage', + meta: { title: 'dashboard', icon: 'homepage', affix: true } + }, + { + path: 'personalCenter', + component: () => import('@/views/system/user/personalCenter.vue'), + name: '个人中心', + meta: { title: 'personalCenter',hidden: true, icon: 'personalCenter' } + }, + { + path: '401', + component: () => import('@/views/error-page/401.vue'), + meta: { hidden: true } + }, + ] + } +]; + +// 创建路由 +const router = createRouter({ + history: createWebHashHistory(), + routes: constantRoutes as RouteRecordRaw[], + // 刷新时,滚动条位置还原 + scrollBehavior: () => ({ left: 0, top: 0 }) +}); + +// 重置路由 +export function resetRouter() { + const permissionStore = usePermissionStoreHook(); + permissionStore.routes.forEach(route => { + const name = route.name; + if (name && router.hasRoute(name)) { + router.hasRoute(name) && router.removeRoute(name); + } + }); +} + +export default router; diff --git a/frontend/src/settings.ts b/frontend/src/settings.ts new file mode 100644 index 0000000..cd9b78b --- /dev/null +++ b/frontend/src/settings.ts @@ -0,0 +1,23 @@ +interface DefaultSettings { + title: string; + showSettings: boolean; + tagsView: boolean; + fixedHeader: boolean; + sidebarLogo: boolean; + errorLog: string; + layout: string; + theme: string; +} + +const defaultSettings: DefaultSettings = { + title: '公司开发平台框架', + showSettings: false, + tagsView: true, + fixedHeader: true, + sidebarLogo: true, + errorLog: 'production', + layout: 'left', + theme: 'light' +}; + +export default defaultSettings; diff --git a/frontend/src/shims.d.ts b/frontend/src/shims.d.ts new file mode 100644 index 0000000..288c4ac --- /dev/null +++ b/frontend/src/shims.d.ts @@ -0,0 +1,2 @@ +declare module 'jsencrypt' +declare module 'sortablejs' \ No newline at end of file diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts new file mode 100644 index 0000000..fc0ba49 --- /dev/null +++ b/frontend/src/store/index.ts @@ -0,0 +1,11 @@ +import type { App } from 'vue'; +import { createPinia } from 'pinia'; + +const store = createPinia(); + +// 全局挂载store +export function setupStore(app: App) { + app.use(store); +} + +export { store }; diff --git a/frontend/src/store/modules/app.ts b/frontend/src/store/modules/app.ts new file mode 100644 index 0000000..eb4d628 --- /dev/null +++ b/frontend/src/store/modules/app.ts @@ -0,0 +1,97 @@ +import { + getSidebarStatus, + setSidebarStatus, + getSize, + setSize, + setLanguage +} from '@/utils/localStorage'; +import { defineStore } from 'pinia'; +import { getLanguage } from '@/lang/index'; +import { computed, reactive, ref } from 'vue'; +import { useStorage } from '@vueuse/core'; + +// Element Plus 语言包 +import zhCn from 'element-plus/es/locale/lang/zh-cn'; +import en from 'element-plus/es/locale/lang/en'; + +export enum DeviceType { + mobile, + desktop +} + +export enum SizeType { + default, + large, + small +} + +// setup +export const useAppStore = defineStore('app', () => { + // state + const device = useStorage('device', 'desktop'); + const size = ref(getSize() || 'default'); + const language = ref(getLanguage()); + const sidebar = reactive({ + opened: getSidebarStatus() !== 'closed', + withoutAnimation: false + }); + + const locale = computed(() => { + if (language?.value == 'en') { + return en; + } else { + return zhCn; + } + }); + + // actions + function toggleSidebar(withoutAnimation: boolean) { + sidebar.opened = !sidebar.opened; + sidebar.withoutAnimation = withoutAnimation; + if (sidebar.opened) { + setSidebarStatus('opened'); + } else { + setSidebarStatus('closed'); + } + } + + function closeSideBar(withoutAnimation: boolean) { + sidebar.opened = false; + sidebar.withoutAnimation = withoutAnimation; + setSidebarStatus('closed'); + } + + function openSideBar(withoutAnimation: boolean) { + sidebar.opened = true; + sidebar.withoutAnimation = withoutAnimation; + setSidebarStatus('opened'); + } + + function toggleDevice(val: string) { + device.value = val; + } + + function changeSize(val: string) { + size.value = val; + setSize(val); + } + + function changeLanguage(val: string) { + language.value = val; + setLanguage(val); + } + + return { + device, + sidebar, + language, + locale, + size, + toggleDevice, + changeSize, + changeLanguage, + toggleSidebar, + closeSideBar, + openSideBar + }; +}); diff --git a/frontend/src/store/modules/permission.ts b/frontend/src/store/modules/permission.ts new file mode 100644 index 0000000..1e632ea --- /dev/null +++ b/frontend/src/store/modules/permission.ts @@ -0,0 +1,82 @@ +import { RouteRecordRaw } from 'vue-router'; +import { defineStore } from 'pinia'; +import { constantRoutes } from '@/router'; +import { store } from '@/store'; +import { listRoutes } from '@/api/menu'; +import { ref } from 'vue'; + +const modules = import.meta.glob('../../views/**/**.vue'); +export const Layout = () => import('@/layout/index.vue'); + +// const hasPermission = (roles: string[], route: RouteRecordRaw) => { +// if (route.meta && route.meta.roles) { +// if (roles.includes('ROOT')) { +// return true; +// } +// return roles.some(role => { +// if (route.meta?.roles !== undefined) { +// return (route.meta.roles as string[]).includes(role); +// } +// }); +// } +// return false; +// }; + +const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => { + const res: RouteRecordRaw[] = []; + routes.forEach(route => { + const tmp = { ...route } as any; + // if (hasPermission(roles, tmp)) { + tmp.path = tmp.opturl; + if (tmp.type == '0') { + tmp.component = Layout; + } else { + const component = modules[`../../views${tmp.opturl}.vue`] as any; + if (component) { + tmp.component = component; + } else { + tmp.component = modules[`../../views/error-page/404.vue`]; + } + } + res.push(tmp) + if (tmp.children) { + tmp.children = filterAsyncRoutes(tmp.children, roles); + } + // } + }); + return res; +}; + +// setup +export const usePermissionStore = defineStore('permission', () => { + // state + const routes = ref([]); + const addRoutes = ref([]); + + // actions + function setRoutes(newRoutes: RouteRecordRaw[]) { + addRoutes.value = newRoutes; + routes.value = constantRoutes.concat(newRoutes); + } + + function generateRoutes(roles: string[]) { + return new Promise((resolve, reject) => { + listRoutes() + .then(response => { + const asyncRoutes :any = response; + const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles); + setRoutes(accessedRoutes); + resolve(accessedRoutes); + }) + .catch(error => { + reject(error); + }); + }); + } + return { routes, setRoutes, generateRoutes }; +}); + +// 非setup +export function usePermissionStoreHook() { + return usePermissionStore(store); +} diff --git a/frontend/src/store/modules/settings.ts b/frontend/src/store/modules/settings.ts new file mode 100644 index 0000000..0b69453 --- /dev/null +++ b/frontend/src/store/modules/settings.ts @@ -0,0 +1,64 @@ +import { defineStore } from 'pinia'; +import defaultSettings from '../../settings'; +import { ref } from 'vue'; +import { useStorage } from '@vueuse/core'; + +/** + * 主题类型 + */ +export enum ThemeType { + light, + dark +} + +/** + * 布局类型 + */ +export enum LayoutType { + left, + top, + mix +} + +export const useSettingsStore = defineStore('setting', () => { + // state + const showSettings = ref(defaultSettings.showSettings); + const tagsView = useStorage('tagsView', defaultSettings.tagsView); + const fixedHeader = ref(defaultSettings.fixedHeader); + const sidebarLogo = ref(defaultSettings.sidebarLogo); + + const layout = useStorage('layout', defaultSettings.layout); + + // actions + function changeSetting(param: { key: string; value: any }) { + const { key, value } = param; + switch (key) { + case 'showSettings': + showSettings.value = value; + break; + case 'fixedHeader': + fixedHeader.value = value; + break; + case 'tagsView': + tagsView.value = value; + break; + case 'sidevarLogo': + sidebarLogo.value = value; + break; + case 'layout': + layout.value = value; + break; + default: + break; + } + } + + return { + showSettings, + tagsView, + fixedHeader, + sidebarLogo, + layout, + changeSetting + }; +}); diff --git a/frontend/src/store/modules/tagsView.ts b/frontend/src/store/modules/tagsView.ts new file mode 100644 index 0000000..f8a69a2 --- /dev/null +++ b/frontend/src/store/modules/tagsView.ts @@ -0,0 +1,214 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { RouteLocationNormalized } from 'vue-router'; + +export interface TagView extends Partial { + title?: string; +} + +// setup +export const useTagsViewStore = defineStore('tagsView', () => { + // state + const visitedViews = ref([]); + const cachedViews = ref([]); + + // actions + function addVisitedView(view: TagView) { + if (visitedViews.value.some(v => v.path === view.path)) return; + if (view.meta && view.meta.affix) { + visitedViews.value.unshift( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ); + } else { + visitedViews.value.push( + Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }) + ); + } + } + + function addCachedView(view: TagView) { + const viewName = view.name as string; + if (cachedViews.value.includes(viewName)) return; + if (view.meta?.keepAlive) { + cachedViews.value.push(viewName); + } + } + + function delVisitedView(view: TagView) { + return new Promise(resolve => { + for (const [i, v] of visitedViews.value.entries()) { + if (v.path === view.path) { + visitedViews.value.splice(i, 1); + break; + } + } + resolve([...visitedViews.value]); + }); + } + + function delCachedView(view: TagView) { + const viewName = view.name as string; + return new Promise(resolve => { + const index = cachedViews.value.indexOf(viewName); + index > -1 && cachedViews.value.splice(index, 1); + resolve([...cachedViews.value]); + }); + } + + function delOtherVisitedViews(view: TagView) { + return new Promise(resolve => { + visitedViews.value = visitedViews.value.filter(v => { + return v.meta?.affix || v.path === view.path; + }); + resolve([...visitedViews.value]); + }); + } + + function delOtherCachedViews(view: TagView) { + const viewName = view.name as string; + return new Promise(resolve => { + const index = cachedViews.value.indexOf(viewName); + if (index > -1) { + cachedViews.value = cachedViews.value.slice(index, index + 1); + } else { + // if index = -1, there is no cached tags + cachedViews.value = []; + } + resolve([...cachedViews.value]); + }); + } + + function updateVisitedView(view: TagView) { + for (let v of visitedViews.value) { + if (v.path === view.path) { + v = Object.assign(v, view); + break; + } + } + } + + function addView(view: TagView) { + addVisitedView(view); + addCachedView(view); + } + + function delView(view: TagView) { + return new Promise(resolve => { + delVisitedView(view); + delCachedView(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + } + + function delOtherViews(view: TagView) { + return new Promise(resolve => { + delOtherVisitedViews(view); + delOtherCachedViews(view); + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + } + + function delLeftViews(view: TagView) { + return new Promise(resolve => { + const currIndex = visitedViews.value.findIndex(v => v.path === view.path); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + // affix:true 固定tag,例如“首页” + if (index >= currIndex || (item.meta && item.meta.affix)) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name as string); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value] + }); + }); + } + function delRightViews(view: TagView) { + return new Promise(resolve => { + const currIndex = visitedViews.value.findIndex(v => v.path === view.path); + if (currIndex === -1) { + return; + } + visitedViews.value = visitedViews.value.filter((item, index) => { + // affix:true 固定tag,例如“首页” + if (index <= currIndex || (item.meta && item.meta.affix)) { + return true; + } + + const cacheIndex = cachedViews.value.indexOf(item.name as string); + if (cacheIndex > -1) { + cachedViews.value.splice(cacheIndex, 1); + } + return false; + }); + resolve({ + visitedViews: [...visitedViews.value] + }); + }); + } + + function delAllViews() { + return new Promise(resolve => { + const affixTags = visitedViews.value.filter(tag => tag.meta?.affix); + visitedViews.value = affixTags; + cachedViews.value = []; + resolve({ + visitedViews: [...visitedViews.value], + cachedViews: [...cachedViews.value] + }); + }); + } + + function delAllVisitedViews() { + return new Promise(resolve => { + const affixTags = visitedViews.value.filter(tag => tag.meta?.affix); + visitedViews.value = affixTags; + resolve([...visitedViews.value]); + }); + } + + function delAllCachedViews() { + return new Promise(resolve => { + cachedViews.value = []; + resolve([...cachedViews.value]); + }); + } + + return { + visitedViews, + cachedViews, + addVisitedView, + addCachedView, + delVisitedView, + delCachedView, + delOtherVisitedViews, + delOtherCachedViews, + updateVisitedView, + addView, + delView, + delOtherViews, + delLeftViews, + delRightViews, + delAllViews, + delAllVisitedViews, + delAllCachedViews + }; +}); diff --git a/frontend/src/store/modules/user.ts b/frontend/src/store/modules/user.ts new file mode 100644 index 0000000..d825937 --- /dev/null +++ b/frontend/src/store/modules/user.ts @@ -0,0 +1,103 @@ +import { defineStore } from 'pinia'; + +import { getToken, setToken, removeToken } from '@/utils/auth'; +import { loginApi, logoutApi } from '@/api/auth'; +import { getUserInfo } from '@/api/user'; +import { resetRouter } from '@/router'; +import { store } from '@/store'; +import { LoginData } from '@/api/auth/types'; +import { ref } from 'vue'; +import { UserInfo } from '@/api/user/types'; + +export const useUserStore = defineStore('user', () => { + // state + const Token = ref(getToken() || ''); + const nickname = ref(''); + const avatar = ref(''); + const roles = ref>([]); // 用户角色编码集合 → 判断路由权限 + const perms = ref>([]); // 用户权限编码集合 → 判断按钮权限 + const badgeval = ref('') + + // actions + + // 登录 + function login(loginData: LoginData) { + return new Promise((resolve, reject) => { + loginApi(loginData) + .then(response => { + const { token } = response.data; + Token.value = token; + setToken(token); + resolve(); + }) + .catch(error => { + reject(error); + }); + }); + } + + // 获取信息(用户昵称、头像、角色集合、权限集合) + function getInfo() { + return new Promise((resolve, reject) => { + getUserInfo() + .then(({ data }) => { + if (!data) { + return reject('Verification failed, please Login again.'); + } + if (!data.roles || data.roles.length <= 0) { + reject('getUserInfo: roles must be a non-null array!'); + } + nickname.value = data.userInfo.nickname; + avatar.value = data.userInfo.avatar; + roles.value = data.roles; + perms.value = data.permissions; + resolve(data); + }) + .catch(error => { + reject(error); + }); + }); + } + + // 注销 + function logout() { + return new Promise((resolve, reject) => { + logoutApi() + .then(() => { + resetRouter(); + resetToken(); + resolve(); + }) + .catch(error => { + reject(error); + }); + }); + } + + // 重置 + function resetToken() { + removeToken(); + Token.value = ''; + nickname.value = ''; + avatar.value = ''; + roles.value = []; + perms.value = []; + } + return { + Token, + nickname, + avatar, + roles, + perms, + login, + getInfo, + logout, + resetToken, + badgeval + }; +}); + +// 非setup +export function useUserStoreHook() { + return useUserStore(store); +} diff --git a/frontend/src/styles/element-plus.scss b/frontend/src/styles/element-plus.scss new file mode 100644 index 0000000..a54cd78 --- /dev/null +++ b/frontend/src/styles/element-plus.scss @@ -0,0 +1,59 @@ +:root { + // 这里可以设置你自定义的颜色变量 + // 这个是element主要按钮:active的颜色,当主题更改后此变量的值也随之更改 + --el-color-primary-dark: #0d84ff; + // --el-font-size-base: 16px !important; +} +.el-button--large, .el-input--large, .el-table--large, .el-form--large, .el-select__tags-text{ + font-size: 16px !important; + .el-form-item__label{ + font-size: 16px !important; + } + --el-font-size-base: 16px !important; +} + + +// 覆盖 element-plus 的样式 +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type='file'] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +// dropdown +.el-dropdown-menu { + a { + display: block; + } +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +// 选中行背景色值 +.el-table__body tr.current-row td { + background-color: #e1f3d8b5 !important; +} + +// card 的header统一高度 +.el-card__header { + height: 60px !important; +} + +// 表格表头和表体未对齐 +.el-table__header col[name='gutter'] { + display: table-cell !important; +} + + diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss new file mode 100644 index 0000000..e0fa2bc --- /dev/null +++ b/frontend/src/styles/index.scss @@ -0,0 +1,39 @@ +@import 'src/styles/variables.module'; +@import 'src/styles/element-plus'; +@import './sidebar.scss'; +@import './tailwind.scss'; + +html,body,#app{ + height: 100%; +} + +// main-container global css +.app-container { + padding: 20px; +} + +.search{ + padding: 18px 0 0 10px; + margin-bottom: 10px; + border-radius: 5px; + border: 1px solid #ddd; + box-shadow: 6px 2px 6px #CCC; +} + +svg{ + display: inline-block; +} +.el-dialog { + display: flex !important; + flex-direction: column !important; + margin: auto !important; + position: absolute !important; + top: 0 ; + left: 0 ; + right: 0; + bottom: 0; + height: max-content; +} +.el-dialog__body { + padding: 20px !important; +} \ No newline at end of file diff --git a/frontend/src/styles/mixin.scss b/frontend/src/styles/mixin.scss new file mode 100644 index 0000000..3ca7168 --- /dev/null +++ b/frontend/src/styles/mixin.scss @@ -0,0 +1,28 @@ +@mixin clearfix { + &:after { + content: ''; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} diff --git a/frontend/src/styles/sidebar.scss b/frontend/src/styles/sidebar.scss new file mode 100644 index 0000000..d0c50ae --- /dev/null +++ b/frontend/src/styles/sidebar.scss @@ -0,0 +1,253 @@ +svg { + vertical-align: text-bottom !important; +} +#app { + .main-container { + // min-height: 100%; + transition: margin-left 0.28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebar-container { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + height: calc(100vh - 60px); + padding-top: 15px; + position: absolute; + top: 60px; + bottom: 0; + left: 0; + z-index: 98; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, + 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + + .svg-icon { + margin-right: 16px; + } + + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-sub-menu__title { + &:hover { + color: $menuHover !important; + } + } + + // .is-active > .el-sub-menu__title { + // color: $subMenuActiveText !important; + // } + + & .nest-menu .el-sub-menu > .el-sub-menu__title, + & .el-sub-menu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + color: $subMenuHover !important; + } + } + .el-menu-item.is-active { + border-right: 3px solid $subMenuHover; + background: #e8f3ff !important; + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + .svg-icon { + margin-right: 0px; + } + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .sub-el-icon { + margin-left: 19px; + } + } + } + + .el-sub-menu { + overflow: hidden; + + & > .el-sub-menu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + .sub-el-icon { + margin-left: 19px; + } + + .el-sub-menu__icon-arrow { + display: none; + } + } + + } + + .el-menu--collapse { + .el-sub-menu { + & > .el-sub-menu__title { + & > span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform 0.28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + & > .el-menu { + .svg-icon { + margin-right: 16px; + } + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + } + + .nest-menu .el-sub-menu > .el-sub-menu__title, + .el-menu-item { + &:hover { + background-color: #ffffff; + // you can use $subMenuHover + color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + > .el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} +body[layout="mix"] { + + .horizontal-header{ + .el-menu-item{ + height: 50px!important; + line-height: 50px!important; + } + + .el-sub-menu__title { + background-color: #001529!important; + height: 50px!important; + } + } + .horizontal-header-right>div { + color: #FFF; + } + .svg-icon{ + margin-right: 16px; + } + +} diff --git a/frontend/src/styles/tailwind.scss b/frontend/src/styles/tailwind.scss new file mode 100644 index 0000000..9bb37ee --- /dev/null +++ b/frontend/src/styles/tailwind.scss @@ -0,0 +1,4 @@ + +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/frontend/src/styles/variables.module.css b/frontend/src/styles/variables.module.css new file mode 100644 index 0000000..518a1f9 --- /dev/null +++ b/frontend/src/styles/variables.module.css @@ -0,0 +1,11 @@ +:export { + menuText: #409eff; + menuActiveText: #409eff; + subMenuActiveText: #409eff; + menuBg: #ffffff; + menuHover: #409eff; + subMenuBg: #ffffff; + subMenuHover: #409eff; + sideBarWidth: 210px; +} +/*# sourceMappingURL=variables.module.css.map */ \ No newline at end of file diff --git a/frontend/src/styles/variables.module.css.map b/frontend/src/styles/variables.module.css.map new file mode 100644 index 0000000..b95fb87 --- /dev/null +++ b/frontend/src/styles/variables.module.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "AAeA,AAAA,OAAO,CAAC;EACN,QAAQ,EAfC,OAAO;EAgBhB,cAAc,EAfC,OAAO;EAgBtB,iBAAiB,EAfC,OAAO;EAgBzB,MAAM,EAdC,OAAO;EAed,SAAS,EAdC,OAAO;EAejB,SAAS,EAbC,OAAO;EAcjB,YAAY,EAbC,OAAO;EAcpB,YAAY,EAZC,KAAK;CAanB", + "sources": [ + "variables.module.scss" + ], + "names": [], + "file": "variables.module.css" +} \ No newline at end of file diff --git a/frontend/src/styles/variables.module.scss b/frontend/src/styles/variables.module.scss new file mode 100644 index 0000000..0376220 --- /dev/null +++ b/frontend/src/styles/variables.module.scss @@ -0,0 +1,25 @@ +// sidebar +$menuText: #505050; +$menuActiveText: #409eff; +$subMenuActiveText: #409eff; + +$menuBg: #ffffff; +$menuHover: #409eff; + +$subMenuBg: #ffffff; +$subMenuHover: #409eff; + +$sideBarWidth: 210px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + subMenuActiveText: $subMenuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + subMenuBg: $subMenuBg; + subMenuHover: $subMenuHover; + sideBarWidth: $sideBarWidth; +} diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts new file mode 100644 index 0000000..c6adc52 --- /dev/null +++ b/frontend/src/utils/auth.ts @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie'; + +const TokenKey = 'token'; + +export function getToken() { + return Cookies.get(TokenKey); +} + +export function setToken(token: any) { + Cookies.set(TokenKey, token); +} + +export function removeToken() { + return Cookies.remove(TokenKey); +} diff --git a/frontend/src/utils/filter.ts b/frontend/src/utils/filter.ts new file mode 100644 index 0000000..38c28bc --- /dev/null +++ b/frontend/src/utils/filter.ts @@ -0,0 +1,80 @@ +/** + * Show plural label if time is plural number + * @param {number} time + * @param {string} label + * @return {string} + */ +function pluralize(time: number, label: string) { + if (time === 1) { + return time + label; + } + return time + label + 's'; +} + +/** + * @param {number} time + */ +export function timeAgo(time: number) { + const between = Date.now() / 1000 - Number(time); + if (between < 3600) { + return pluralize(~~(between / 60), ' minute'); + } else if (between < 86400) { + return pluralize(~~(between / 3600), ' hour'); + } else { + return pluralize(~~(between / 86400), ' day'); + } +} + +/** + * Number formatting + * like 10000 => 10k + * @param {number} num + * @param {number} digits + */ +export function numberFormatter(num: number, digits: number) { + const si = [ + { value: 1e18, symbol: 'E' }, + { value: 1e15, symbol: 'P' }, + { value: 1e12, symbol: 'T' }, + { value: 1e9, symbol: 'G' }, + { value: 1e6, symbol: 'M' }, + { value: 1e3, symbol: 'k' } + ]; + for (let i = 0; i < si.length; i++) { + if (num >= si[i].value) { + return ( + (num / si[i].value) + .toFixed(digits) + .replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol + ); + } + } + return num.toString(); +} + +/** + * 10000 => "10,000" + * @param {number} num + */ +export function toThousandFilter(num: number) { + return (+num || 0) + .toString() + .replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')); +} + +/** + * Upper case first char + * @param {String} string + */ +export function uppercaseFirst(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +/** + * 金额转换(分->元) + * 100 => 1 + * @param {number} num + */ +export function moneyFormatter(num: number) { + return '¥' + (isNaN(num) ? 0.0 : parseFloat((num / 100).toFixed(2))); +} diff --git a/frontend/src/utils/i18n.ts b/frontend/src/utils/i18n.ts new file mode 100644 index 0000000..b95552a --- /dev/null +++ b/frontend/src/utils/i18n.ts @@ -0,0 +1,12 @@ +// translate router.meta.title, be used in breadcrumb sidebar tagsview +import i18n from '@/lang/index'; + +export function generateTitle(title: any) { + // 判断是否存在国际化配置,如果没有原生返回 + const hasKey = i18n.global.te('route.' + title); + if (hasKey) { + const translatedTitle = i18n.global.t('route.' + title); + return translatedTitle; + } + return title; +} diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts new file mode 100644 index 0000000..cbf844d --- /dev/null +++ b/frontend/src/utils/index.ts @@ -0,0 +1,97 @@ +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele: HTMLElement, cls: string) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele: HTMLElement, cls: string) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls; +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele: HTMLElement, cls: string) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + ele.className = ele.className.replace(reg, ' '); + } +} + +export function mix(color1: string, color2: string, weight: number) { + weight = Math.max(Math.min(Number(weight), 1), 0); + const r1 = parseInt(color1.substring(1, 3), 16); + const g1 = parseInt(color1.substring(3, 5), 16); + const b1 = parseInt(color1.substring(5, 7), 16); + const r2 = parseInt(color2.substring(1, 3), 16); + const g2 = parseInt(color2.substring(3, 5), 16); + const b2 = parseInt(color2.substring(5, 7), 16); + const r = Math.round(r1 * (1 - weight) + r2 * weight); + const g = Math.round(g1 * (1 - weight) + g2 * weight); + const b = Math.round(b1 * (1 - weight) + b2 * weight); + const rStr = ('0' + (r || 0).toString(16)).slice(-2); + const gStr = ('0' + (g || 0).toString(16)).slice(-2); + const bStr = ('0' + (b || 0).toString(16)).slice(-2); + return '#' + rStr + gStr + bStr; +} + +export function parseTime(time :any, cFormat :any) { + if (arguments.length === 0) { + return null + } + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'undefined' || time === null || time === 'null') { + return '' + } else if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result :any, key :any) => { + let value : any = formatObj[key] + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} +export function downloadFile(obj :any, name :any, suffix :any) { + const url = window.URL.createObjectURL(new Blob([obj])) + const link = document.createElement('a') + link.style.display = 'none' + link.href = url + const fileName = parseTime(new Date(),'') + '-' + name + '.' + suffix + link.setAttribute('download', fileName) + document.body.appendChild(link) + link.click() + document.body.removeChild(link) +} \ No newline at end of file diff --git a/frontend/src/utils/localStorage.ts b/frontend/src/utils/localStorage.ts new file mode 100644 index 0000000..d5628e5 --- /dev/null +++ b/frontend/src/utils/localStorage.ts @@ -0,0 +1,53 @@ +/** + * window.localStorage 浏览器永久缓存 + */ +export const localStorage = { + // 设置永久缓存 + set(key: string, val: any) { + window.localStorage.setItem(key, JSON.stringify(val)); + }, + // 获取永久缓存 + get(key: string) { + const json: any = window.localStorage.getItem(key); + return JSON.parse(json); + }, + // 移除永久缓存 + remove(key: string) { + window.localStorage.removeItem(key); + }, + // 移除全部永久缓存 + clear() { + window.localStorage.clear(); + } +}; + +// 侧边栏状态(显示/隐藏) +const SidebarStatusKey = 'sidebarStatus'; +export function getSidebarStatus() { + return localStorage.get(SidebarStatusKey); +} + +export function setSidebarStatus(sidebarStatus: string) { + localStorage.set(SidebarStatusKey, sidebarStatus); +} +// 布局大小 +const SizeKey = 'size'; + +export function getSize() { + return localStorage.get(SizeKey); +} + +export function setSize(size: string) { + localStorage.set(SizeKey, size); +} + +// 语言 +const LanguageKey = 'language'; + +export function getLanguage() { + return localStorage.get(LanguageKey); +} + +export function setLanguage(language: string) { + localStorage.set(LanguageKey, language); +} diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts new file mode 100644 index 0000000..abe108e --- /dev/null +++ b/frontend/src/utils/request.ts @@ -0,0 +1,87 @@ +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { ElMessage, ElMessageBox } from 'element-plus'; +import { getToken } from '@/utils/auth'; +import { useUserStoreHook } from '@/store/modules/user'; + +// 创建 axios 实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API, + timeout: 50000, + headers: { 'Content-Type': 'application/json;charset=utf-8' } +}); + +// 请求拦截器 +service.interceptors.request.use( + (config: AxiosRequestConfig) => { + if (!config.headers) { + throw new Error( + `Expected 'config' and 'config.headers' not to be undefined` + ); + } + const user = useUserStoreHook(); + if (user.Token) { + config.headers.token = getToken(); + } + return config; + }, + (error: any) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +service.interceptors.response.use( + (response: AxiosResponse) => { + const { status, msg } = response; + if (status === 200) { + if (response.data.code == 401) { + ElMessage({ + message: '用户名或密码错误,请重试!', + type: 'error' + }); + return; + }else if(response.data.code == 1){ + ElMessage({ + message: response.data.msg, + type: 'error' + }); + return; + } + return response.data; + } else { + // 响应数据为二进制流处理(Excel导出) + if (response.data instanceof ArrayBuffer) { + return response; + } + ElMessage({ + message: msg || '系统出错', + type: 'error' + }); + return Promise.reject(new Error(msg || 'Error')); + } + }, + (error: any) => { + if (error.response.data) { + const { status, msg } = error.response.data; + // token 过期,重新登录 + if (status === '403') { + ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', { + confirmButtonText: 'OK', + type: 'warning' + }).then(() => { + localStorage.clear(); + window.location.href = '/'; + }); + } else { + ElMessage({ + message: msg || '当前页面已失效', + type: 'error' + }); + } + } + return Promise.reject(error.message); + } +); + +// 导出 axios 实例 +export default service; diff --git a/frontend/src/utils/resize.ts b/frontend/src/utils/resize.ts new file mode 100644 index 0000000..343bb0f --- /dev/null +++ b/frontend/src/utils/resize.ts @@ -0,0 +1,73 @@ +import { ref } from 'vue'; +export default function () { + const chart = ref(); + const sidebarElm = ref(); + + const chartResizeHandler = () => { + if (chart.value) { + chart.value.resize(); + } + }; + + const sidebarResizeHandler = (e: TransitionEvent) => { + if (e.propertyName === 'width') { + chartResizeHandler(); + } + }; + + const initResizeEvent = () => { + window.addEventListener('resize', chartResizeHandler, {passive:true}); + }; + + const destroyResizeEvent = () => { + window.removeEventListener('resize', chartResizeHandler); + }; + + const initSidebarResizeEvent = () => { + sidebarElm.value = document.getElementsByClassName('sidebar-container')[0]; + if (sidebarElm.value) { + sidebarElm.value.addEventListener( + 'transitionend', + sidebarResizeHandler as EventListener, + {passive:true} + ); + } + }; + + const destroySidebarResizeEvent = () => { + if (sidebarElm.value) { + sidebarElm.value.removeEventListener( + 'transitionend', + sidebarResizeHandler as EventListener + ); + } + }; + + const mounted = () => { + initResizeEvent(); + initSidebarResizeEvent(); + }; + + const beforeDestroy = () => { + destroyResizeEvent(); + destroySidebarResizeEvent(); + }; + + const activated = () => { + initResizeEvent(); + initSidebarResizeEvent(); + }; + + const deactivated = () => { + destroyResizeEvent(); + destroySidebarResizeEvent(); + }; + + return { + chart, + mounted, + beforeDestroy, + activated, + deactivated + }; +} diff --git a/frontend/src/utils/rsaEncrypt.ts b/frontend/src/utils/rsaEncrypt.ts new file mode 100644 index 0000000..7bbc829 --- /dev/null +++ b/frontend/src/utils/rsaEncrypt.ts @@ -0,0 +1,29 @@ +import JSEncrypt from 'jsencrypt' +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD\n' + + '2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ==' + +const privateKey = 'MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8\n' + + 'mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9p\n' + + 'B6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue\n' + + '/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZ\n' + + 'UBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6\n' + + 'vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha\n' + + '4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3\n' + + 'tTbklZkD2A==' + +// 加密 +export function encrypt(txt:string) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对需要加密的数据进行加密 +} + +// 解密 +export function decrypt(txt:string) { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) + return encryptor.decrypt(txt) +} + diff --git a/frontend/src/utils/scroll-to.ts b/frontend/src/utils/scroll-to.ts new file mode 100644 index 0000000..591e3ec --- /dev/null +++ b/frontend/src/utils/scroll-to.ts @@ -0,0 +1,69 @@ +const easeInOutQuad = (t: number, b: number, c: number, d: number) => { + t /= d / 2; + if (t < 1) { + return (c / 2) * t * t + b; + } + t--; + return (-c / 2) * (t * (t - 2) - 1) + b; +}; + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +const requestAnimFrame = (function () { + return ( + window.requestAnimationFrame || + (window as any).webkitRequestAnimationFrame || + (window as any).mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + } + ); +})(); + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +const move = (amount: number) => { + document.documentElement.scrollTop = amount; + (document.body.parentNode as HTMLElement).scrollTop = amount; + document.body.scrollTop = amount; +}; + +const position = () => { + return ( + document.documentElement.scrollTop || + (document.body.parentNode as HTMLElement).scrollTop || + document.body.scrollTop + ); +}; + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export const scrollTo = (to: number, duration: number, callback?: any) => { + const start = position(); + const change = to - start; + const increment = 20; + let currentTime = 0; + duration = typeof duration === 'undefined' ? 500 : duration; + const animateScroll = function () { + // increment the time + currentTime += increment; + // find the value with the quadratic in-out easing function + const val = easeInOutQuad(currentTime, start, change, duration); + // move the document.body + move(val); + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll); + } else { + if (callback && typeof callback === 'function') { + // the animation is done so lets callback + callback(); + } + } + }; + animateScroll(); +}; diff --git a/frontend/src/utils/sessionStorage.ts b/frontend/src/utils/sessionStorage.ts new file mode 100644 index 0000000..fae9a21 --- /dev/null +++ b/frontend/src/utils/sessionStorage.ts @@ -0,0 +1,22 @@ +/** + * window.sessionStorage 浏览器临时缓存 + */ +export const sessionStorage = { + // 设置临时缓存 + set(key: string, val: any) { + window.sessionStorage.setItem(key, JSON.stringify(val)); + }, + // 获取临时缓存 + get(key: string) { + const json: any = window.sessionStorage.getItem(key); + return JSON.parse(json); + }, + // 移除临时缓存 + remove(key: string) { + window.sessionStorage.removeItem(key); + }, + // 移除全部临时缓存 + clear() { + window.sessionStorage.clear(); + } +}; diff --git a/frontend/src/utils/validate.ts b/frontend/src/utils/validate.ts new file mode 100644 index 0000000..bc8ccee --- /dev/null +++ b/frontend/src/utils/validate.ts @@ -0,0 +1,12 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path: string) { + const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); + return isExternal; +} diff --git a/frontend/src/views/TaskSetting/index.vue b/frontend/src/views/TaskSetting/index.vue new file mode 100644 index 0000000..e295a48 --- /dev/null +++ b/frontend/src/views/TaskSetting/index.vue @@ -0,0 +1,1288 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/views/component/editor.vue b/frontend/src/views/component/editor.vue new file mode 100644 index 0000000..b0f9a87 --- /dev/null +++ b/frontend/src/views/component/editor.vue @@ -0,0 +1,22 @@ + + diff --git a/frontend/src/views/component/uploader.vue b/frontend/src/views/component/uploader.vue new file mode 100644 index 0000000..8a4ab8b --- /dev/null +++ b/frontend/src/views/component/uploader.vue @@ -0,0 +1,33 @@ + + diff --git a/frontend/src/views/dashboard/components/Chart/BarChart.vue b/frontend/src/views/dashboard/components/Chart/BarChart.vue new file mode 100644 index 0000000..05be0c4 --- /dev/null +++ b/frontend/src/views/dashboard/components/Chart/BarChart.vue @@ -0,0 +1,174 @@ + + + + diff --git a/frontend/src/views/dashboard/components/Chart/FunnelChart.vue b/frontend/src/views/dashboard/components/Chart/FunnelChart.vue new file mode 100644 index 0000000..44206d2 --- /dev/null +++ b/frontend/src/views/dashboard/components/Chart/FunnelChart.vue @@ -0,0 +1,131 @@ + + + + + + diff --git a/frontend/src/views/dashboard/components/Chart/PieChart.vue b/frontend/src/views/dashboard/components/Chart/PieChart.vue new file mode 100644 index 0000000..2ad159f --- /dev/null +++ b/frontend/src/views/dashboard/components/Chart/PieChart.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/frontend/src/views/dashboard/components/Chart/RadarChart.vue b/frontend/src/views/dashboard/components/Chart/RadarChart.vue new file mode 100644 index 0000000..998d62c --- /dev/null +++ b/frontend/src/views/dashboard/components/Chart/RadarChart.vue @@ -0,0 +1,132 @@ + + + + + + diff --git a/frontend/src/views/dashboard/components/Project/index.vue b/frontend/src/views/dashboard/components/Project/index.vue new file mode 100644 index 0000000..63e471f --- /dev/null +++ b/frontend/src/views/dashboard/components/Project/index.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/frontend/src/views/dashboard/components/Team/index.vue b/frontend/src/views/dashboard/components/Team/index.vue new file mode 100644 index 0000000..8b0cf8d --- /dev/null +++ b/frontend/src/views/dashboard/components/Team/index.vue @@ -0,0 +1,240 @@ + + + + + + diff --git a/frontend/src/views/dashboard/index.vue b/frontend/src/views/dashboard/index.vue new file mode 100644 index 0000000..d0f23a2 --- /dev/null +++ b/frontend/src/views/dashboard/index.vue @@ -0,0 +1,19 @@ + + + + + + + diff --git a/frontend/src/views/error-page/401.vue b/frontend/src/views/error-page/401.vue new file mode 100644 index 0000000..59b6f1a --- /dev/null +++ b/frontend/src/views/error-page/401.vue @@ -0,0 +1,114 @@ + + + + + + + + diff --git a/frontend/src/views/error-page/404.vue b/frontend/src/views/error-page/404.vue new file mode 100644 index 0000000..8559404 --- /dev/null +++ b/frontend/src/views/error-page/404.vue @@ -0,0 +1,280 @@ + + + + + + + + diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue new file mode 100644 index 0000000..e49fb8b --- /dev/null +++ b/frontend/src/views/login/index.vue @@ -0,0 +1,513 @@ + + + + + + + diff --git a/frontend/src/views/nested/level1/index.vue b/frontend/src/views/nested/level1/index.vue new file mode 100644 index 0000000..7daf19c --- /dev/null +++ b/frontend/src/views/nested/level1/index.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/views/nested/level1/level2/index.vue b/frontend/src/views/nested/level1/level2/index.vue new file mode 100644 index 0000000..abcc3a7 --- /dev/null +++ b/frontend/src/views/nested/level1/level2/index.vue @@ -0,0 +1,7 @@ + diff --git a/frontend/src/views/nested/level1/level2/level3/index1.vue b/frontend/src/views/nested/level1/level2/level3/index1.vue new file mode 100644 index 0000000..888f58e --- /dev/null +++ b/frontend/src/views/nested/level1/level2/level3/index1.vue @@ -0,0 +1,5 @@ + diff --git a/frontend/src/views/nested/level1/level2/level3/index2.vue b/frontend/src/views/nested/level1/level2/level3/index2.vue new file mode 100644 index 0000000..a99c98e --- /dev/null +++ b/frontend/src/views/nested/level1/level2/level3/index2.vue @@ -0,0 +1,5 @@ + diff --git a/frontend/src/views/redirect/index.vue b/frontend/src/views/redirect/index.vue new file mode 100644 index 0000000..47cad96 --- /dev/null +++ b/frontend/src/views/redirect/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/frontend/src/views/system/dept/index.vue b/frontend/src/views/system/dept/index.vue new file mode 100644 index 0000000..a10061b --- /dev/null +++ b/frontend/src/views/system/dept/index.vue @@ -0,0 +1,638 @@ + + + + + + + diff --git a/frontend/src/views/system/dict/index.vue b/frontend/src/views/system/dict/index.vue new file mode 100644 index 0000000..bf14f6a --- /dev/null +++ b/frontend/src/views/system/dict/index.vue @@ -0,0 +1,708 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/system/menu/index.vue b/frontend/src/views/system/menu/index.vue new file mode 100644 index 0000000..f9877ee --- /dev/null +++ b/frontend/src/views/system/menu/index.vue @@ -0,0 +1,832 @@ + + + \ No newline at end of file diff --git a/frontend/src/views/system/record/index.vue b/frontend/src/views/system/record/index.vue new file mode 100644 index 0000000..eb25244 --- /dev/null +++ b/frontend/src/views/system/record/index.vue @@ -0,0 +1,207 @@ + + + + + + + diff --git a/frontend/src/views/system/role/index.vue b/frontend/src/views/system/role/index.vue new file mode 100644 index 0000000..2d458c7 --- /dev/null +++ b/frontend/src/views/system/role/index.vue @@ -0,0 +1,510 @@ + + + + + + + diff --git a/frontend/src/views/system/user/index.vue b/frontend/src/views/system/user/index.vue new file mode 100644 index 0000000..979b4bc --- /dev/null +++ b/frontend/src/views/system/user/index.vue @@ -0,0 +1,559 @@ + + + + + + + + + diff --git a/frontend/src/views/system/user/personalCenter.vue b/frontend/src/views/system/user/personalCenter.vue new file mode 100644 index 0000000..1184351 --- /dev/null +++ b/frontend/src/views/system/user/personalCenter.vue @@ -0,0 +1,289 @@ + + + + + + + diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..5b86b53 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,15 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], + darkMode: 'class', + theme: { + + backgroundColor: theme => ({ + ...theme('colors'), + "sidebar-logo":'#2b2f3a' + }) + }, + plugins: [], + + +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..4f8945a --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "esnext", + "useDefineForClassFields": true, + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "baseUrl": ".", + "allowJs": true, + "paths": { + "@/*": ["src/*"] + }, + "skipLibCheck": true /* Skip type checking all .d.ts files. */, + "allowSyntheticDefaultImports": true /* 允许默认导入 */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + "types": ["element-plus/global"], + "typeRoots": [ + "./node_modules/@types/", + "./types" + ] /* 指定多个文件夹,这些文件夹的作用类似于 './node_modules/@types'. */ + }, + "include": ["src/**/*.ts", "src/**/*.vue", "types/**/*.d.ts"], + "exclude": ["node_modules", "dist", "**/*.js"] +} diff --git a/frontend/types/components.d.ts b/frontend/types/components.d.ts new file mode 100644 index 0000000..94e8b82 --- /dev/null +++ b/frontend/types/components.d.ts @@ -0,0 +1,9 @@ +// 全局组件类型声明 +import Pagination from '@/components/Pagination/index.vue'; + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + Pagination: typeof Pagination; + } +} +export {}; diff --git a/frontend/types/env.d.ts b/frontend/types/env.d.ts new file mode 100644 index 0000000..bcddf3e --- /dev/null +++ b/frontend/types/env.d.ts @@ -0,0 +1,19 @@ +/// + +declare module '*.vue' { + import { DefineComponent } from 'vue'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any>; + export default component; +} + +// 环境变量 TypeScript的智能提示 +interface ImportMetaEnv { + VITE_APP_TITLE: string; + VITE_APP_PORT: string; + VITE_APP_BASE_API: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/frontend/types/global.d.ts b/frontend/types/global.d.ts new file mode 100644 index 0000000..63d85cc --- /dev/null +++ b/frontend/types/global.d.ts @@ -0,0 +1,23 @@ +declare global { + interface PageQuery { + pageNum: number; + pageSize: number; + } + + interface PageResult { + list: T; + total: number; + } + type DialogType = { + title?: string; + visible: boolean; + }; + + type OptionType = { + value: string; + label: string; + checked?: boolean; + children?: OptionType[]; + }; +} +export {}; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..7b198e2 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,44 @@ +import { UserConfig, ConfigEnv, loadEnv } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; +import path from 'path'; + +export default ({ mode }: ConfigEnv): UserConfig => { + // 获取 .env 环境配置文件 + const env = loadEnv(mode, process.cwd()); + + return { + plugins: [ + vue(), + createSvgIconsPlugin({ + // 指定需要缓存的图标文件夹 + iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], + // 指定symbolId格式 + symbolId: 'icon-[dir]-[name]' + }) + ], + // 本地反向代理解决浏览器跨域限制 + server: { + host: '0.0.0.0', + port: Number(env.VITE_APP_PORT), + open: true, // 运行自动打开浏览器 + proxy: { + [env.VITE_APP_BASE_API]: { + // 线上API地址 + //target: 'http://192.168.1.20:8090/', + // 本地API地址 + target: 'http://localhost:8093', + changeOrigin: true, + rewrite: path => + path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') + } + } + }, + resolve: { + // Vite路径别名配置 + alias: { + '@': path.resolve('./src') + } + } + }; +}; diff --git a/pom.xml b/pom.xml index 64b61cd..73c9d02 100644 --- a/pom.xml +++ b/pom.xml @@ -19,4 +19,4 @@ 17 - \ No newline at end of file + diff --git a/src/main/java/com/yfd/platform/PlatformApplication.java b/src/main/java/com/yfd/platform/PlatformApplication.java new file mode 100644 index 0000000..a98fea6 --- /dev/null +++ b/src/main/java/com/yfd/platform/PlatformApplication.java @@ -0,0 +1,45 @@ +package com.yfd.platform; + +import com.yfd.platform.annotation.rest.AnonymousGetMapping; +import com.yfd.platform.datasource.DynamicDataSourceConfig; +import com.yfd.platform.utils.SpringContextHolder; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.web.bind.annotation.RestController; + +//@SpringBootApplication +@RestController +@EnableTransactionManagement +@ServletComponentScan("com.yfd.platform.config") +@MapperScan(basePackages = "com.yfd.platform.modules.*.mapper,com.yfd.platform.*.mapper") +@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) +@Import({DynamicDataSourceConfig.class}) +@EnableCaching +public class PlatformApplication { + + public static void main(String[] args) { + SpringApplication.run(PlatformApplication.class, args); + } + + @Bean + public SpringContextHolder springContextHolder() { + return new SpringContextHolder(); + } + + /** + * 访问首页提示 + * + * @return / + */ + @AnonymousGetMapping("/") + public String index() { + return "Backend service started successfully"; + } +} diff --git a/src/main/java/com/yfd/platform/ServletInitializer.java b/src/main/java/com/yfd/platform/ServletInitializer.java new file mode 100644 index 0000000..0234b48 --- /dev/null +++ b/src/main/java/com/yfd/platform/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.yfd.platform; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(PlatformApplication.class); + } + +} diff --git a/src/main/java/com/yfd/platform/annotation/AnonymousAccess.java b/src/main/java/com/yfd/platform/annotation/AnonymousAccess.java new file mode 100644 index 0000000..2fbd4c0 --- /dev/null +++ b/src/main/java/com/yfd/platform/annotation/AnonymousAccess.java @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.annotation; + +import java.lang.annotation.*; + +/** + * @author jacky + * 用于标记匿名访问方法 + */ +@Inherited +@Documented +@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AnonymousAccess { + +} diff --git a/src/main/java/com/yfd/platform/annotation/Log.java b/src/main/java/com/yfd/platform/annotation/Log.java new file mode 100644 index 0000000..6739494 --- /dev/null +++ b/src/main/java/com/yfd/platform/annotation/Log.java @@ -0,0 +1,20 @@ +package com.yfd.platform.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author TangWei + * @date 2018-11-24 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Log { + + String value() default ""; + + String module() default ""; +} + diff --git a/src/main/java/com/yfd/platform/annotation/rest/AnonymousGetMapping.java b/src/main/java/com/yfd/platform/annotation/rest/AnonymousGetMapping.java new file mode 100644 index 0000000..01fcc32 --- /dev/null +++ b/src/main/java/com/yfd/platform/annotation/rest/AnonymousGetMapping.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yfd.platform.annotation.rest; + +import com.yfd.platform.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.lang.annotation.*; + +/** + * Annotation for mapping HTTP {@code GET} requests onto specific handler + * methods. + *

+ * 支持匿名访问 GetMapping + * + * @author liaojinlong + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.GET) +public @interface AnonymousGetMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + * + * @since 4.3.5 + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/src/main/java/com/yfd/platform/aspect/LogAspect.java b/src/main/java/com/yfd/platform/aspect/LogAspect.java new file mode 100644 index 0000000..0c52a21 --- /dev/null +++ b/src/main/java/com/yfd/platform/aspect/LogAspect.java @@ -0,0 +1,93 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.aspect; + +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.RequestHolder; +import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * @author + * @date 2018-11-24 + */ +@Component +@Aspect +@Slf4j +public class LogAspect { + + @Resource + private final ISysLogService sysLogService; + + @Resource + private IUserService userService; + + ThreadLocal currentTime = new ThreadLocal<>(); + + public LogAspect(ISysLogService sysLogService) { + this.sysLogService = sysLogService; + } + + /** + * 配置切入点 + */ + @Pointcut("@annotation(com.yfd.platform.annotation.Log)") + public void logPointcut() { + // 该方法无方法体,主要为了让同类中其他方法使用此切入点 + } + + /** + * 配置环绕通知,使用在方法logPointcut()上注册的切入点 + * + * @param joinPoint join point for advice + */ + @Around("logPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + Object result; + currentTime.set(System.currentTimeMillis()); + result = joinPoint.proceed(); + SysLog log = new SysLog("INFO"); + currentTime.remove(); + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + Map nameInfo = userService.getNameInfo(); + String nickname = nameInfo.get("nickname"); + String username = nameInfo.get("username"); + sysLogService.save(nickname, username, StringUtils.getBrowser(request), + StringUtils.getIp(request), joinPoint, log); + return result; + } + + public String getUsername() { + try { + return SecurityUtils.getCurrentUsername(); + } catch (Exception e) { + return ""; + } + } +} diff --git a/src/main/java/com/yfd/platform/component/ServerSendEventServer.java b/src/main/java/com/yfd/platform/component/ServerSendEventServer.java new file mode 100644 index 0000000..ca1b1fe --- /dev/null +++ b/src/main/java/com/yfd/platform/component/ServerSendEventServer.java @@ -0,0 +1,147 @@ +package com.yfd.platform.component; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +/** + * @author Huhailong + * SSE Server send Event 服务器推送服务 + */ +@Slf4j +public class ServerSendEventServer { + + /** + * 当前连接数 + */ + private static AtomicInteger count = new AtomicInteger(0); + + private static Map sseEmitterMap = + new ConcurrentHashMap<>(); + + public static SseEmitter connect(String userId) { + //设置超时时间,0表示不过期,默认是30秒,超过时间未完成会抛出异常 + SseEmitter sseEmitter = new SseEmitter(0L); + //SseEmitter sseEmitter = new SseEmitter(); + //注册回调 + sseEmitter.onCompletion(completionCallBack(userId)); + sseEmitter.onError(errorCallBack(userId)); + sseEmitter.onTimeout(timeOutCallBack(userId)); + sseEmitterMap.put(userId, sseEmitter); + //数量+1 + count.getAndIncrement(); + log.info("create new sse connect ,current user:{}", userId); + return sseEmitter; + } + + /** + * 给指定用户发消息 + */ + public static void sendMessage(String userId, String message) { + if (sseEmitterMap.containsKey(userId)) { + try { + sseEmitterMap.get(userId).send(message); + } catch (IOException e) { + log.error("user id:{}, send message error:{}", userId, + e.getMessage()); + e.printStackTrace(); + } + } + } + + /** + * 给所有用户发消息 + */ + public static void sendMessage(String message) { + if (sseEmitterMap != null && !sseEmitterMap.isEmpty()) { + sseEmitterMap.forEach((k, v) -> { + // 发送消息 + sendMessage(k, message); + + }); + } + } + + /** + * 想多人发送消息,组播 + */ + public static void groupSendMessage(String groupId, String message) { + if (sseEmitterMap != null && !sseEmitterMap.isEmpty()) { + sseEmitterMap.forEach((k, v) -> { + try { + if (k.startsWith(groupId)) { + v.send(message, MediaType.APPLICATION_JSON); + } + } catch (IOException e) { + log.error("user id:{}, send message error:{}", groupId, + message); + removeUser(k); + } + }); + } + } + + public static void batchSendMessage(String message) { + sseEmitterMap.forEach((k, v) -> { + try { + v.send(message, MediaType.APPLICATION_JSON); + } catch (IOException e) { + log.error("user id:{}, send message error:{}", k, + e.getMessage()); + removeUser(k); + } + }); + } + + /** + * 群发消息 + */ + public static void batchSendMessage(String message, Set userIds) { + userIds.forEach(userId -> sendMessage(userId, message)); + } + + public static void removeUser(String userId) { + sseEmitterMap.remove(userId); + //数量-1 + count.getAndDecrement(); + log.info("remove user id:{}", userId); + } + + public static List getIds() { + return new ArrayList<>(sseEmitterMap.keySet()); + } + + public static int getUserCount() { + return count.intValue(); + } + + private static Runnable completionCallBack(String userId) { + return () -> { + log.info("结束连接,{}", userId); + removeUser(userId); + }; + } + + private static Runnable timeOutCallBack(String userId) { + return () -> { + log.info("连接超时,{}", userId); + removeUser(userId); + }; + } + + private static Consumer errorCallBack(String userId) { + return throwable -> { + log.error("连接异常,{}", userId); + removeUser(userId); + }; + } +} diff --git a/src/main/java/com/yfd/platform/component/WebSocketServer.java b/src/main/java/com/yfd/platform/component/WebSocketServer.java new file mode 100644 index 0000000..1d0b913 --- /dev/null +++ b/src/main/java/com/yfd/platform/component/WebSocketServer.java @@ -0,0 +1,106 @@ +package com.yfd.platform.component; + +import org.springframework.stereotype.Component; + +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.CopyOnWriteArrayList; + +@ServerEndpoint("/websocket/{token}") +@Component +public class WebSocketServer { + private static int onlineCount=0;//在线人数 + private static CopyOnWriteArrayList webSocketSet=new CopyOnWriteArrayList();//在线用户集合 + private Session session;//与某个客户端的连接会话 + private String currentUser; + + @OnOpen + public void onOpen(@PathParam("token") String token, Session session){ + this.currentUser = token; + this.session=session; + webSocketSet.add(this);//加入set中 + addOnlineCount(); + System.out.println("有新连接加入!当前在线人数为"+getOnlineCount()); + allCurrentOnline(); + } + + @OnClose + public void onClose(){ + webSocketSet.remove(this); + subOnlineCount(); + System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount()); + allCurrentOnline(); + } + + @OnMessage + public void onMessage(String message, Session session){ + System.out.println("来自客户端的消息:"+message); + for (WebSocketServer item:webSocketSet){ + try { + item.sendMessage(message); + } catch (IOException e) { + e.printStackTrace(); + continue; + } + } + } + + @OnError + public void onError(Session session, Throwable throwable){ + System.out.println("发生错误!"); + throwable.printStackTrace(); + } + + public void sendMessage(String message) throws IOException { + this.session.getBasicRemote().sendText(message); + } + + /** + * 获取当前所有在线用户名 + */ + public static void allCurrentOnline(){ + for (WebSocketServer item : webSocketSet) { + System.out.println(item.currentUser); + } + } + + /** + * 发送给指定用户 + */ + public static void sendMessageTo(String message,String token) throws IOException { + for (WebSocketServer item : webSocketSet) { + if(item.currentUser.equals(token)){ + item.session.getBasicRemote().sendText(message); + } + } + } + + /** + * 群发自定义消息 + */ + public static void sendInfo(String message) throws IOException { + System.out.println(message); + for (WebSocketServer item : webSocketSet) { + try { + item.sendMessage(message); + } catch (IOException e) { + continue; + } + } + } + + public static synchronized int getOnlineCount(){ + return onlineCount; + } + public static synchronized void addOnlineCount(){ + WebSocketServer.onlineCount++; + } + public static synchronized void subOnlineCount(){ + WebSocketServer.onlineCount--; + } + +} + + diff --git a/src/main/java/com/yfd/platform/config/FileProperties.java b/src/main/java/com/yfd/platform/config/FileProperties.java new file mode 100644 index 0000000..c179499 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/FileProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "file") +public class FileProperties { + + /** 文件大小限制 */ + private Long maxSize; + + /** 头像大小限制 */ + private Long avatarMaxSize; + + private ElPath mac; + + private ElPath linux; + + private ElPath windows; + + public ElPath getPath(){ + String os = System.getProperty("os.name"); + if(os.toLowerCase().startsWith("win")) { + return windows; + } else if(os.toLowerCase().startsWith("mac")){ + return mac; + } + return linux; + } + + @Data + public static class ElPath{ + + private String path; + + private String avatar; + } +} diff --git a/src/main/java/com/yfd/platform/config/FileSpaceProperties.java b/src/main/java/com/yfd/platform/config/FileSpaceProperties.java new file mode 100644 index 0000000..74aab7d --- /dev/null +++ b/src/main/java/com/yfd/platform/config/FileSpaceProperties.java @@ -0,0 +1,17 @@ +package com.yfd.platform.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 文件空间相关配置,替换 @Value("${file-space.system}") 用法 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "file-space") +public class FileSpaceProperties { + + /** 基础目录,例如 D:/data/platform/ */ + private String system; +} \ No newline at end of file diff --git a/src/main/java/com/yfd/platform/config/GlobalExceptionHandler.java b/src/main/java/com/yfd/platform/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..d9c7d7a --- /dev/null +++ b/src/main/java/com/yfd/platform/config/GlobalExceptionHandler.java @@ -0,0 +1,24 @@ +package com.yfd.platform.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author TangWei + * @Date: 2023/3/27 18:07 + * @Description: + */ +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler { + + @ResponseBody + @ExceptionHandler(value = Throwable.class) + public ResponseResult handleException(Throwable e) { + log.error("message:{}", e.getMessage()); + return ResponseResult.error(e.getMessage()); + } + +} diff --git a/src/main/java/com/yfd/platform/config/JobRunner.java b/src/main/java/com/yfd/platform/config/JobRunner.java new file mode 100644 index 0000000..c0d451e --- /dev/null +++ b/src/main/java/com/yfd/platform/config/JobRunner.java @@ -0,0 +1,56 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.mapper.QuartzJobMapper; +import com.yfd.platform.utils.QuartzManage; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author + * @date 2019-01-07 + */ +@Component +@RequiredArgsConstructor +public class JobRunner implements ApplicationRunner { + + private static final Logger log = LoggerFactory.getLogger(JobRunner.class); + private final QuartzJobMapper quartzJobMapper; + private final QuartzManage quartzManage; + + /** + * 项目启动时重新激活启用的定时任务 + * + * @param applicationArguments / + */ + @Override + public void run(ApplicationArguments applicationArguments) { + log.info("--------------------注入定时任务---------------------"); + List quartzJobs = + quartzJobMapper.selectList(new LambdaQueryWrapper().eq(QuartzJob::getStatus, "1")); + quartzJobs.forEach(quartzManage::addJob); + log.info("--------------------定时任务注入完成---------------------"); + } +} diff --git a/src/main/java/com/yfd/platform/config/JwtAuthenticationTokenFilter.java b/src/main/java/com/yfd/platform/config/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..bcb79c0 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/JwtAuthenticationTokenFilter.java @@ -0,0 +1,83 @@ +package com.yfd.platform.config; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.jwt.JWT; +import cn.hutool.jwt.JWTUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.annotation.Resource; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + + @Autowired + private WebConfig webConfig; + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + //获取token + String uri = httpServletRequest.getRequestURI(); + String token = httpServletRequest.getHeader("token"); + if (StrUtil.isEmpty(token) || "/user/login".equals(uri)) { + filterChain.doFilter(httpServletRequest, httpServletResponse); + return; + } + //解析token + boolean isok = JWTUtil.verify(token, "12345678".getBytes()); + String userid = ""; + if (isok) { + final JWT jwt = JWTUtil.parseToken(token); + userid = jwt.getPayload("userid").toString(); + //从cachekey中获取用户信息失效时间 + String cachekey = "expire_time:" + userid; + if(StrUtil.isNotEmpty(webConfig.loginuserCache().get(cachekey))){ + long expire_time =Long.parseLong(webConfig.loginuserCache().get(cachekey)); + if (System.currentTimeMillis() > expire_time) { + httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Token超过期限!"); + return; + } + } + } + + //从cachekey中获取用户信息 + String cachekey = "login:" + userid; + String jsonstr = webConfig.loginuserCache().get(cachekey); + LoginUser loginUser = JSON.parseObject(jsonstr, LoginUser.class); + if (ObjectUtil.isEmpty(loginUser)) { + httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, + "登录用户已失效!"); + return; + } + //存入SecurityContextHolder + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(loginUser, null, + loginUser.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + webConfig.loginuserCache().put(Constant.TOKEN + userid, token); + //更新了超期时间 + long expireTime =System.currentTimeMillis() + ( 30L * 60L * 1000L); + webConfig.loginuserCache().put("expire_time:" + userid, String.valueOf(expireTime)); + //放行过滤器 + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + +} diff --git a/src/main/java/com/yfd/platform/config/MessageConfig.java b/src/main/java/com/yfd/platform/config/MessageConfig.java new file mode 100644 index 0000000..e079d02 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/MessageConfig.java @@ -0,0 +1,50 @@ +package com.yfd.platform.config; + +import cn.hutool.cache.Cache; +import cn.hutool.cache.impl.CacheObj; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.IUserService; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.Iterator; + +/** + * @author TangWei + * @Date: 2023/3/24 15:56 + * @Description: + */ +@Component +public class MessageConfig { + + @Resource + private IMessageService messageService; + + @Resource + private IUserService userService; + + @Resource + private WebConfig webConfig; + + public void sendMessage() { + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + String userId = userService.getUserInfo().getId(); + String token = webConfig.loginuserCache().get(Constant.TOKEN + userId); + ServerSendEventServer.sendMessage(token, count + ""); + } + + public void addMessage(Message message) { + messageService.save(message); + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + ServerSendEventServer.sendMessage(count + ""); + } +} diff --git a/src/main/java/com/yfd/platform/config/MybitsPlusConfig.java b/src/main/java/com/yfd/platform/config/MybitsPlusConfig.java new file mode 100644 index 0000000..de009d7 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/MybitsPlusConfig.java @@ -0,0 +1,24 @@ +package com.yfd.platform.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/****************************** + * 用途说明: + * 作者姓名: pcj + * 创建时间: 2022/10/24 10:50 + ******************************/ +@Configuration +public class MybitsPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); + mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return mybatisPlusInterceptor; + } + +} diff --git a/src/main/java/com/yfd/platform/config/ProdApiPrefixFilter.java b/src/main/java/com/yfd/platform/config/ProdApiPrefixFilter.java new file mode 100644 index 0000000..4c1680a --- /dev/null +++ b/src/main/java/com/yfd/platform/config/ProdApiPrefixFilter.java @@ -0,0 +1,44 @@ +package com.yfd.platform.config; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 将以 /prod-api/ 开头的请求转发到去掉前缀的真实后端接口路径。 + * 例如:/prod-api/user/code -> /user/code + * 这样可以兼容前端生产环境仍使用 /prod-api 作为网关前缀的情况。 + */ +@WebFilter(urlPatterns = "/prod-api/*", filterName = "prodApiPrefixFilter") +public class ProdApiPrefixFilter implements Filter { + + private static final String PREFIX = "/prod-api"; + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + if (!(req instanceof HttpServletRequest) || !(res instanceof HttpServletResponse)) { + chain.doFilter(req, res); + return; + } + + HttpServletRequest request = (HttpServletRequest) req; + String uri = request.getRequestURI(); + + // 仅拦截 /prod-api/* 的接口请求并进行内部 forward + if (uri.startsWith(PREFIX + "/")) { + String forwardUri = uri.substring(PREFIX.length()); + RequestDispatcher dispatcher = request.getRequestDispatcher(forwardUri); + dispatcher.forward(req, res); + return; + } + + chain.doFilter(req, res); + } +} \ No newline at end of file diff --git a/src/main/java/com/yfd/platform/config/QuartzConfig.java b/src/main/java/com/yfd/platform/config/QuartzConfig.java new file mode 100644 index 0000000..41a36db --- /dev/null +++ b/src/main/java/com/yfd/platform/config/QuartzConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config; + +import org.quartz.Scheduler; +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.AdaptableJobFactory; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 定时任务配置 + * + * @author / + * @date 2019-01-07 + */ +@Configuration +public class QuartzConfig { + + /** + * 解决Job中注入Spring Bean为null的问题 + */ + @Component("quartzJobFactory") + public static class QuartzJobFactory extends AdaptableJobFactory { + + private final AutowireCapableBeanFactory capableBeanFactory; + + public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) { + this.capableBeanFactory = capableBeanFactory; + } + + @Override + protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { + + //调用父类的方法 + Object jobInstance = super.createJobInstance(bundle); + capableBeanFactory.autowireBean(jobInstance); + return jobInstance; + } + } + + /** + * 注入scheduler到spring + * + * @param quartzJobFactory / + * @return Scheduler + * @throws Exception / + */ + @Bean(name = "scheduler") + public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception { + SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); + factoryBean.setJobFactory(quartzJobFactory); + factoryBean.afterPropertiesSet(); + Scheduler scheduler = factoryBean.getScheduler(); + scheduler.start(); + return scheduler; + } + +} diff --git a/src/main/java/com/yfd/platform/config/ResponseResult.java b/src/main/java/com/yfd/platform/config/ResponseResult.java new file mode 100644 index 0000000..9638fcf --- /dev/null +++ b/src/main/java/com/yfd/platform/config/ResponseResult.java @@ -0,0 +1,57 @@ +package com.yfd.platform.config; + +import java.util.HashMap; + +public class ResponseResult extends HashMap { + private static final long serialVersionUID = 1L; + + public ResponseResult() { + } + + public static ResponseResult unlogin() { + return message("401", "未登录"); + } + + public static ResponseResult error() { + return error("操作失败"); + } + + public static ResponseResult success() { + return success("操作成功"); + } + + public static ResponseResult error(String msg) { + ResponseResult json = new ResponseResult(); + json.put((String)"code", "1");//错误 + json.put((String)"msg", msg); + return json; + } + + public static ResponseResult message(String code, String msg) { + ResponseResult json = new ResponseResult(); + json.put((String)"code", code); + json.put((String)"msg", msg); + return json; + } + + public static ResponseResult success(String msg) { + ResponseResult json = new ResponseResult(); + json.put((String)"code", "0");//正常 + json.put((String)"msg", msg); + return json; + } + + public static ResponseResult successData(Object obj) { + ResponseResult json = new ResponseResult(); + json.put((String)"code", "0");//正常 + json.put((String)"msg", "操作成功"); + json.put("data", obj); + return json; + } + + + public ResponseResult put(String key, Object value) { + super.put(key, value); + return this; + } +} diff --git a/src/main/java/com/yfd/platform/config/SecurityConfig.java b/src/main/java/com/yfd/platform/config/SecurityConfig.java new file mode 100644 index 0000000..f1e1cfa --- /dev/null +++ b/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -0,0 +1,91 @@ +package com.yfd.platform.config; + +import com.yfd.platform.config.bean.LoginProperties; +import com.yfd.platform.exception.AccessDeniedHandExcetion; +import com.yfd.platform.exception.AuthenticationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +public class SecurityConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + @ConfigurationProperties(prefix = "login", ignoreUnknownFields = true) + public LoginProperties loginProperties() { + return new LoginProperties(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } + + @Autowired + private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + + @Autowired + private AuthenticationException authenticationException; + + @Autowired + private AccessDeniedHandExcetion accessDeniedHandExcetion; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) + .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/user/login").anonymous() + .requestMatchers("/user/code").permitAll() + .requestMatchers(HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js", + "/webSocket/**").permitAll() + .requestMatchers( + "/swagger-ui.html", + "/swagger-ui/**", + "/v3/api-docs/**", + "/v3/api-docs.yaml", + "/swagger-resources/**", + "/webjars/**", + "/*/api-docs").permitAll() + .requestMatchers( + "/report/**", + "/images/**", + "/pageimage/**", + "/avatar/**", + "/systemurl/**", + "/api/imageserver/upload").permitAll() + .requestMatchers("/**/**").permitAll() + .anyRequest().authenticated() + ) + .cors(cors -> {}); + + http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + + http.exceptionHandling(ex -> ex + .authenticationEntryPoint(authenticationException) + .accessDeniedHandler(accessDeniedHandExcetion) + ); + + return http.build(); + } +} diff --git a/src/main/java/com/yfd/platform/config/SwaggerConfig.java b/src/main/java/com/yfd/platform/config/SwaggerConfig.java new file mode 100644 index 0000000..b92f09b --- /dev/null +++ b/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -0,0 +1,50 @@ +package com.yfd.platform.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springdoc.core.models.GroupedOpenApi; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.Contact; + +/** + * Springdoc OpenAPI 配置 + */ +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI projectOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("项目API 接口文档") + .version("3.0") + .description("") + .contact(new Contact().name("郑顺利").email("13910913995@163.com")) + ); + } + + @Bean + public GroupedOpenApi groupWebsiteApi() { + return GroupedOpenApi.builder() + .group("1. 深北莫网站") + .packagesToScan("com.yfd.platform.modules.sbmwebsitedb.controller") + .build(); + } + + @Bean + public GroupedOpenApi groupQuartzApi() { + return GroupedOpenApi.builder() + .group("2. 定时任务") + .packagesToScan("com.yfd.platform.modules.quartz.controller") + .build(); + } + + @Bean + public GroupedOpenApi groupSystemApi() { + return GroupedOpenApi.builder() + .group("3. 系统管理") + .packagesToScan("com.yfd.platform.system.controller") + .build(); + } +} diff --git a/src/main/java/com/yfd/platform/config/WebConfig.java b/src/main/java/com/yfd/platform/config/WebConfig.java new file mode 100644 index 0000000..c242a34 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/WebConfig.java @@ -0,0 +1,58 @@ +package com.yfd.platform.config; + +import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheUtil; +import lombok.SneakyThrows; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Resource + private FileSpaceProperties fileSpaceProperties; + + + + @Bean + public Cache loginuserCache() { + return CacheUtil.newLRUCache(200);//用户登录缓存数 缺省200 + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = + new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOriginPattern("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + config.setMaxAge(3600L); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + + @SneakyThrows + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 菜单图标访问路径 + String iconUrl = "file:" + System.getProperty("user.dir") + "\\src" + + "\\main\\resources\\static\\icon\\"; + registry.addResourceHandler("/menu/**").addResourceLocations(iconUrl).setCachePeriod(0); + + registry.addResourceHandler("swagger-ui.html").addResourceLocations( + "classpath:/META-INF/resources/"); + + String systemUrl = "file:" + fileSpaceProperties.getSystem().replace("\\", "/")+"user\\"; + registry.addResourceHandler("/avatar/**").addResourceLocations(systemUrl).setCachePeriod(0); + + + } + +} diff --git a/src/main/java/com/yfd/platform/config/WebSocketConfig.java b/src/main/java/com/yfd/platform/config/WebSocketConfig.java new file mode 100644 index 0000000..349ead0 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/WebSocketConfig.java @@ -0,0 +1,16 @@ +package com.yfd.platform.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + + return new ServerEndpointExporter(); + } +} + diff --git a/src/main/java/com/yfd/platform/config/bean/LoginCode.java b/src/main/java/com/yfd/platform/config/bean/LoginCode.java new file mode 100644 index 0000000..2a7586b --- /dev/null +++ b/src/main/java/com/yfd/platform/config/bean/LoginCode.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.bean; + +import lombok.Data; + +/** + * 登录验证码配置信息 + * + * @author: liaojinlong + * @date: 2020/6/10 18:53 + */ +@Data +public class LoginCode { + + /** + * 验证码配置 + */ + private LoginCodeEnum codeType; + /** + * 验证码有效期 分钟 + */ + private Long expiration = 2L; + /** + * 验证码内容长度 + */ + private int length = 2; + /** + * 验证码宽度 + */ + private int width = 111; + /** + * 验证码高度 + */ + private int height = 36; + /** + * 验证码字体 + */ + private String fontName; + /** + * 字体大小 + */ + private int fontSize = 25; + + public LoginCodeEnum getCodeType() { + return codeType; + } +} diff --git a/src/main/java/com/yfd/platform/config/bean/LoginCodeEnum.java b/src/main/java/com/yfd/platform/config/bean/LoginCodeEnum.java new file mode 100644 index 0000000..d9ade21 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/bean/LoginCodeEnum.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.bean; + +/** + * 验证码配置枚举 + * + * @author: liaojinlong + * @date: 2020/6/10 17:40 + */ + +public enum LoginCodeEnum { + /** + * 算数 + */ + arithmetic, + /** + * 中文 + */ + chinese, + /** + * 中文闪图 + */ + chinese_gif, + /** + * 闪图 + */ + gif, + spec +} diff --git a/src/main/java/com/yfd/platform/config/bean/LoginProperties.java b/src/main/java/com/yfd/platform/config/bean/LoginProperties.java new file mode 100644 index 0000000..b16644d --- /dev/null +++ b/src/main/java/com/yfd/platform/config/bean/LoginProperties.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version loginCode.length.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-loginCode.length.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.bean; + +import cn.hutool.core.util.StrUtil; +import com.wf.captcha.*; +import com.wf.captcha.base.Captcha; +import com.yfd.platform.exception.BadConfigurationException; +import lombok.Data; +import java.awt.*; +import java.util.Objects; + +/** + * 配置文件读取 + * + * @author liaojinlong + * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 + */ +@Data +public class LoginProperties { + + /** + * 账号单用户 登录 + */ + private boolean singleLogin = false; + + private LoginCode loginCode; + /** + * 用户登录信息缓存 + */ + private boolean cacheEnable; + + public boolean isSingleLogin() { + return singleLogin; + } + + public boolean isCacheEnable() { + return cacheEnable; + } + + /** + * 获取验证码生产类 + * + * @return / + */ + public Captcha getCaptcha() { + if (Objects.isNull(loginCode)) { + loginCode = new LoginCode(); + if (Objects.isNull(loginCode.getCodeType())) { + loginCode.setCodeType(LoginCodeEnum.arithmetic); + } + } + return switchCaptcha(loginCode); + } + + /** + * 依据配置信息生产验证码 + * + * @param loginCode 验证码配置信息 + * @return / + */ + private Captcha switchCaptcha(LoginCode loginCode) { + Captcha captcha; + synchronized (this) { + switch (loginCode.getCodeType()) { + case arithmetic: + // 算术类型 https://gitee.com/whvse/EasyCaptcha + captcha = new ArithmeticCaptcha(loginCode.getWidth(), loginCode.getHeight()); + // 几位数运算,默认是两位 + captcha.setLen(loginCode.getLength()); + break; + case chinese: + captcha = new ChineseCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case chinese_gif: + captcha = new ChineseGifCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case gif: + captcha = new GifCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case spec: + captcha = new SpecCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + default: + throw new BadConfigurationException("验证码配置信息错误!正确配置查看 LoginCodeEnum "); + } + } + if(StrUtil.isNotBlank(loginCode.getFontName())){ + captcha.setFont(new Font(loginCode.getFontName(), Font.PLAIN, loginCode.getFontSize())); + } + return captcha; + } +} diff --git a/src/main/java/com/yfd/platform/config/thread/AsyncTaskExecutePool.java b/src/main/java/com/yfd/platform/config/thread/AsyncTaskExecutePool.java new file mode 100644 index 0000000..ff10654 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/thread/AsyncTaskExecutePool.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 异步任务线程池装配类 + * @author https://juejin.im/entry/5abb8f6951882555677e9da2 + * @date 2019年10月31日15:06:18 + */ +@Slf4j +@Configuration +public class AsyncTaskExecutePool implements AsyncConfigurer { + + /** 注入配置类 */ + private final AsyncTaskProperties config; + + public AsyncTaskExecutePool(AsyncTaskProperties config) { + this.config = config; + } + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + //核心线程池大小 + executor.setCorePoolSize(config.getCorePoolSize()); + //最大线程数 + executor.setMaxPoolSize(config.getMaxPoolSize()); + //队列容量 + executor.setQueueCapacity(config.getQueueCapacity()); + //活跃时间 + executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); + //线程名字前缀 + executor.setThreadNamePrefix("el-async-"); + // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务 + // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + log.error("===="+throwable.getMessage()+"====", throwable); + log.error("exception method:"+method.getName()); + }; + } +} diff --git a/src/main/java/com/yfd/platform/config/thread/AsyncTaskProperties.java b/src/main/java/com/yfd/platform/config/thread/AsyncTaskProperties.java new file mode 100644 index 0000000..a5bc7d2 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/thread/AsyncTaskProperties.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 线程池配置属性类 + * @author https://juejin.im/entry/5abb8f6951882555677e9da2 + * @date 2019年10月31日14:58:18 + */ +@Data +@Component +@ConfigurationProperties(prefix = "task.pool") +public class AsyncTaskProperties { + + private int corePoolSize; + + private int maxPoolSize; + + private int keepAliveSeconds; + + private int queueCapacity; +} diff --git a/src/main/java/com/yfd/platform/config/thread/TheadFactoryName.java b/src/main/java/com/yfd/platform/config/thread/TheadFactoryName.java new file mode 100644 index 0000000..118faba --- /dev/null +++ b/src/main/java/com/yfd/platform/config/thread/TheadFactoryName.java @@ -0,0 +1,62 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + +import org.springframework.stereotype.Component; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 自定义线程名称 + * @author + * @date 2019年10月31日17:49:55 + */ +@Component +public class TheadFactoryName implements ThreadFactory { + + private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public TheadFactoryName() { + this("el-pool"); + } + + private TheadFactoryName(String name){ + // 使用当前线程的线程组,避免依赖已弃用的 SecurityManager + group = Thread.currentThread().getThreadGroup(); + //此时namePrefix就是 name + 第几个用这个工厂创建线程池的 + this.namePrefix = name + + POOL_NUMBER.getAndIncrement(); + } + + @Override + public Thread newThread(Runnable r) { + //此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程 + Thread t = new Thread(group, r, + namePrefix + "-thread-"+threadNumber.getAndIncrement(), + 0); + if (t.isDaemon()) { + t.setDaemon(false); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } +} diff --git a/src/main/java/com/yfd/platform/config/thread/ThreadPoolExecutorUtil.java b/src/main/java/com/yfd/platform/config/thread/ThreadPoolExecutorUtil.java new file mode 100644 index 0000000..cb84cc4 --- /dev/null +++ b/src/main/java/com/yfd/platform/config/thread/ThreadPoolExecutorUtil.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + + + +import com.yfd.platform.utils.SpringContextHolder; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * 用于获取自定义线程池 + * @author + * @date 2019年10月31日18:16:47 + */ +public class ThreadPoolExecutorUtil { + + public static ThreadPoolExecutor getPoll(){ + AsyncTaskProperties properties = SpringContextHolder.getBean(AsyncTaskProperties.class); + return new ThreadPoolExecutor( + properties.getCorePoolSize(), + properties.getMaxPoolSize(), + properties.getKeepAliveSeconds(), + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(properties.getQueueCapacity()), + new TheadFactoryName() + ); + } +} diff --git a/src/main/java/com/yfd/platform/constant/Constant.java b/src/main/java/com/yfd/platform/constant/Constant.java new file mode 100644 index 0000000..c460999 --- /dev/null +++ b/src/main/java/com/yfd/platform/constant/Constant.java @@ -0,0 +1,42 @@ +package com.yfd.platform.constant; + +/** + * @author TangWei + * @Date: 2023/3/3 17:40 + * @Description: 常量类 + */ +public class Constant { + + public static final String LOGIN = "login:"; + public static final String TOKEN = "token:"; + public static final String USER_ID = "userid"; + public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + public static final String CODE_KEY = "code-key-"; + public static final long CODE_EXPIRATION_TIME = 1000 * 60; + /** + * 用于IP定位转换 + */ + public static final String REGION = "内网IP|内网IP"; + /** + * win 系统 + */ + public static final String WIN = "win"; + + /** + * mac 系统 + */ + public static final String MAC = "mac"; + + /** + * 常用接口 + */ + public static class Url { + + // IP归属地查询 + // public static final String IP_URL = "http://whois.pconline.com + // .cn/ipJson.jsp?ip=%s&json=true"; + public static final String IP_URL = "http://whois.pconline.com" + + ".cn/ipJson.jsp?ip=%s&json=true"; + } +} diff --git a/src/main/java/com/yfd/platform/datasource/DataSource.java b/src/main/java/com/yfd/platform/datasource/DataSource.java new file mode 100644 index 0000000..7c6d795 --- /dev/null +++ b/src/main/java/com/yfd/platform/datasource/DataSource.java @@ -0,0 +1,17 @@ +package com.yfd.platform.datasource; + +import java.lang.annotation.*; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:48 + ******************************/ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataSource { + + String name() default ""; + +} diff --git a/src/main/java/com/yfd/platform/datasource/DataSourceAspect.java b/src/main/java/com/yfd/platform/datasource/DataSourceAspect.java new file mode 100644 index 0000000..f20c0f8 --- /dev/null +++ b/src/main/java/com/yfd/platform/datasource/DataSourceAspect.java @@ -0,0 +1,55 @@ +package com.yfd.platform.datasource; + +import cn.hutool.core.util.StrUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:50 + ******************************/ +@Aspect +@Component +public class DataSourceAspect { + + @Pointcut("@annotation(com.yfd.platform.datasource.DataSource)") + public void dataSourcePointCut() { + + } + + private String DataBaseName; + + @Around("dataSourcePointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + if (StrUtil.isNotBlank(DataBaseName)){ + DynamicDataSource.setDataSource(DataBaseName); + }else { + DynamicDataSource.setDataSource("master"); + } + + try { + return point.proceed(); + } finally { + DynamicDataSource.clearDataSource(); + } + } + + public String getDataBase(Integer type){ + if (type == 1){ + DataBaseName="master"; + }else { + DataBaseName="slave"; + } + return DataBaseName; + } + +} diff --git a/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java b/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java new file mode 100644 index 0000000..8b52521 --- /dev/null +++ b/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java @@ -0,0 +1,40 @@ +package com.yfd.platform.datasource; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.Map; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:47 + ******************************/ +public class DynamicDataSource extends AbstractRoutingDataSource { + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() { + return getDataSource(); + } + + public static void setDataSource(String dataSource) { + contextHolder.set(dataSource); + } + + public static String getDataSource() { + return contextHolder.get(); + } + + public static void clearDataSource() { + contextHolder.remove(); + } + + +} diff --git a/src/main/java/com/yfd/platform/datasource/DynamicDataSourceConfig.java b/src/main/java/com/yfd/platform/datasource/DynamicDataSourceConfig.java new file mode 100644 index 0000000..fc4a972 --- /dev/null +++ b/src/main/java/com/yfd/platform/datasource/DynamicDataSourceConfig.java @@ -0,0 +1,38 @@ +package com.yfd.platform.datasource; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:45 + ******************************/ +@Configuration +@Component +public class DynamicDataSourceConfig { + + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource wglMasterDataSource(){ + return DruidDataSourceBuilder.create().build(); + } + + @Bean + @Primary + public DynamicDataSource dataSource(DataSource wglMasterDataSource, DataSource wglSlaveDataSource) { + Map targetDataSources = new HashMap<>(); + targetDataSources.put("master",wglMasterDataSource); + return new DynamicDataSource(wglMasterDataSource, targetDataSources); + } + + +} diff --git a/src/main/java/com/yfd/platform/exception/AccessDeniedHandExcetion.java b/src/main/java/com/yfd/platform/exception/AccessDeniedHandExcetion.java new file mode 100644 index 0000000..0bb0ed3 --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/AccessDeniedHandExcetion.java @@ -0,0 +1,27 @@ +package com.yfd.platform.exception; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +@Component +public class AccessDeniedHandExcetion implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + JSONObject jobj=new JSONObject(); + jobj.putOnce("status","403"); + jobj.putOnce("msg","用户权限不足,不能访问"); + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().println(JSONUtil.toJsonStr(jobj)); + } +} diff --git a/src/main/java/com/yfd/platform/exception/AuthenticationException.java b/src/main/java/com/yfd/platform/exception/AuthenticationException.java new file mode 100644 index 0000000..15e8e97 --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/AuthenticationException.java @@ -0,0 +1,32 @@ +package com.yfd.platform.exception; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.config.ResponseResult; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +@Component +public class AuthenticationException implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException { + JSONObject jobj=new JSONObject(); + if(authException.getMessage().equals("用户账号不存在!")){ + jobj.putOnce("code","401"); + jobj.putOnce("msg","用户账号不存在/密码错误,登录失败!"); + }else{ + jobj.putOnce("code","401"); + jobj.putOnce("msg","用户Token失效,请重新登录!"); + } + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().println(JSONUtil.toJsonStr(jobj)); + } +} diff --git a/src/main/java/com/yfd/platform/exception/BadConfigurationException.java b/src/main/java/com/yfd/platform/exception/BadConfigurationException.java new file mode 100644 index 0000000..92ffac0 --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/BadConfigurationException.java @@ -0,0 +1,98 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +/** + * 统一关于错误配置信息 异常 + * + * @author: liaojinlong + * @date: 2020/6/10 18:06 + */ +public class BadConfigurationException extends RuntimeException { + /** + * Constructs a new runtime exception with {@code null} as its + * detail message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause}. + */ + public BadConfigurationException() { + super(); + } + + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public BadConfigurationException(String message) { + super(message); + } + + /** + * Constructs a new runtime exception with the specified detail message and + * cause.

Note that the detail message associated with + * {@code cause} is not automatically incorporated in + * this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public BadConfigurationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new runtime exception with the specified cause and a + * detail message of {@code (cause==null ? null : cause.toString())} + * (which typically contains the class and detail message of + * {@code cause}). This constructor is useful for runtime exceptions + * that are little more than wrappers for other throwables. + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public BadConfigurationException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new runtime exception with the specified detail + * message, cause, suppression enabled or disabled, and writable + * stack trace enabled or disabled. + * + * @param message the detail message. + * @param cause the cause. (A {@code null} value is permitted, + * and indicates that the cause is nonexistent or unknown.) + * @param enableSuppression whether or not suppression is enabled + * or disabled + * @param writableStackTrace whether or not the stack trace should + * be writable + * @since 1.7 + */ + protected BadConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/yfd/platform/exception/BadRequestException.java b/src/main/java/com/yfd/platform/exception/BadRequestException.java new file mode 100644 index 0000000..f2202ec --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/BadRequestException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +/** + * @author + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException{ + + private Integer status = BAD_REQUEST.value(); + + public BadRequestException(String msg){ + super(msg); + } + + public BadRequestException(HttpStatus status, String msg){ + super(msg); + this.status = status.value(); + } +} diff --git a/src/main/java/com/yfd/platform/exception/ChildrenExistException.java b/src/main/java/com/yfd/platform/exception/ChildrenExistException.java new file mode 100644 index 0000000..18eca34 --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/ChildrenExistException.java @@ -0,0 +1,20 @@ +package com.yfd.platform.exception; + +import org.springframework.util.StringUtils; + +/** + * @Author pcj + * @Date 2021/1/26 9:07 + * @Version 1.0 + */ +public class ChildrenExistException extends RuntimeException{ + + public ChildrenExistException(Class clazz, String field, String val) { + super(ChildrenExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " Children Exist"; + } +} diff --git a/src/main/java/com/yfd/platform/exception/EntityExistException.java b/src/main/java/com/yfd/platform/exception/EntityExistException.java new file mode 100644 index 0000000..028aeed --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/EntityExistException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +import org.springframework.util.StringUtils; + +/** + * @author + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, String field, String val) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " existed"; + } +} \ No newline at end of file diff --git a/src/main/java/com/yfd/platform/exception/EntityNotFoundException.java b/src/main/java/com/yfd/platform/exception/EntityNotFoundException.java new file mode 100644 index 0000000..8f5e1c5 --- /dev/null +++ b/src/main/java/com/yfd/platform/exception/EntityNotFoundException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +import org.springframework.util.StringUtils; + +/** + * @author + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, String field, String val) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " does not exist"; + } +} \ No newline at end of file diff --git a/src/main/java/com/yfd/platform/system/controller/DataSourceController.java b/src/main/java/com/yfd/platform/system/controller/DataSourceController.java new file mode 100644 index 0000000..b34b37c --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/DataSourceController.java @@ -0,0 +1,42 @@ +package com.yfd.platform.system.controller; + +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.datasource.DataSource; +import com.yfd.platform.datasource.DataSourceAspect; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; + +/** + * @author zhengsl + * @since 2022-09-20 + */ +@RestController +@RequestMapping("/system") +@Tag(name = "切换数据库") +public class DataSourceController { + + @Resource + DataSourceAspect dataSourceAspect; + + /** + * 切换数据库 + * + * @DataSource(name="master") 可以通过注解方式切换数据库 + */ + @GetMapping("/changeDataSource") + @Operation(summary = "切换数据库") + public ResponseResult changeDataSource(Integer type) { + if (type == null) { + return ResponseResult.error("参数为空"); + } + String dataBase = dataSourceAspect.getDataBase(type); + String mess = "已切换为" + dataBase + "数据库"; + return ResponseResult.success(mess); + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/LoginController.java b/src/main/java/com/yfd/platform/system/controller/LoginController.java new file mode 100644 index 0000000..ac08378 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/LoginController.java @@ -0,0 +1,238 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import cn.hutool.jwt.JWTUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.wf.captcha.base.Captcha; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.config.WebConfig; +import com.yfd.platform.config.bean.LoginCodeEnum; +import com.yfd.platform.config.bean.LoginProperties; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.RequestHolder; +import com.yfd.platform.utils.RsaUtils; +import com.yfd.platform.utils.StringUtils; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +/** + * @author TangWei + */ +@RestController +@RequestMapping("/user") +@Tag(name = "用户登录") +public class LoginController { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private WebConfig webConfig; + + @Resource + private IUserService userService; + + @Value("${rsa.private_key}") + private String privateKey; + + @Resource + private ISysLogService sysLogService; + + @Resource + private LoginProperties loginProperties; + + @PostMapping("/login") + @Operation(summary = "登录用户") + @ResponseBody + public ResponseResult login(SysUser user) throws Exception { + // 密码解密 + String password = RsaUtils.decryptByPrivateKey(privateKey, + user.getPassword()); + + // 是否需要验证码不需要改成false + boolean hascode = true; + if (hascode) { + // 查询验证码 + String code = webConfig.loginuserCache().get(user.getUuid()); + // 清除验证码 + webConfig.loginuserCache().remove(user.getUuid()); + if (StrUtil.isBlank(code)) { + return ResponseResult.error("验证码不存在或已过期"); + } + if (StrUtil.isBlank(user.getCode()) || !user.getCode().equalsIgnoreCase(code)) { + return ResponseResult.error("验证码错误"); + } + } + //如果认证通过了,使用userId生成token token存入ResponseResult返回 + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(user.getUsername(), + password); + Authentication authenticate = + authenticationManager.authenticate(authenticationToken); + if (ObjectUtil.isNull(authenticate)) { + return ResponseResult.unlogin(); + } + LoginUser loginUser = (LoginUser) authenticate.getPrincipal(); + Integer status = loginUser.getUser().getStatus(); + if ("0".equals(status.toString())) { + return ResponseResult.error("账号已停用"); + } + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + SysLog sysLog = new SysLog(); + sysLog.setUsercode(user.getUsername()); + sysLog.setUsername(loginUser.getUser().getNickname()); + sysLog.setRequestip(StringUtils.getIp(request)); + sysLog.setBrowser(StringUtils.getBrowser(request)); + sysLog.setOpttype("登录(login)"); + sysLog.setModule("用户登录"); + String className = this.getClass().getName(); + String method = + Thread.currentThread().getStackTrace()[1].getMethodName(); + sysLog.setMethod(className + "." + method + "()"); + //sysLog.setParams(user.toString()); + sysLog.setDescription(loginUser.getUser().getNickname() + "登录系统!"); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); + String userId = loginUser.getUser().getId(); + Map map = new HashMap(10) { + private static final long serialVersionUID = 1L; + + { + put("userid", userId); + put("username", loginUser.getUsername()); + long expireTime = + System.currentTimeMillis() + (long) (30L * 24L * 60L * 60L * 1000L); + put("expire_time", expireTime);//个月过期 + } + }; + + String token = JWTUtil.createToken(map, "12345678".getBytes()); + map.put("token", token); + //把完整的用户信息存入到HuTool缓存中,userId作为key + String jsonStr = JSONUtil.toJsonStr(loginUser); + webConfig.loginuserCache().put("login:" + userId, jsonStr); + webConfig.loginuserCache().put("expire_time:" + userId, map.get("expire_time").toString()); + return ResponseResult.successData(map); + } + + @Operation(summary = "获取验证码") + @GetMapping(value = "/code") + public ResponseResult getCode() { + // 获取运算的结果 + Captcha captcha = loginProperties.getCaptcha(); + String uuid = Constant.CODE_KEY + IdUtil.simpleUUID(); + //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 + String captchaValue = captcha.text(); + if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() && captchaValue.contains(".")) { + captchaValue = captchaValue.split("\\.")[0]; + } + // 保存 + //redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode() + // .getExpiration(), TimeUnit.MINUTES); + // 将验证码放入缓存,设置失效时间为60秒 + webConfig.loginuserCache().put(uuid, captchaValue, + Constant.CODE_EXPIRATION_TIME); + // 验证码信息 + Map imgResult = new HashMap(2) {{ + put("img", captcha.toBase64()); + put("uuid", uuid); + }}; + return ResponseResult.successData(imgResult); + } + + @PostMapping("/logout") + @Operation(summary = "退出登录") + @ResponseBody + public ResponseResult logout() { + //获取SecurityContextHolder中的用户id + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + String userId = loginuser.getUser().getId(); + //删除redis中的登陆用户信息 + webConfig.loginuserCache().remove("login:" + userId); + //记录退出日志 + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + SysLog sysLog = new SysLog(); + sysLog.setUsercode(loginuser.getUsername()); + sysLog.setUsername(loginuser.getUser().getNickname()); + sysLog.setRequestip(StringUtils.getIp(request)); + sysLog.setBrowser(StringUtils.getBrowser(request)); + sysLog.setOpttype("其他(other)"); + sysLog.setModule("注销退出"); + sysLog.setDescription("注销退出系统!"); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); + return ResponseResult.success(); + } + + @Log(module = "用户登录", value = "更改用户密码") + @GetMapping("/updatePassword") + @Operation(summary = "更改用户密码") + @ResponseBody + public ResponseResult updatePassword(@RequestBody SysUser user) throws Exception { + // 密码解密 + String password = RsaUtils.decryptByPrivateKey(privateKey, + user.getPassword()); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + String cryptPassword = passwordEncoder.encode(password); + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.set("password", cryptPassword); + updateWrapper.eq("id", user.getId()); + userService.update(updateWrapper); + return ResponseResult.success(); + } + + @GetMapping("/me") + @Operation(summary = "查询当前用户信息") + @ResponseBody + public ResponseResult getUserInfo() { + ResponseResult responseResult = userService.getLoginUserInfo(); + return ResponseResult.successData(responseResult); + } + + @Log(module = "用户登录", value = "修改个人信息") + @PostMapping("/updatePersonalInfo") + @Operation(summary = "修改个人信息") + @ResponseBody + public ResponseResult updateUser(@org.springframework.web.bind.annotation.RequestBody SysUser user) { + if (StrUtil.isEmpty(user.getId())) { + return ResponseResult.error("没有用户ID"); + } + //填写 当前用户名称 + user.setLastmodifier(userService.getUsername()); + //填写 当前日期 + user.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok = userService.updateById(user); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/MessageController.java b/src/main/java/com/yfd/platform/system/controller/MessageController.java new file mode 100644 index 0000000..58f61ba --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/MessageController.java @@ -0,0 +1,150 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.MessageConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.config.WebConfig; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.util.*; + +/** + *

+ * 消息通知 前端控制器 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +@RestController +@RequestMapping("/system/message") +@Tag(name = "消息通知") +public class MessageController { + + @Resource + private IMessageService messageService; + + @Resource + private MessageConfig messageConfig; + + @Operation(summary = "查询消息") + @GetMapping("/getMessageList") + public ResponseResult getMessageList(Page page, + String status, String title, + String type, String startDate, + String endDate) { + if (StrUtil.isBlank(status)) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if ("0".equals(status)) { + queryWrapper.eq(Message::getStatus, "1"); + } else { + List statusList = new ArrayList<>(); + statusList.add("2"); + statusList.add("9"); + queryWrapper.in(Message::getStatus, statusList); + + if (StrUtil.isNotBlank(title)) { + queryWrapper.like(Message::getTitle, title); + } + + if (StrUtil.isNotBlank(type)) { + queryWrapper.eq(Message::getType, type); + } + + DateTime parseStartDate = DateUtil.parse(startDate); + DateTime parseEndDate = DateUtil.parse(endDate); + DateTime dateTime = DateUtil.offsetDay(parseEndDate, 1); + + if (parseStartDate != null && parseEndDate != null) { + queryWrapper.ge(Message::getCreatetime, parseStartDate).lt(Message::getCreatetime, dateTime); + } + } + queryWrapper.orderByDesc(Message::getCreatetime); + Page pageList = messageService.page(page, queryWrapper); + return ResponseResult.successData(pageList); + } + + @Operation(summary = "根据ID查询消息") + @GetMapping("/getMessageById") + public ResponseResult getMessageById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + Message message = messageService.getById(id); + Map map = new HashMap<>(); + map.put("title", message.getTitle()); + map.put("content", message.getContent()); + map.put("createtime", message.getCreatetime()); + return ResponseResult.successData(map); + } + + @Log(module = "消息通知",value = "根据ID删除消息") + @Operation(summary = "根据ID删除消息") + @PostMapping("/deleteMessageById") + public ResponseResult deleteMessageById(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] split = id.split(","); + List idList = Arrays.asList(split); + boolean ok = messageService.removeByIds(idList); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + + } + + @Log(module = "消息通知", value = "将消息标记为已阅状态") + @Operation(summary = "标记已阅") + @PostMapping("/setMessageStatus") + public ResponseResult setMessageStatus(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] split = id.split(","); + long time = System.currentTimeMillis(); + for (String mid : split) { + Message message = messageService.getById(mid); + if ("9".equals(message.getStatus())) { + continue; + } + message.setStatus("2"); + message.setReadtime(new Timestamp(time)); + messageService.updateById(message); + } + messageConfig.sendMessage(); + return ResponseResult.success(); + } + + @Operation(summary = "全部已阅") + @PostMapping("/setAllMessageStatus") + public ResponseResult setAllMessageStatus() { + long time = System.currentTimeMillis(); + List list = + messageService.list(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + for (Message message : list) { + message.setStatus("2"); + message.setReadtime(new Timestamp(time)); + messageService.updateById(message); + } + messageConfig.sendMessage(); + return ResponseResult.success(); + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/QuartzJobController.java b/src/main/java/com/yfd/platform/system/controller/QuartzJobController.java new file mode 100644 index 0000000..9da6c89 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/QuartzJobController.java @@ -0,0 +1,183 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.service.IQuartzJobService; +import com.yfd.platform.system.service.impl.UserServiceImpl; +import com.yfd.platform.utils.QuartzManage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.quartz.CronExpression; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + *

+ * 定时任务 前端控制器 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +@RestController +@RequestMapping("/system/quartzjob") +@Tag(name = "定时任务") +@Transactional +public class QuartzJobController { + + @Resource + private IQuartzJobService quartzJobService; + + @Resource + private UserServiceImpl currentUser; + + @Resource + private QuartzManage quartzManage; + + @Operation(summary = "查询定时任务") + @GetMapping("/getQuartzJobList") + public ResponseResult getQuartzJobList(Page page, + String jobName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(jobName)) { + queryWrapper.like(QuartzJob::getJobName, jobName); + } + queryWrapper.orderByAsc(QuartzJob::getOrderno); + Page pageList = quartzJobService.page(page, queryWrapper); + return ResponseResult.successData(pageList); + } + + @Log(module = "定时任务管理", value = "新增定时任务") + @Operation(summary = "新增定时任务") + @PostMapping("/addQuartzJob") + public ResponseResult addQuartzJob(@RequestBody QuartzJob quartzJob) { + if (quartzJob == null) { + return ResponseResult.error("参数为空"); + } + // 添加最近修改人 + quartzJob.setLastmodifier(currentUser.getUsername()); + // 添加最近修改时间 + quartzJob.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + if (StrUtil.isBlank(quartzJob.getJobCron()) || !CronExpression.isValidExpression(quartzJob.getJobCron())) { + return ResponseResult.error("cron表达式格式错误"); + } + quartzJob.setStatus("0"); + boolean ok = quartzJobService.addQuartzJob(quartzJob); + quartzManage.addJob(quartzJob); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "定时任务管理", value = "设置定时任务是否有效") + @Operation(summary = "设置定时任务是否有效") + @PostMapping("/setQuartzStatus") + public ResponseResult setQuartzStatus(@RequestParam String id, + @RequestParam String status) { + if (StrUtil.isBlank(id) || StrUtil.isBlank(status)) { + return ResponseResult.error("参数为空"); + } + LambdaUpdateWrapper updateWrapper = + new LambdaUpdateWrapper<>(); + //根据id 更新状态,最近修改人,最近修改时间 + updateWrapper.eq(QuartzJob::getId, id).set(QuartzJob::getStatus, + status).set( + QuartzJob::getLastmodifier, currentUser.getUsername()).set(QuartzJob::getLastmodifydate, + LocalDateTime.now()); + boolean ok = quartzJobService.update(updateWrapper); + QuartzJob quartzJob = quartzJobService.getById(id); + if ("0".equals(quartzJob.getStatus())) { + quartzManage.pauseJob(quartzJob); + } else { + quartzManage.resumeJob(quartzJob); + } + + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @Operation(summary = "根据ID查询定时任务") + @GetMapping("/getQuartzJobById") + public ResponseResult getQuartzJobById(String id) { + QuartzJob quartzJob = quartzJobService.getById(id); + return ResponseResult.successData(quartzJob); + } + + @Log(module = "定时任务管理", value = "修改定时任务") + @Operation(summary = "修改定时任务") + @PostMapping("/updateQuartzJob") + @Transactional(rollbackFor = Exception.class) + public ResponseResult updateQuartzJob(@RequestBody QuartzJob quartzJob) { + // 添加最近修改人 + quartzJob.setLastmodifier(currentUser.getUsername()); + // 添加最近修改时间 + quartzJob.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + if (StrUtil.isBlank(quartzJob.getJobCron()) || !CronExpression.isValidExpression(quartzJob.getJobCron())) { + return ResponseResult.error("cron表达式格式错误"); + } + boolean ok = quartzJobService.updateById(quartzJob); + quartzManage.updateJobCron(quartzJob); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "定时任务管理", value = "删除定时任务") + @Operation(summary = "删除定时任务") + @PostMapping("/deleteQuartzJob") + public ResponseResult deleteQuartzJob(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = quartzJobService.deleteQuartzJob(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "定时任务管理", value = "执行定时任务") + @Operation(summary = "执行定时任务") + @PostMapping("/execution") + public ResponseResult execution(@RequestParam String id) { + quartzJobService.execution(quartzJobService.getById(id)); + return ResponseResult.success(); + } + + /********************************** + * 用途说明: 拖动修改定时顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 成功或者失败 + ***********************************/ + @Log(module = "定时任务管理", value = "拖动定时任务") + @PostMapping("/changeDictOrder") + @Operation(summary = "拖动修改定时任务顺序") + public ResponseResult changeQuartzOrder(@RequestParam String fromID, + @RequestParam String toID) { + + boolean ok = quartzJobService.changeDictOrder(fromID, toID); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } +} diff --git a/src/main/java/com/yfd/platform/system/controller/SSEController.java b/src/main/java/com/yfd/platform/system/controller/SSEController.java new file mode 100644 index 0000000..41adfbb --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SSEController.java @@ -0,0 +1,59 @@ +package com.yfd.platform.system.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.config.WebConfig; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import jakarta.annotation.Resource; + +/** + * @author Huhailong + */ +@Slf4j +@RestController +@CrossOrigin +@RequestMapping("/sse") +@Tag(name = "SSE推送服务") +public class SSEController { + + @Resource + private IMessageService messageService; + + @GetMapping("/connect/{token}") + @Operation(summary = "建立连接") + public SseEmitter connect(@PathVariable String token) { + SseEmitter connect = ServerSendEventServer.connect(token); + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + ServerSendEventServer.sendMessage(token, count + ""); + return connect; + } + + @GetMapping("/sendmsg") + @Operation(summary = "发送消息") + public void sendMessage(String token, String message) throws InterruptedException { + + ServerSendEventServer.sendMessage(token, message); + } + + @GetMapping("/sendgroupmsg") + @Operation(summary = "多人发送消息") + public void sendgroupmsg(String groupid, String message) throws InterruptedException { + ServerSendEventServer.groupSendMessage(groupid, message); + } + + @GetMapping("/disconnect/{token}") + @Operation(summary = "关闭连接") + public void disconnect(@PathVariable String token) throws InterruptedException { + ServerSendEventServer.removeUser(token); + } +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysConfigController.java b/src/main/java/com/yfd/platform/system/controller/SysConfigController.java new file mode 100644 index 0000000..3cfa7cc --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysConfigController.java @@ -0,0 +1,68 @@ +package com.yfd.platform.system.controller; + + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysConfig; +import com.yfd.platform.system.service.ISysConfigService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.io.IOException; +import java.sql.Timestamp; + + +/** + *

+ * 系统全局配置 前端控制器 + *

+ * + * @author zhengsl + * @since 2022-01-19 + */ +@RestController + @RequestMapping("/system/config") +@Tag(name = "系统全局配置") +public class SysConfigController { + @Resource + private ISysConfigService configService; + + @Resource + private IUserService userService; + + @PostMapping("/getOneById") + @Operation(summary = "根据id查询全局配置详情记录") + @ResponseBody + public SysConfig getOneById(String id){ + return configService.getById(id); + } + + @PostMapping("/addConfig") + @Operation(summary = "根据id查询全局配置详情记录") + @ResponseBody + public ResponseResult addConfig(@RequestBody SysConfig config ) throws IOException, UnsupportedAudioFileException { + if (StrUtil.isEmpty(config.getId())){ + config.setId(IdUtil.fastSimpleUUID()); } + config.setLastmodifier(userService.getUsername()); + config.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok=configService.save(config); + return ResponseResult.success(); + } + + @PostMapping("/updateById") + @Operation(summary = "根据id修改全局配置记录") + @ResponseBody + public ResponseResult updateById(@RequestBody SysConfig config) throws IOException, UnsupportedAudioFileException { + config.setLastmodifier(userService.getUsername()); + config.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok=configService.updateById(config); + return ResponseResult.success(); + } + + +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java b/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java new file mode 100644 index 0000000..70a5fdf --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java @@ -0,0 +1,142 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.service.ISysDictionaryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Objects; + +/** + *

+ * 数据字典表 前端控制器 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@RestController +@RequestMapping("/system/dictionary") +@Tag(name = "数据字典") +public class SysDictionaryController { + + @Resource + private ISysDictionaryService sysDictionaryService; + + /********************************** + * 用途说明: 获取数据字典列表 + * 参数说明 dictType 字典类型 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @GetMapping("/dictList") + @Operation(summary = "获取数据字典列表") + public ResponseResult getDictList(String dictType) { + if (StrUtil.isBlank(dictType)) { + return ResponseResult.error("参数为空"); + } + List sysDictionaries = + sysDictionaryService.getDictList(dictType); + return ResponseResult.successData(sysDictionaries); + } + + /********************************** + * 用途说明: 根据ID删除字典 + * 参数说明 id 字典ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除结果成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "根据ID删除字典") + @PostMapping("/deleteById") + @Operation(summary = "根据ID删除字典") + public ResponseResult deleteDictById(@RequestParam String id) { + boolean ok = sysDictionaryService.deleteDictById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 新增字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "新增数据字典") + @PostMapping("/addDict") + @Operation(summary = "新增字典") + public ResponseResult addDict(@RequestBody SysDictionary sysDictionary) { + if (sysDictionary == null) { + return ResponseResult.error("参数为空"); + } + boolean ok = sysDictionaryService.addDict(sysDictionary); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 修改字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "修改数据字典") + @PostMapping("/updateDict") + @Operation(summary = "修改字典") + public ResponseResult updateDict(@RequestBody SysDictionary sysDictionary) { + if (sysDictionary == null) { + return ResponseResult.error("参数为空"); + } + boolean ok = sysDictionaryService.updateById(sysDictionary); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 根据ID查询字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回查询结果 + ***********************************/ + @PostMapping("/getDictById") + @Operation(summary = "根据ID查询字典") + public ResponseResult getDictById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + SysDictionary sysDictionary = sysDictionaryService.getById(id); + return ResponseResult.successData(sysDictionary); + } + + /********************************** + * 用途说明: 拖动修改字典顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "拖动修改字典顺序") + @PostMapping("/changeDictOrder") + @Operation(summary = "拖动修改字典顺序") + public ResponseResult changeDictOrder(@RequestParam String fromID, + @RequestParam String toID) { + + boolean ok = sysDictionaryService.changeDictOrder(fromID, toID); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java b/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java new file mode 100644 index 0000000..bf85d30 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java @@ -0,0 +1,201 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Arrays; +import java.util.List; + +/** + *

+ * 数据字典明细 前端控制器 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@RestController +@RequestMapping("/system/dictionaryItems") +@Tag(name = "数据字典项") +public class SysDictionaryItemsController { + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + /********************************** + * 用途说明: 分页查询字典项信息 + * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @GetMapping("/page") + @Operation(summary = "分页查询字典项信息") + public ResponseResult getDictItemPage(String dictId, String dictName, + Page page) { + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId, dictId).orderByAsc(SysDictionaryItems::getOrderNo); + + // 查询前将序号初始化 + List list = + sysDictionaryItemsService.list(queryWrapper); + for (int i = 0; i < list.size(); i++) { + SysDictionaryItems sysDictionaryItems = list.get(i); + sysDictionaryItems.setOrderNo(i + 1); + sysDictionaryItemsService.updateById(sysDictionaryItems); + } + Page sysDictionaryItemsPage = + sysDictionaryItemsService.getDictItemPage(dictId, dictName, + page); + + return ResponseResult.successData(sysDictionaryItemsPage); + } + + /********************************** + * 用途说明: 增加字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "增加字典项") + @PostMapping("/addDictionaryItem") + @Operation(summary = "增加字典项") + public ResponseResult addDictionaryItem(@RequestBody SysDictionaryItems sysDictionaryItems) { + if (sysDictionaryItems == null) { + return ResponseResult.error("参数为空"); + } + boolean ok = + sysDictionaryItemsService.addDictionaryItem(sysDictionaryItems); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 修改字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "修改字典项") + @PostMapping("/updateDictionaryItem") + @Operation(summary = "修改字典项") + public ResponseResult updateDictionaryItem(@RequestBody SysDictionaryItems sysDictionaryItems) { + if (sysDictionaryItems == null) { + return ResponseResult.error("参数为空"); + } + boolean ok = + sysDictionaryItemsService.updateById(sysDictionaryItems); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 根据ID查询字典项 + * 参数说明 id 字典项ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回字典项信息 + ***********************************/ + @GetMapping("/getDictItemById") + @Operation(summary = "根据ID查询字典项") + public ResponseResult getDictItemById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + SysDictionaryItems sysDictionaryItems = + sysDictionaryItemsService.getById(id); + return ResponseResult.successData(sysDictionaryItems); + } + + /********************************** + * 用途说明: 根据ID删除字典项 + * 参数说明 id 字典项ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "根据ID删除字典项") + @PostMapping("/deleteDictItemById") + @Operation(summary = "根据ID删除字典项") + public ResponseResult deleteDictItemById(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = sysDictionaryItemsService.removeById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 批量删除字典项 + * 参数说明 ids 字典项id数组 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 + ***********************************/ + @Log(module = "数据字典项", value = "批量删除字典项") + @PostMapping("/deleteDictItemByIds") + @Operation(summary = "批量删除字典项") + public ResponseResult deleteDictItemByIds(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] splitIds = id.split(","); + // 数组转集合 + List ids = Arrays.asList(splitIds); + boolean ok = sysDictionaryItemsService.removeByIds(ids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 拖动修改字典项顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "拖动修改字典项顺序") + @PostMapping("/changeItemOrder") + @Operation(summary = "拖动修改字典项顺序") + public ResponseResult changeItemOrder(@RequestParam String fromID, + @RequestParam String toID) { + boolean ok = sysDictionaryItemsService.changeItemOrder(fromID, toID); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 导出数据字典项数据 + * 参数说明 sysDictionaryItemsList 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或失败 + ***********************************/ + @Log(module = "数据字典项", value = "导出字典数据到Excel") + @GetMapping("/exportExcel") + @Operation(summary = "导出数据字典项数据") + public void exportExcel(String dictID, String itemName, + Page page, + HttpServletResponse response) { + Page sysDictionaryItemsPage = + sysDictionaryItemsService.getDictItemPage(dictID, itemName, + page); + sysDictionaryItemsService.exportExcel(sysDictionaryItemsPage.getRecords(), response); + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysLogController.java b/src/main/java/com/yfd/platform/system/controller/SysLogController.java new file mode 100644 index 0000000..9f2081a --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysLogController.java @@ -0,0 +1,74 @@ +package com.yfd.platform.system.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.service.ISysLogService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统操作日志 前端控制器 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@RestController +@RequestMapping("/system/log") +@Tag(name = "系统日志") +public class SysLogController { + + @Resource + private ISysLogService sysLogService; + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 page分页对象、username(用户名)、(optType) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @PostMapping("/getLogList") + @Operation(summary = "分页查询日志信息") + public ResponseResult getLogList(String username, String optType, + String startDate, + String endDate, Page page) { + + Page sysLogPage = sysLogService.getLogList(username, optType, + startDate, endDate, page); + Map map = new HashMap<>(); + map.put("list", sysLogPage.getRecords()); + map.put("total", sysLogPage.getTotal()); + map.put("size", sysLogPage.getSize()); + map.put("current", sysLogPage.getCurrent()); + return ResponseResult.successData(map); + } + + /********************************** + * 用途说明: 导出日志数据 + * 参数说明 sysLogs 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + @Log(module = "系统日志", value = "导出系统日志到Excel") + @GetMapping("/exportExcel") + @Operation(summary = "导出日志数据") + public void exportExcel(String username, String optType, + String startDate, + String endDate, Page page, + HttpServletResponse response) throws IOException { + + Page sysLogPage = sysLogService.getLogList(username, optType, + startDate, endDate, page); + sysLogService.exportExcel(sysLogPage.getRecords(), response); + } +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysMenuController.java b/src/main/java/com/yfd/platform/system/controller/SysMenuController.java new file mode 100644 index 0000000..70a16a0 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysMenuController.java @@ -0,0 +1,308 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysMenu; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysMenuService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.apache.catalina.User; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + *

+ * 菜单及按钮 前端控制器 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@RestController +@RequestMapping("/system/menu") +@Tag(name = "菜单及按钮") +public class SysMenuController { + + @Resource + private ISysMenuService sysMenuService; + + @Resource + private IUserService userService; + + // 菜单图片路径通过服务层配置获取,无需在控制器注入 + + /*********************************** + * 用途说明:获取菜单结构树(含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @PostMapping("/getMenuButtonTree") + @Operation(summary = "获取菜单结构树(含按钮)") + @ResponseBody + public List> getMenuButtonTree(String systemcode, + String name, + String isdisplay) { + return sysMenuService.getMenuButtonTree(systemcode, name, isdisplay); + } + + /*********************************** + * 用途说明:获取菜单结构树(不含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @PostMapping("/getMenuTree") + @Operation(summary = "获取菜单结构树(不含按钮)") + @ResponseBody + public List> getMenuTree(String systemcode, + String name, + String isdisplay) { + return sysMenuService.getMenuTree(systemcode, name, isdisplay); + } + + /*********************************** + * 用途说明:权限分配 + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @PostMapping("/permissionAssignment") + @Operation(summary = "获取分配权限(不含按钮)") + @ResponseBody + public List> permissionAssignment(String roleId) { + + return sysMenuService.permissionAssignment(roleId); + } + + /********************************** + * 用途说明: 获取当前用户菜单结构树 + * 参数说明 + * 返回值说明: java.util.List + ***********************************/ + @GetMapping("/treeRoutes") + @Operation(summary = "获取当前用户菜单结构树") + @ResponseBody + public List> getMenuTreeByUser() { + SysUser userInfo = userService.getUserInfo(); + String id = ""; + if (0 != userInfo.getUsertype()) { + id = userInfo.getId(); + } + return sysMenuService.getMenuTree(id); + } + + /*********************************** + * 用途说明:根据id查询菜单或按钮详情 + * 参数说明 + * id 菜单或按钮表id + * 返回值说明: 菜单或按钮表对象 + ***********************************/ + @PostMapping("/getOneById") + @Operation(summary = "根据id查询菜单或按钮详情") + @ResponseBody + public ResponseResult getOneById(String id) { + SysMenu sysMenu = sysMenuService.getById(id); + return ResponseResult.successData(sysMenu); + } + + /*********************************** + * 用途说明:新增菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否添加成功提示 + ***********************************/ + @Log(module = "菜单及按钮", value = "新增菜单及按钮!") + @PostMapping("/addMenu") + @Operation(summary = "新增菜单及按钮") + @ResponseBody + public ResponseResult addMenu(@RequestBody SysMenu sysMenu) { + boolean isOk = sysMenuService.addMenu(sysMenu); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否修改成功提示 + ***********************************/ + @Log(module = "菜单及按钮", value = "修改菜单及按钮") + @PostMapping("/updateById") + @Operation(summary = "修改菜单及按钮") + @ResponseBody + public ResponseResult updateById(@RequestBody SysMenu sysMenu) { + sysMenu.setLastmodifier(userService.getUsername()); + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean isOk = sysMenuService.updateById(sysMenu); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除单个图标 + * 参数说明 + * id 删除图标id + * icon 图标名称 + * 返回值说明: 是否删除成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "根据id删除单个图标!") + @PostMapping("/deleteIcon") + @Operation(summary = "根据id删除单个图标") + @ResponseBody + public ResponseResult deleteIcon(@RequestParam String id) { + boolean ok = sysMenuService.deleteIcon(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:更新菜单及按钮是否有效 + * 参数说明 + * id 菜单及按钮表id + * isdisplay 是否有效字段 + * 返回值说明: 是否更新成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "更新菜单及按钮是否有效!") + @PostMapping("/setIsDisplay") + @Operation(summary = "更新菜单及按钮是否有效") + @ResponseBody + public ResponseResult setIsDisplay(String id, String isdisplay) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 修改是否显示 ,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("isdisplay", isdisplay).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", + new Timestamp(System.currentTimeMillis())); + boolean ok = sysMenuService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:菜单及按钮序号排序 + * 参数说明 + * parentid 上级id + * orderMap map<菜单及按钮表id,排列序号> + * 返回值说明: 是否更新成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "菜单及按钮序号排序!") + @PostMapping("/moveOrderno") + @Operation(summary = "菜单及按钮序号排序") + @ResponseBody + public ResponseResult moveOrderno(@RequestParam String parentid, + @RequestParam String id, + @RequestParam int orderno) { + boolean ok = sysMenuService.moveOrderno(parentid, id, orderno); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除菜单或按钮 + * 参数说明 + * id 删除列的id + * 返回值说明: 是否删除成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "根据id删除菜单或按钮!") + @PostMapping("/deleteById") + @Operation(summary = "根据id删除菜单或按钮") + @ResponseBody + public ResponseResult deleteById(@RequestParam String id) { + boolean ok = sysMenuService.deleteById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 菜单或者按钮拖动 + * 参数说明 id + * 参数说明 id1 + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + @Log(module = "菜单及按钮", value = "拖动修改菜单或按钮同级顺序!") + @PostMapping("/changeMenuOrder") + @Operation(summary = "菜单或按钮切换") + @ResponseBody + public ResponseResult changeMenuOrder(@RequestParam String fromId, + @RequestParam String toId) { + if (StrUtil.isBlank(fromId) || StrUtil.isBlank(toId)) { + return ResponseResult.error("参数为空!"); + } + if (fromId.equals(toId)) { + return ResponseResult.error("切换失败!"); + } + boolean ok = sysMenuService.changeOderNoById(fromId, toId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + @PostMapping("/uploadIcon") + @Operation(summary = "上传单个图标") + @ResponseBody + public ResponseResult uploadIcon(MultipartFile icon, String menuId) throws FileNotFoundException { + if (StrUtil.isNotBlank(menuId)) { + SysMenu sysMenu = sysMenuService.getById(menuId); + //图片路径 + String iconname = + System.getProperty("user.dir") + "\\src\\main" + + "\\resources\\static\\icon" + File.separator + sysMenu.getIcon(); + //删除图标 + new File(iconname).delete(); + } + String filename = sysMenuService.uploadIcon(icon); + SysMenu sysMenu = new SysMenu(); + sysMenu.setId(menuId); + sysMenu.setIcon(filename); + sysMenuService.updateById(sysMenu); + return ResponseResult.successData(filename); + } +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysOrganizationController.java b/src/main/java/com/yfd/platform/system/controller/SysOrganizationController.java new file mode 100644 index 0000000..9f51b71 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysOrganizationController.java @@ -0,0 +1,210 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysOrganizationService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + *

+ * 系统组织框架 前端控制器 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@RestController +@RequestMapping("/system/organization") +@Tag(name = "系统组织框架") +public class SysOrganizationController { + + @Resource + private ISysOrganizationService organizationService; + + @Resource + private IUserService userService; + + /*********************************** + * 用途说明:获取组织范围树结构 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + @PostMapping("/getOrgScopeTree") + @Operation(summary = "获取组织范围树结构") + @ResponseBody + public List> getOrgScopeTree(String roleId) { + return organizationService.getOrgScopeTree(roleId); + } + + /*********************************** + * 用途说明:获取组织范围 + * 参数说明 + * 返回值说明: 组织范围集合 + ***********************************/ + @PostMapping("/getOrgTree") + @Operation(summary = "获取组织结构树") + @ResponseBody + public List> getOrgTree(String parentid, + String params) { + return organizationService.getOrgTree(parentid, params); + } + + /*********************************** + * 用途说明:根据企业ID查询组织详情 + * 参数说明 + * id 企业id + * 返回值说明: 系统组织框架对象 + ***********************************/ + @PostMapping("/getOrganizationById") + @Operation(summary = "根据企业ID查询组织信息") + @ResponseBody + public ResponseResult getOrganizationById(String id, String orgName) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("查询失败!"); + } + List sysOrganizations = + organizationService.getOrganizationById(id, orgName); + return ResponseResult.successData(sysOrganizations); + } + + /*********************************** + * 用途说明:根据ID查询组织详情 + * 参数说明 + * id 系统组织id + * 返回值说明: 系统组织框架对象 + ***********************************/ + @PostMapping("/getOneById") + @Operation(summary = "根据ID查询组织详情") + @ResponseBody + public ResponseResult getOneById(String id) { + SysOrganization sysOrganization = organizationService.getById(id); + return ResponseResult.successData(sysOrganization); + } + + /*********************************** + * 用途说明:新增系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统组织框架", value = "新增企业或者部门!") + @PostMapping("/addOrg") + @Operation(summary = "新增系统组织框架") + @ResponseBody + public ResponseResult addOrg(@RequestBody SysOrganization sysOrganization) { + //判断是否是否填写 有效 否则默认为 1 + if (StrUtil.isEmpty(sysOrganization.getIsvaild())) { + sysOrganization.setIsvaild("1"); + } + //填写 当前用户名称 + sysOrganization.setLastmodifier(userService.getUsername()); + //填写 当前日期 + sysOrganization.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //新增 系统组织R + boolean isOk = organizationService.addOrg(sysOrganization); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否修改成功 + ***********************************/ + @Log(module = "系统组织框架", value = "修改企业或者部门信息!") + @PostMapping("/updateById") + @Operation(summary = "修改系统组织框架") + @ResponseBody + public ResponseResult updateById(@RequestBody SysOrganization sysOrganization) { + //填写 当前用户名称 + sysOrganization.setLastmodifier(userService.getUsername()); + //填写 当前日期 + sysOrganization.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //根据id 修改系统组织 + boolean isOk = organizationService.updateById(sysOrganization); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否修改成功 + ***********************************/ + @Log(module = "系统组织框架", value = "设置企业/部门是否有效!") + @PostMapping("/setIsValid") + @Operation(summary = "设置组织是否有效") + @ResponseBody + public ResponseResult setIsValid(@RequestParam String id, + @RequestParam String isvaild) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 修改是否有效,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("isvaild", isvaild).set("lastmodifier" + , userService.getUsername()).set("lastmodifydate", + new Timestamp(System.currentTimeMillis())); + boolean isOk = organizationService.update(updateWrapper); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除系统组织框架 + * 参数说明 + * id 系统组织框架id + * 返回值说明: 是否删除成功 + ***********************************/ + @Log(module = "系统组织框架", value = "根据ID删除企业或者部门!") + @PostMapping("/deleteById") + @Operation(summary = "根据id删除系统组织框架") + @ResponseBody + public ResponseResult deleteById(@RequestParam String id) { + String[] orgIds = id.split(","); + for (String orgId : orgIds) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + List list = + organizationService.list(queryWrapper.eq(SysOrganization::getParentid, orgId)); + List ids = + list.stream().map(SysOrganization::getId).collect(Collectors.toList()); + boolean isOk = organizationService.removeById(orgId); + if (!isOk) { + continue; + } + for (String oid : ids) { + organizationService.removeById(oid); + } + } + return ResponseResult.success(); + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/SysRoleController.java b/src/main/java/com/yfd/platform/system/controller/SysRoleController.java new file mode 100644 index 0000000..ea2fa0f --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/SysRoleController.java @@ -0,0 +1,324 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.service.ISysRoleService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统角色 前端控制器 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@RestController +@RequestMapping("/system/role") +@Tag(name = "系统角色") +public class SysRoleController { + + @Resource + private ISysRoleService roleService; + + @Resource + private IUserService userService; + + /*********************************** + * 用途说明:查询所有角色 + * 参数说明 + * roleName 角色名称 + * 返回值说明: 查询都有角色 + ***********************************/ + @PostMapping("/list") + @Operation(summary = "查询所有角色") + @ResponseBody + public List list(String rolename) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotEmpty(rolename)) { + //根据角色名称模糊查询 + queryWrapper.like("rolename", rolename); + } + //根据角色级别,角色编号 正序排序 + queryWrapper.ne("level", "1").orderByAsc("level", "lastmodifydate"); + return roleService.list(queryWrapper); + } + + /*********************************** + * 用途说明:根据Id获取当个角色 + * 参数说明 + * id 角色表id + * 返回值说明: 根据id查询到角色详情 + ***********************************/ + @PostMapping("/getOneById") + @Operation(summary = "根据Id获取当个角色") + @ResponseBody + public ResponseResult getOneById(String id) { + SysRole sysRole = roleService.getById(id); + return ResponseResult.successData(sysRole); + } + + /*********************************** + * 用途说明:新增角色 + * 参数说明 + * sysRole 新增角色信息 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "新增角色") + @PostMapping("/addRole") + @Operation(summary = "新增角色") + @ResponseBody + public ResponseResult addRole(@RequestBody SysRole sysRole) { + boolean isOk = roleService.addRole(sysRole); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:分配操作权限 + * 参数说明 + * id 角色id + * optscope 分配的权限 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "分配操作权限") + @PostMapping("/setOptScope") + @Operation(summary = "分配操作权限") + @ResponseBody + public ResponseResult setOptScope(@RequestParam String id, + @RequestParam String optscope) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新权限,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("optscope", optscope).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:角色菜单权限 + * 参数说明 + * id 角色id + * menuIds 权限id字符串 + * 返回值说明: 是否分配成功 + ***********************************/ + @Log(module = "系统角色", value = "角色菜单权限") + @PostMapping("/setMenuById") + @Operation(summary = "角色菜单权限") + @ResponseBody + public ResponseResult setMenuById(String id, String menuIds) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + if (StrUtil.isBlank(menuIds)) { + return ResponseResult.success(); + } + boolean ok = roleService.setMenuById(id, menuIds); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + + /*********************************** + * 用途说明:设置组织范围 + * 参数说明 + * id 角色id + * orgscope 组织范围 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "设置组织范围") + @PostMapping("/setOrgscope") + @Operation(summary = "设置组织范围") + @ResponseBody + public ResponseResult setOrgscope(@RequestParam String id, + @RequestParam String orgscope) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新组织范围,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("orgscope", orgscope).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:设置业务范围 + * 参数说明 + * id 角色id + * busscope 业务范围 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "设置业务范围") + @PostMapping("/setBusscope") + @Operation(summary = "设置业务范围") + @ResponseBody + public ResponseResult setBusscope(@RequestParam String id, + @RequestParam String busscope) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新业务范围,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("busscope", busscope).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:角色添加用户 + * 参数说明 + * roleid 角色id + * userids 用户id组 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "角色添加用户") + @PostMapping("/setRoleUsers") + @Operation(summary = "角色添加用户") + @ResponseBody + public ResponseResult setRoleUsers(String roleid, String userids) { + boolean isOk = true; + String[] temp = userids.split(","); + for (String userid : temp) { + isOk = isOk && userService.addUserRoles(roleid, userid); + } + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:删除角色用户 + * 参数说明 + * roleid 角色id + * 返回值说明: 是否新增成功 + ***********************************/ + @PostMapping("/deleteRoleUser") + @Operation(summary = "删除角色用户") + @ResponseBody + public ResponseResult deleteRoleUsers(@RequestParam String roleid, + @RequestParam String userids) { + //根据角色id、用户id删除 + boolean ok = roleService.deleteRoleUsers(roleid, userids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:设置角色是否有效 + * 参数说明 + * id 角色id + *isvaild 是否有效(1 是 0 否 ) + * 返回值说明: 是否新增成功 + ***********************************/ + @PostMapping("/setIsvaild") + @Operation(summary = "设置角色是否有效") + @ResponseBody + public ResponseResult setIsvaild(String id, String isvaild) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新业务范围,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("isvaild", isvaild).set("lastmodifier" + , userService.getUsername()).set("lastmodifydate", + LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:更新角色信息 + * 参数说明 + *sysRole 角色对象 + * 返回值说明: 是否修改成功 + ***********************************/ + @PostMapping("/updateById") + @Operation(summary = "更新角色信息") + @ResponseBody + public ResponseResult updateById(@RequestBody SysRole sysRole) { + //更新最近修改人 + sysRole.setLastmodifier(userService.getUsername()); + //更新最近修改时间 + sysRole.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //根据id更新角色信息 + boolean ok = roleService.updateById(sysRole); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除角色 + * 参数说明 + *id 角色id + * 返回值说明: 是否删除成功 + ***********************************/ + @PostMapping("/deleteById") + @Operation(summary = "根据id删除角色") + @ResponseBody + public ResponseResult deleteById(@RequestParam String id) { + roleService.deleteById(id); + return ResponseResult.success(); + } + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + @PostMapping("/listRoleUsers") + @Operation(summary = "查询已分配的用户") + @ResponseBody + public List listRoleUsers(String orgid, String username, + String status, String level, + String rolename, String isvaild) { + return roleService.listRoleUsers(orgid, username, status, level, + rolename, isvaild); + } + +} diff --git a/src/main/java/com/yfd/platform/system/controller/UserController.java b/src/main/java/com/yfd/platform/system/controller/UserController.java new file mode 100644 index 0000000..c90e345 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/controller/UserController.java @@ -0,0 +1,188 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.datasource.DataSource; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.IUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.util.Map; + +/** + *

+ * 用户信息 前端控制器 + *

+ * + * @author zhengsl + * @since 2022-09-20 + */ +@RestController +@RequestMapping("/system/user") +@Tag(name = "系统用户") +public class UserController { + + @Resource + private IUserService userService; + + @Log(module = "系统用户", value = "新增系统用户") + @PostMapping("/addUser") + @Operation(summary = "新增系统用户") + @ResponseBody + public ResponseResult addUser(@RequestBody SysUser user, String roleids) { + Map reslut = userService.addUser(user, roleids); + return ResponseResult.successData(reslut); + } + + @Log(module = "系统用户", value = "修改用户信息") + @PostMapping("/updateUser") + @Operation(summary = "修改用户信息") + @ResponseBody + public ResponseResult updateUser(@RequestBody SysUser user, + String roleids) { + if (StrUtil.isEmpty(user.getId())) { + return ResponseResult.error("没有用户ID"); + } + //填写 当前用户名称 + user.setLastmodifier(userService.getUsername()); + //填写 当前日期 + user.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + Map reslut = userService.updateById(user, roleids); + return ResponseResult.successData(reslut); + } + + @GetMapping("/queryUsers") + @Operation(summary = "查询用户信息") + @ResponseBody + public ResponseResult queryUsers(String orgid, + String username, Page page) { + + Page> mapPage = userService.queryUsers(orgid, + username, page); + return ResponseResult.successData(mapPage); + } + + /*********************************** + * 用途说明:用户分配角色 + * 参数说明 + *idMap 用户id与角色id + * 返回值说明: 判断是否添加成功 + ************************************/ + @Log(module = "系统用户", value = "用户分配角色") + @PostMapping("/setUserRoles") + @Operation(summary = "用户分配角色") + @ResponseBody + public ResponseResult setUserRoles(String roleid, String userids) { + boolean ok = userService.setUserRoles(roleid, userids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除用户 + * 参数说明 + *id 用户id + * 返回值说明: 判断是否删除成功 + ************************************/ + @Log(module = "系统用户", value = "根据ID删除用户") + @PostMapping("/deleteById") + @Operation(summary = "根据ID删除用户") + @ResponseBody + public ResponseResult deleteById(String id) { + userService.deleteById(id); + return ResponseResult.success(); + } + + /*********************************** + * 用途说明:根据ID批量删除用户 + * 参数说明 + *ids 用户id集合 + * 返回值说明: 判断是否删除成功 + ************************************/ + @Log(module = "系统用户", value = "根据ID批量删除用户") + @PostMapping("/deleteUserByIds") + @Operation(summary = "根据ID批量删除用户") + @ResponseBody + public ResponseResult deleteUserByIds(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = userService.deleteUserByIds(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:重置用户密码(管理员) + * 参数说明 + *id 重置密码的 用户id + * 返回值说明: 判断是否重置成功 + ************************************/ + @Log(module = "系统用户", value = "重置用户密码") + @PostMapping("/resetPassword") + @Operation(summary = "重置用户密码") + @ResponseBody + @DataSource + public ResponseResult resetPassword(String id) throws Exception { + if (StrUtil.isBlank(id)) { + ResponseResult.error("参数为空"); + } + boolean ok = userService.resetPassword(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:设置账号状态(管理员) + * 参数说明 + *id 用户id + * status 设置状态 + * 返回值说明: 判断是否设置成功 + ************************************/ + @Log(module = "系统用户", value = "设置账号状态") + @PostMapping("/setStatus") + @Operation(summary = "设置账号状态") + @ResponseBody + public ResponseResult setStatus(@RequestParam String id, + @RequestParam String status) { + boolean ok = userService.setStatus(id, status); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改头像(管理员) + * 参数说明 + * multipartFile 文件对象 + * status 设置状态 + * 返回值说明: 文件名 + ************************************/ + @Operation(summary = "修改头像") + @PostMapping(value = "/updateAvatar") + public ResponseResult updateAvatar(String id, MultipartFile multipartFile) { + if (multipartFile == null) { + ResponseResult.error("参数为空"); + } + boolean ok = userService.uploadAvatar(id, multipartFile); + return ResponseResult.success(); + } +} diff --git a/src/main/java/com/yfd/platform/system/domain/Dictionary.java b/src/main/java/com/yfd/platform/system/domain/Dictionary.java new file mode 100644 index 0000000..df234e3 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/Dictionary.java @@ -0,0 +1,78 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 数据字典表 + *

+ * + * @author zhengsl + * @since 2021-10-27 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("rca_dictionary") +public class Dictionary implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 类型 + */ + private String type; + + /** + * 类型名称 + */ + private String typename; + + /** + * 代码 + */ + private String code; + + /** + * 名称 + */ + private String name; + + /** + * 顺序号 + */ + private String orderno; + + /** + * 上级代码 + */ + private String parentcode; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/src/main/java/com/yfd/platform/system/domain/LoginUser.java b/src/main/java/com/yfd/platform/system/domain/LoginUser.java new file mode 100644 index 0000000..44cfb35 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/LoginUser.java @@ -0,0 +1,76 @@ +package com.yfd.platform.system.domain; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LoginUser implements UserDetails { + + private SysUser user; + + private List permissions; + + public LoginUser(SysUser user, List permissions) { + this.user = user; + this.permissions = permissions; + } + + @JSONField(serialize = false) + private List authorities; + + @Override + public Collection getAuthorities() { + // 将权限信息放入集合 + authorities = permissions.stream() + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + return authorities; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + //获取用户昵称 + public String geNickname() { + return user.getNickname(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/src/main/java/com/yfd/platform/system/domain/Message.java b/src/main/java/com/yfd/platform/system/domain/Message.java new file mode 100644 index 0000000..54578e5 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/Message.java @@ -0,0 +1,117 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 消息通知 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_message") +public class Message implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(type = IdType.ASSIGN_UUID) + @Schema(description = "ID") + private String id; + + /** + * 创建时间:排序 + */ + @Schema(description = "创建时间:排序") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp createtime; + + /** + * 消息类型:1-定时任务 2-工作流触发 3-人工触发 + */ + @Schema(description = "消息类型:1-定时任务 2-工作流触发 3-人工触发") + private String type; + + /** + * 消息标题 + */ + @Schema(description = "消息标题") + private String title; + + /** + * 消息内容 + */ + @Schema(description = "消息内容") + private String content; + + /** + * 发送者名称,定时器,人员 + */ + @Schema(description = "发送者名称,定时器,人员") + private String senderName; + + /** + * 接收者代码 人员账号列表 + */ + @Schema(description = "接收者代码 人员账号列表 ") + private String receiverCodes; + + /** + * 接收者名称:为空 即为所有人,人员名称列表 + */ + @Schema(description = "接收者名称:为空 即为所有人,人员名称列表") + private String receiverNames; + + /** + * 状态:1、初始创建 2-消息已阅 9-消息过期 + */ + @Schema(description = "状态:1、初始创建 2-消息已阅 9-消息过期") + private String status; + + /** + * 有效期:小时 + */ + @Schema(description = "有效期:小时") + private Integer validperiod; + + /** + * 已阅时间 + */ + @Schema(description = "已阅时间") + private Timestamp readtime; + + /** + * 备用1 + */ + @Schema(description = "备用1") + private String custom1; + + /** + * 备用2 + */ + @Schema(description = "备用2") + private String custom2; + + /** + * 备用3 + */ + @Schema(description = "备用3") + private String custom3; + +} diff --git a/src/main/java/com/yfd/platform/system/domain/QuartzJob.java b/src/main/java/com/yfd/platform/system/domain/QuartzJob.java new file mode 100644 index 0000000..e3504ce --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/QuartzJob.java @@ -0,0 +1,118 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 定时任务 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_quartz_job") +public class QuartzJob implements Serializable { + + public static final String JOB_KEY = "JOB_KEY"; + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID") + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 排序号 + */ + @Schema(description = "排序号") + private Integer orderno; + + /** + * 任务名称 + */ + @Schema(description = "任务名称") + private String jobName; + + /** + * 执行类名称 + */ + @Schema(description = "执行类名称") + private String jobClass; + + /** + * 执行方法名称 + */ + @Schema(description = "执行方法名称") + private String jobMethod; + + /** + * 时间周期表达式 + */ + @Schema(description = "时间周期表达式") + private String jobCron; + + /** + * 方法参数 + */ + @Schema(description = "方法参数") + private String jobParams; + + /** + * 任务描述 + */ + @Schema(description = "任务描述") + private String description; + + /** + * 状态:0-暂停、1-启用 + */ + @Schema(description = "状态:0-暂停、1-启用") + private String status; + + /** + * 最近修改者 + */ + @Schema(description = "最近修改者") + private String lastmodifier; + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Schema(description = "最近修改日期") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + @Schema(description = "备用1") + private String custom1; + + /** + * 备用2 + */ + @Schema(description = "备用2") + private String custom2; + + /** + * 备用3 + */ + @Schema(description = "备用3") + private String custom3; + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysConfig.java b/src/main/java/com/yfd/platform/system/domain/SysConfig.java new file mode 100644 index 0000000..3bef030 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysConfig.java @@ -0,0 +1,78 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统全局配置 + *

+ * + * @author zhengsl + * @since 2022-01-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_config") +public class SysConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 欢迎词 + */ + private String welcome; + + /** + * 系统功能介绍 + */ + private String funcation; + + /** + * 系统版本信息 + */ + private String versioninfo; + + /** + * 备注 + */ + private String remark; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @TableField(fill = FieldFill.UPDATE) + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysDictionary.java b/src/main/java/com/yfd/platform/system/domain/SysDictionary.java new file mode 100644 index 0000000..ca1fb9c --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysDictionary.java @@ -0,0 +1,71 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 数据字典表 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysDictionary implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 字典类型 00-系统内置 01-用户配置 + */ + @TableField("dicttype") + private String dictType; + + /** + * 顺序号 + */ + @TableField("orderno") + private Integer orderNo; + + /** + * 字典编码 + */ + @TableField("dictcode") + private String dictCode; + + /** + * 字典名称 + */ + @TableField("dictname") + private String dictName; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysDictionaryItems.java b/src/main/java/com/yfd/platform/system/domain/SysDictionaryItems.java new file mode 100644 index 0000000..431bfda --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysDictionaryItems.java @@ -0,0 +1,77 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 数据字典明细 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysDictionaryItems implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 对应字典ID + */ + @TableField("dictid") + private String dictId; + + /** + * 顺序号 + */ + @TableField("orderno") + private Integer orderNo; + + /** + * 项编码 + */ + @TableField("itemcode") + private String itemCode; + + /** + * 项名称 + */ + @TableField("dictname") + private String dictName; + + /** + * 父项编码 + */ + @TableField("parentcode") + private String parentCode; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysLog.java b/src/main/java/com/yfd/platform/system/domain/SysLog.java new file mode 100644 index 0000000..dfe091d --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysLog.java @@ -0,0 +1,92 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + *

+ * 系统操作日志 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class SysLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户账号 + */ + @TableField("usercode") + private String usercode; + + /** + * 用户名称 + */ + private String username; + + /** + * 操作类型 00-登录 01-新增 02-修改 03-删除 06-查询 09其他 + */ + @TableField("opttype") + private String opttype; + + /** + * 模块名称 + */ + private String module; + + /** + * 日志描述 + */ + private String description; + + /** + * 操作方法 + */ + private String method; + + /** + * 方法参数 + */ + private String params; + + /** + * 创建时间 + */ + @TableField("logtime") + private Timestamp logtime; + + /** + * 请求IP + */ + @TableField("requestip") + private String requestip; + + /** + * 浏览器类型 + */ + private String browser; + + public SysLog(String opttype) { + this.opttype = opttype; + } +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysMenu.java b/src/main/java/com/yfd/platform/system/domain/SysMenu.java new file mode 100644 index 0000000..18802d7 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysMenu.java @@ -0,0 +1,114 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * 菜单及按钮 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_menu") +public class SysMenu implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 1-web 2-pad 3-mobile + */ + private String systemcode; + + /** + * 1-菜单 2-按钮 + */ + private String type; + + /** + * 在系统内自动生成 + */ + private String code; + + /** + * 名称 + */ + private String name; + + /** + * 图标地址 + */ + private String icon; + + /** + * 是否外链 + */ + private String islink; + + /** + * 内部模块路径或者外链地址 + */ + private String opturl; + + /** + * 权限控制标识 + */ + private String permission; + + /** + * 顶级为0 + */ + private String parentid; + + /** + * 排序号 + */ + private Integer orderno; + + /** + * 0-不显示 1-显示 + */ + private String isdisplay; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @TableField(fill = FieldFill.UPDATE) + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysOrganization.java b/src/main/java/com/yfd/platform/system/domain/SysOrganization.java new file mode 100644 index 0000000..cc7f711 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysOrganization.java @@ -0,0 +1,93 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统组织框架 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_organization") +public class SysOrganization implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 1-公司 -2-部门 + */ + private String orgtype; + + /** + * 两位一级 + */ + private String orgcode; + + /** + * 组织名称 + */ + private String orgname; + + /** + * 上级id + */ + private String parentid; + + /** + * 组织负责人 + */ + private String manager; + + /** + * 1-是 0-否 + */ + private String isvaild; + + /** + * 描述 + */ + private String description; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @TableField(fill = FieldFill.UPDATE) + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysRole.java b/src/main/java/com/yfd/platform/system/domain/SysRole.java new file mode 100644 index 0000000..1b21e76 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysRole.java @@ -0,0 +1,98 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统角色 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_role") +public class SysRole implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 系统生成,三位编号 + */ + private String rolecode; + + /** + * 角色名称 + */ + private String rolename; + + /** + * 1-超级管理员 2-单位管理员 3-普通用户 + */ + private String level; + + /** + * 描述 + */ + private String description; + + /** + * org1,org2 + */ + private String orgscope; + + /** + * 多个操作代码(菜单、按钮) + */ + private String optscope; + + /** + * json格式自定义业务范围 + */ + private String busscope; + + /** + * 1-是 0-否 + */ + private String isvaild; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @TableField(fill = FieldFill.UPDATE) + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/src/main/java/com/yfd/platform/system/domain/SysUser.java b/src/main/java/com/yfd/platform/system/domain/SysUser.java new file mode 100644 index 0000000..0e01f4a --- /dev/null +++ b/src/main/java/com/yfd/platform/system/domain/SysUser.java @@ -0,0 +1,116 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统用户 + *

+ * + * @author zhengsl + * @since 2021-10-27 + */ + +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_user") +public class SysUser implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 用户类型 0-管理员 1-普通用户 + */ + private Integer usertype; + + /** + * 用户名(账号) + */ + private String username; + /** + * 用户昵称 + */ + private String nickname; + + /** + * 登录密码(加密存储) + */ + private String password; + + /** + * 性别(0-男 1-女 ) + */ + private String sex; + + /** + * 邮箱 + */ + private String email; + /** + * 手机号 + */ + private String phone; + + /** + * 头像(预留) + */ + private String avatar; + + /** + * 账号状态(1-正常 0-停用) + */ + private Integer status; + + /** + * 部门ID + */ + private String orgid; + + /** + * 密码重置时间 + */ + private String pwdresettime; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @TableField(fill = FieldFill.UPDATE) + private Timestamp lastmodifydate; + + @TableField(exist = false) + private String uuid; + + @TableField(exist = false) + private String code; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; +} diff --git a/src/main/java/com/yfd/platform/system/mapper/MessageMapper.java b/src/main/java/com/yfd/platform/system/mapper/MessageMapper.java new file mode 100644 index 0000000..d08fa95 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/MessageMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.yfd.platform.system.domain.Message; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 消息通知 Mapper 接口 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +public interface MessageMapper extends BaseMapper { + +} diff --git a/src/main/java/com/yfd/platform/system/mapper/QuartzJobMapper.java b/src/main/java/com/yfd/platform/system/mapper/QuartzJobMapper.java new file mode 100644 index 0000000..ad46f1b --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/QuartzJobMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.yfd.platform.system.domain.QuartzJob; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 定时任务 Mapper 接口 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +public interface QuartzJobMapper extends BaseMapper { + +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysConfigMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..5bae048 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysConfigMapper.java @@ -0,0 +1,17 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysConfig; + + +/** + *

+ * 系统全局配置 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2022-01-19 + */ +public interface SysConfigMapper extends BaseMapper { + +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysDictionaryItemsMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysDictionaryItemsMapper.java new file mode 100644 index 0000000..de7deaa --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysDictionaryItemsMapper.java @@ -0,0 +1,17 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.system.domain.SysDictionaryItems; + +/** + *

+ * 数据字典明细 Mapper 接口 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +public interface SysDictionaryItemsMapper extends BaseMapper { + +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysDictionaryMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysDictionaryMapper.java new file mode 100644 index 0000000..7d03ca9 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysDictionaryMapper.java @@ -0,0 +1,22 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysDictionary; + +/** + *

+ * 数据字典表 Mapper 接口 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +public interface SysDictionaryMapper extends BaseMapper { + + /********************************** + * 用途说明: 根据字典类型获取字典最大序号 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: 返回增加成功或者失败 + ***********************************/ + Integer selectMaxNo(String dictType); +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysLogMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysLogMapper.java new file mode 100644 index 0000000..7b18518 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysLogMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysLog; + +/** + *

+ * 系统操作日志 Mapper 接口 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +public interface SysLogMapper extends BaseMapper { + +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysMenuMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..f57b35e --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysMenuMapper.java @@ -0,0 +1,59 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 菜单及按钮 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +public interface SysMenuMapper extends BaseMapper { + + /*********************************** + * 用途说明:菜单及按钮序号向上移动 + * 参数说明 + * parentid 上级id + *Orderno 小于序号(原序号) + *upOrderno 大于等于序号(更改的序号加一) + * 返回值说明: 是否更新成功 + ***********************************/ + boolean upMoveOrderno(@Param("parentid") String parentid, @Param("Orderno") int Orderno, @Param("upOrderno") int upOrderno); + + /*********************************** + * 用途说明:菜单及按钮序号向下移动 + * 参数说明 + * parentid 上级id + *Orderno 大于序号(原序号) + *downOrderno 小于等于序号(更改的序号减一) + * 返回值说明: 是否更新成功 + ***********************************/ + boolean downMoveOrderno(@Param("parentid") String parentid, @Param("Orderno") int Orderno, @Param("downOrderno") int downOrderno); + + + List selectPermsByUserId(String userId); + + //List selectMenuByUserId(String userId); + List> selectMenuByUserId(String userId); + + /*********************************** + * 用途说明:根据权限id查找系统类型 + * 参数说明 id 权限id + * 返回值说明: 返回系统类型 + ***********************************/ + String getSystemCodeById(String id); + + /*********************************** + * 用途说明:根据角色Id查找权限 + * 参数说明 id 权限id + * 返回值说明: 返回权限集合 + ***********************************/ + List selectMenuByRoleId(String id); +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysOrganizationMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysOrganizationMapper.java new file mode 100644 index 0000000..e856aae --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysOrganizationMapper.java @@ -0,0 +1,33 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysOrganization; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 系统组织框架 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +public interface SysOrganizationMapper extends BaseMapper { + + /*********************************** + * 用途说明:去重查询组织分类 + * 返回值说明: 所有组织分类 + ***********************************/ + List queryOrgtype(); + + /*********************************** + * 用途说明:根据组织分类查询上级id + * 参数说明 + * orgtype 组织分类 + * 返回值说明: 上级id + ***********************************/ + List queryParentid(@Param("orgtype") String orgtype); + +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysRoleMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..acb7a81 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysRoleMapper.java @@ -0,0 +1,89 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统角色 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +public interface SysRoleMapper extends BaseMapper { + + /*********************************** + * 用途说明:根据角色id查询是否存在用户 + * 参数说明 + * roleid 角色id + * 返回值说明: 该角色下是否存在用户 + ************************************/ + List> isRoleUsersByroleid(String roleid); + + /*********************************** + * 用途说明:根据角色id查询是否存在权限 + * 参数说明 + * roleid 角色id + * 返回值说明: 该角色下是否存在权限 + ************************************/ + List> isRoleMenuByRoleId(String roleId); + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 '1-超级管理员 2-单位管理员 3-普通用户' + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + List listRoleUsers(String orgid, String username, String status, + String level, String rolename, String isvaild); + + /*********************************** + * 用途说明:根据 角色id和用户id 删除 (admin除外) + * 参数说明 + *roleid 角色id + * urserid 用户id + * 返回值说明: 是否删除成功 + ***********************************/ + boolean deleteRoleUsers(String roleid, String urserid); + + /********************************** + * 用途说明: 根据用户id获取角色信息 + * 参数说明 id 角色id + * 返回值说明: void + ***********************************/ + List getRoleByUserId(String id); + + /********************************** + * 用途说明: 根据角色ID删除菜单与角色关联信息 + * 参数说明 id 角色id + * 返回值说明: void + ***********************************/ + boolean deleteRoleMenus(String id); + + /********************************** + * 用途说明: 根据角色ID删除用户与角色关联信息 + * 参数说明 id 角色id + * 返回值说明: void + ***********************************/ + boolean deleteRoleUser(String id); + + /********************************** + * 用途说明: 根据角色id获取用户id + * 参数说明 id 角色id + * 返回值说明: 用户id + ***********************************/ + List getUserIdById(String id); + + void addRoleMenu(@Param("id") String id, @Param("roleid") String roleid, + @Param("menuid") String menuid); +} diff --git a/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java b/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..ea57c78 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java @@ -0,0 +1,96 @@ +package com.yfd.platform.system.mapper; + + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.system.domain.SysUser; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统用户表 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2021-10-27 + */ +public interface SysUserMapper extends BaseMapper { + List list(@Param("total")String total, @Param("size")String size, @Param("orgid")String orgid, @Param("username")String username, @Param("mobile")String mobile , @Param("status")String status); + + /*********************************** + * 用途说明:新增系统角色用户对照表 对用户分配角色 + * 参数说明 + * id 生成的id + * roleid 角色id + * userid 用户id + * 返回值说明: + ************************************/ + boolean addUserRoles(@Param("id")String id,@Param("roleid") String roleid,@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户id 和角色id 查询 系统角色用户对照表 + * 参数说明 + * userid 用户id + * roleid 角色id + * 返回值说明: + ************************************/ + List getRoleUsersByid(@Param("roleid") String roleid,@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户表id查询角色表所有角色 + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + List getLevel(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户表id查询角色表所有角色id + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + List getRoleid(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户表id查询角色表级别 + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + String getMaxLevel(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户id删除所分配的角色 + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + boolean delRoleUsersByUserid(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户id删除所分配的不包含角色 + * 参数说明 + * userid 用户id + * roleids 多个角色id + * 返回值说明: + ************************************/ + boolean delInRoleUsersByUserid(@Param("userid") String userid,@Param("roleids")String[] roleids); + + Page> queryUsers(String orgid, + String username, + Page page); + + Map getOrganizationByid(String id); + + /********************************** + * 用途说明: 根据ID删除用户与角色的关联信息 + * 参数说明 ids 用户id集合 + * 返回值说明: void + ***********************************/ + void delRoleUsersByUserIds(List ids); +} diff --git a/src/main/java/com/yfd/platform/system/service/IMessageService.java b/src/main/java/com/yfd/platform/system/service/IMessageService.java new file mode 100644 index 0000000..2b4c645 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/IMessageService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.service; + +import com.yfd.platform.system.domain.Message; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 消息通知 服务类 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +public interface IMessageService extends IService { + +} diff --git a/src/main/java/com/yfd/platform/system/service/IQuartzJobService.java b/src/main/java/com/yfd/platform/system/service/IQuartzJobService.java new file mode 100644 index 0000000..8b71868 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/IQuartzJobService.java @@ -0,0 +1,43 @@ +package com.yfd.platform.system.service; + +import com.yfd.platform.system.domain.QuartzJob; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 定时任务 服务类 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +public interface IQuartzJobService extends IService { + + /********************************** + * 用途说明: 新增定时任务 + * 参数说明 quartzJob 定时对象 + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean addQuartzJob(QuartzJob quartzJob); + + /********************************** + * 用途说明: 删除定时任务 + * 参数说明 id id + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean deleteQuartzJob(String id); + + /********************************** + * 用途说明: 拖动修改定时任务顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + boolean changeDictOrder(String fromID, String toID); + + /********************************** + * 用途说明: 执行定时任务 + * 参数说明 id id + * 返回值说明: void + ***********************************/ + void execution(QuartzJob byId); +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysConfigService.java b/src/main/java/com/yfd/platform/system/service/ISysConfigService.java new file mode 100644 index 0000000..ef3a399 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysConfigService.java @@ -0,0 +1,22 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysConfig; + +import javax.sound.sampled.UnsupportedAudioFileException; +import java.io.IOException; +import java.util.Map; + +/** + *

+ * 系统全局配置 服务类 + *

+ * + * @author zhengsl + * @since 2022-01-19 + */ +public interface ISysConfigService extends IService { + + + +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysDictionaryItemsService.java b/src/main/java/com/yfd/platform/system/service/ISysDictionaryItemsService.java new file mode 100644 index 0000000..32f9b8f --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysDictionaryItemsService.java @@ -0,0 +1,47 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysDictionaryItems; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + *

+ * 数据字典明细 服务类 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +public interface ISysDictionaryItemsService extends IService { + + /********************************** + * 用途说明: 分页查询字典项信息 + * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + Page getDictItemPage(String dictId, String itemName, Page page); + + /********************************** + * 用途说明: 增加字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + boolean addDictionaryItem(SysDictionaryItems sysDictionaryItems); + + /********************************** + * 用途说明: 拖动修改字典项顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + boolean changeItemOrder(String fromID, String toID); + + /********************************** + * 用途说明: 导出数据字典项数据 + * 参数说明 sysDictionaryItemsList 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或失败 + ***********************************/ + void exportExcel(List records, HttpServletResponse response); +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysDictionaryService.java b/src/main/java/com/yfd/platform/system/service/ISysDictionaryService.java new file mode 100644 index 0000000..f3a4990 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysDictionaryService.java @@ -0,0 +1,45 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysDictionary; + +import java.util.List; + +/** + *

+ * 数据字典表 服务类 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +public interface ISysDictionaryService extends IService { + + /********************************** + * 用途说明: 获取数据字典列表 + * 参数说明 dictType 字典类型 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + List getDictList(String dictType); + + /********************************** + * 用途说明: 新增字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + boolean addDict(SysDictionary sysDictionary); + + /********************************** + * 用途说明: 根据ID删除字典 + * 参数说明 id 字典ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除结果成功或者失败 + ***********************************/ + boolean deleteDictById(String id); + + /********************************** + * 用途说明: 拖动修改字典顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + boolean changeDictOrder(String fromID, String toID); +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysLogService.java b/src/main/java/com/yfd/platform/system/service/ISysLogService.java new file mode 100644 index 0000000..85fdffa --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysLogService.java @@ -0,0 +1,52 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysLog; +import org.aspectj.lang.ProceedingJoinPoint; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统操作日志 服务类 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +public interface ISysLogService extends IService { + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 pageNum(页码数)、pageSize(页大小,如果是固定页大小可不传)、username(用户名)、(optType) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + Page getLogList(String username, String optType, + String startDate, + String endDate, Page page); + + + /********************************** + * 用途说明: 导出日志数据 + * 参数说明 sysLogs 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + void exportExcel(List sysLogs, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 新增日志 + * 参数说明 nickname 用户名 + * 参数说明 username 用户账号 + * 参数说明 browser 浏览器 + * 参数说明 ip 本机Ip地址 + * 参数说明 joinPoint 连接点 + * 参数说明 log 日志信息 + * 返回值说明: void + ***********************************/ + void save(String nickname,String username, String browser, String ip, ProceedingJoinPoint joinPoint, SysLog log); +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysMenuService.java b/src/main/java/com/yfd/platform/system/service/ISysMenuService.java new file mode 100644 index 0000000..7093e3b --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysMenuService.java @@ -0,0 +1,101 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; + +import com.yfd.platform.system.domain.SysMenu; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileNotFoundException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 菜单及按钮 服务类 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +public interface ISysMenuService extends IService { + + /*********************************** + * 用途说明:获取菜单结构树(含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + List> getMenuButtonTree(String systemcode, String name, String isdisplay); + + /*********************************** + * 用途说明:获取菜单结构树(不含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + List> getMenuTree(String systemcode, String name, String isdisplay); + + + /*********************************** + * 用途说明:新增菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否添加成功提示 + ***********************************/ + boolean addMenu(SysMenu sysMenu); + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * id 上传图标id + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + boolean uploadIcon(String id, MultipartFile icon); + + /*********************************** + * 用途说明:根据id删除单个图标 + * 参数说明 + * id 删除图标id + * icon 图标名称 + * 返回值说明: 是否删除成功 + ***********************************/ + boolean deleteIcon(String id); + + /*********************************** + * 用途说明:菜单及按钮序号排序 + * 参数说明 + * parentid 上级id + * orderMap map<菜单及按钮表id,排列序号> + * 返回值说明: 是否更新成功 + ***********************************/ + boolean moveOrderno(String parentid, String id, int orderno); + + /*********************************** + * 用途说明:根据id删除菜单或按钮 + * 参数说明 + * id 删除列的id + * 返回值说明: 是否删除成功 + ***********************************/ + boolean deleteById(String id); + + boolean changeOderNoById(String fromId, String toId); + + List> getMenuTree(String id); + + /*********************************** + * 用途说明:权限分配 + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + List> permissionAssignment(String roleId); + + String uploadIcon(MultipartFile icon) throws FileNotFoundException; +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysOrganizationService.java b/src/main/java/com/yfd/platform/system/service/ISysOrganizationService.java new file mode 100644 index 0000000..5b03119 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysOrganizationService.java @@ -0,0 +1,60 @@ +package com.yfd.platform.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysOrganization; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统组织框架 服务类 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +public interface ISysOrganizationService extends IService { + + /*********************************** + * 用途说明:获取组织结构树 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + List> getOrgTree(String parentid, String params); + + /*********************************** + * 用途说明:新增系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否新增成功 + ***********************************/ + boolean addOrg(SysOrganization sysOrganization); + + /*********************************** + * 用途说明:根据企业ID查询组织详情 + * 参数说明 + * id 企业id + * 返回值说明: 系统组织框架对象 + ***********************************/ + List getOrganizationById(String id,String orgName); + + /*********************************** + * 用途说明:获取组织范围树结构 + * 参数说明 + *roleId 角色id + * 返回值说明: 组织树集合 + ***********************************/ + List> getOrgScopeTree(String roleId); + + /********************************** + * 用途说明: 修改角色组织范围 + * 参数说明 roleId 角色id + * 参数说明 orgscope 组织id集合 + * 返回值说明: boolean 是否修改成功 + ***********************************/ + boolean updateOrgScopeByRoleId(String roleId, String orgscope); +} diff --git a/src/main/java/com/yfd/platform/system/service/ISysRoleService.java b/src/main/java/com/yfd/platform/system/service/ISysRoleService.java new file mode 100644 index 0000000..7dcdb5c --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/ISysRoleService.java @@ -0,0 +1,66 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysRole; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统角色 服务类 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +public interface ISysRoleService extends IService { + + /*********************************** + * 用途说明:新增角色 + * 参数说明 + * sysRole 新增角色信息 + * 返回值说明: 是否新增成功 + ***********************************/ + boolean addRole(SysRole sysRole); + + /*********************************** + * 用途说明:删除角色用户 + * 参数说明 + * id 系统角色用户对照表id + * 返回值说明: 是否新增成功 + ***********************************/ + + boolean deleteRoleUsers(String roleid, String urserids); + + /*********************************** + * 用途说明:根据id删除角色 + * 参数说明 + *id 角色id + * 返回值说明: 是否删除成功 + ***********************************/ + void deleteById(String id); + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + List listRoleUsers(String orgid, String username, String status, String level, String rolename, String isvaild); + + + /*********************************** + * 用途说明:角色分配权限 + * 参数说明 + * id 角色id + * menuIds 权限id字符串 + * 返回值说明: 是否分配成功 + ***********************************/ + boolean setMenuById(String id, String menuIds); +} diff --git a/src/main/java/com/yfd/platform/system/service/IUserService.java b/src/main/java/com/yfd/platform/system/service/IUserService.java new file mode 100644 index 0000000..b141e53 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/IUserService.java @@ -0,0 +1,143 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysUser; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统用户 + *

+ * + * @author zhengsl + * @since 2021-10-27 + */ +public interface IUserService extends IService { + + //获取当前用户账号及名称 + String getUsername(); + + //获取当前用户信息 + SysUser getUserInfo(); + /*********************************** + * 用途说明:获取当前用户账号与姓名 + * 返回值说明: 当前用户账号与姓名 + ************************************/ + Map getNameInfo(); + //获取当前用户信息带权限 + ResponseResult getLoginUserInfo(); + + /*********************************** + * 用途说明:新增用户 + * 参数说明 + *sysUser 新增用户对象 + * id 创建者id + * roleId 角色id + * 返回值说明: 提示字符串 + ************************************/ + Map addUser(SysUser sysUser, String roleids); + + /*********************************** + * 用途说明:查询系统用户 + * 参数说明 + *page 分页集合参数 + *orgid 所属组织 + *username 用户名称 + * mobile 手机号 + * status 状态 + * 返回值说明: 用户分页集合 + ************************************/ + List list(String total, String size, String orgid, String username, + String mobile, String status); + + /*********************************** + * 用途说明:根据ID查询用户详情 + * 参数说明 + *id 用户id + * 返回值说明: 用户表对象 + ************************************/ + Map getOneById(String id); + + /*********************************** + * 用途说明:根据ID修改用户 + * 参数说明 + *sysUser 用户对象 + *roleids 角色id + * 返回值说明: 是否更新成功 + ************************************/ + Map updateById(SysUser sysUser, String roleids); + + /*********************************** + * 用途说明:用户分配角色(多个) + * 参数说明 + *roleid 角色id + * userids 用户id数组 + * 返回值说明: 判断是否添加成功 + ************************************/ + boolean setUserRoles(String roleid, String userids); + + /*********************************** + * 用途说明:根据id删除用户 + * 参数说明 + *id 用户id + * 返回值说明: 判断是否删除成功 + ************************************/ + boolean deleteById(String id); + + /*********************************** + * 用途说明:重置用户密码(管理员) + * 参数说明 + *id 重置密码的 用户id + * 返回值说明: 判断是否重置成功 + ************************************/ + boolean resetPassword(String id) throws Exception; + + /*********************************** + * 用途说明:设置账号状态(管理员) + * 参数说明 + *id 用户id + * status 设置状态 + * 返回值说明: 判断是否设置成功 + ************************************/ + boolean setStatus(String id, String status); + + /*********************************** + * 用途说明:上传用户头像 + * 参数说明 + * id 用户id + * img 账号头像 + * 返回值说明: 判断是否上传 + ***********************************/ + boolean uploadAvatar(String id, MultipartFile img); + + /*********************************** + * 用途说明:新增系统角色用户对照表 对用户分配角色(单个) + * 参数说明 + * id 生成的id + * userid 用户id + * roleid 角色id + * 返回值说明: + ************************************/ + boolean addUserRoles(String roleid, String userid); + + //Page queryUsers(String orgid, String username, Page page); + Page> queryUsers(String orgid, String username, Page page); + + /*********************************** + * 用途说明:根据ID批量删除用户 + * 参数说明 + *ids 用户id集合 + * 返回值说明: 判断是否删除成功 + ************************************/ + boolean deleteUserByIds(String ids); + + + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/MessageServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/MessageServiceImpl.java new file mode 100644 index 0000000..0d3875f --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/MessageServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.system.service.impl; + +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.mapper.MessageMapper; +import com.yfd.platform.system.service.IMessageService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 消息通知 服务实现类 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +@Service +public class MessageServiceImpl extends ServiceImpl implements IMessageService { + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/QuartzJobServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/QuartzJobServiceImpl.java new file mode 100644 index 0000000..d2a9030 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/QuartzJobServiceImpl.java @@ -0,0 +1,114 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.mapper.QuartzJobMapper; +import com.yfd.platform.system.service.IQuartzJobService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.utils.QuartzManage; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + *

+ * 定时任务 服务实现类 + *

+ * + * @author TangWei + * @since 2023-03-19 + */ +@Service +public class QuartzJobServiceImpl extends ServiceImpl implements IQuartzJobService { + + @Resource + private QuartzJobMapper quartzJobMapper; + + @Resource + private QuartzManage quartzManage; + + /********************************** + * 用途说明: 新增定时任务 + * 参数说明 quartzJob 定时对象 + * 返回值说明: boolean 是否成功 + ***********************************/ + @Override + public boolean addQuartzJob(QuartzJob quartzJob) { + // 生成序号 + long orderNo = this.count() + 1L; + quartzJob.setOrderno((int) orderNo); + return this.save(quartzJob); + } + + /********************************** + * 用途说明: 删除定时任务 + * 参数说明 id id + * 返回值说明: boolean 是否成功 + ***********************************/ + @Override + public boolean deleteQuartzJob(String id) { + String[] split = id.split(","); + Set ids = Arrays.stream(split).collect(Collectors.toSet()); + for (String s : ids) { + QuartzJob quartzJob = this.getById(s); + quartzManage.deleteJob(quartzJob); + this.removeById(s); + } + + // 查询所有定时任务 + List list = + this.list(new LambdaQueryWrapper().orderByAsc(QuartzJob::getOrderno)); + // 更新序号 + for (int i = 0; i < list.size(); i++) { + QuartzJob quartzJob = list.get(i); + quartzJob.setOrderno(i + 1); + this.updateById(quartzJob); + } + return true; + } + + /********************************** + * 用途说明: 拖动修改定时任务顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Override + public boolean changeDictOrder(String fromID, String toID) { + QuartzJob fromQuartzJob = + quartzJobMapper.selectById(fromID); + QuartzJob toQuartzJob = quartzJobMapper.selectById(toID); + // 如果数据字典不存在拖动失败 + if (fromQuartzJob == null || toQuartzJob == null) { + return false; + } + Integer fromOrderNo = fromQuartzJob.getOrderno(); + Integer toOrderNo = toQuartzJob.getOrderno(); + // 如果数据字典的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + // 将顺序号放入字典对象中 + fromQuartzJob.setOrderno(toOrderNo); + toQuartzJob.setOrderno(fromOrderNo); + // 更改顺序号 + boolean fromBool = this.updateById(fromQuartzJob); + boolean toBool = this.updateById(toQuartzJob); + return fromBool && toBool; + } + + /********************************** + * 用途说明: 执行定时任务 + * 参数说明 id id + * 返回值说明: void + ***********************************/ + @Override + public void execution(QuartzJob quartzJob) { + quartzManage.runJobNow(quartzJob); + } +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysConfigServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..da15188 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,34 @@ +package com.yfd.platform.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysConfig; +import com.yfd.platform.system.mapper.SysConfigMapper; +import com.yfd.platform.system.service.ISysConfigService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + *

+ * 系统全局配置 服务实现类 + *

+ * + * @author zhengsl + * @since 2022-01-19 + */ +@Service +public class SysConfigServiceImpl extends ServiceImpl implements ISysConfigService { + @Resource + private UserServiceImpl currentUser; + + + + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryItemsServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryItemsServiceImpl.java new file mode 100644 index 0000000..26a30ab --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryItemsServiceImpl.java @@ -0,0 +1,123 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + *

+ * 数据字典明细 服务实现类 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@Service +public class SysDictionaryItemsServiceImpl extends ServiceImpl implements ISysDictionaryItemsService { + + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; + + /********************************** + * 用途说明: 分页查询字典项信息 + * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @Override + public Page getDictItemPage(String dictId, + String itemName, + Page page) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(itemName)) { + queryWrapper.like(SysDictionaryItems::getDictName, itemName); + } + queryWrapper.eq(SysDictionaryItems::getDictId, dictId).orderByAsc(SysDictionaryItems::getOrderNo); + return sysDictionaryItemsMapper.selectPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 增加字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + @Override + public boolean addDictionaryItem(SysDictionaryItems sysDictionaryItems) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId,sysDictionaryItems.getDictId()); + long orderNo = this.count(queryWrapper) + 1L; + // 添加顺序号 + sysDictionaryItems.setOrderNo((int) orderNo); + return this.save(sysDictionaryItems); + } + + /********************************** + * 用途说明: 拖动修改字典项顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Override + public boolean changeItemOrder(String fromID, String toID) { + SysDictionaryItems fromSysDictionaryItems = + sysDictionaryItemsMapper.selectById(fromID); + SysDictionaryItems toSysDictionaryItems = + sysDictionaryItemsMapper.selectById(toID); + // 如果数据字典项不存在拖动失败 + if (fromSysDictionaryItems == null || toSysDictionaryItems == null) { + return false; + } + Integer fromOrderNo = fromSysDictionaryItems.getOrderNo(); + Integer toOrderNo = toSysDictionaryItems.getOrderNo(); + // 如果数据字典的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + // 将顺序号放入字典对象中 + fromSysDictionaryItems.setOrderNo(toOrderNo); + toSysDictionaryItems.setOrderNo(fromOrderNo); + // 更改顺序号 + boolean fromBool = this.updateById(fromSysDictionaryItems); + boolean toBool = this.updateById(toSysDictionaryItems); + return fromBool && toBool; + + } + + /********************************** + * 用途说明: 导出数据字典项数据 + * 参数说明 sysDictionaryItemsList 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或失败 + ***********************************/ + @Override + public void exportExcel(List sysDictionaryItems, + HttpServletResponse response) { + try { + List> list = new LinkedList<>(); + for (SysDictionaryItems sysDictionaryItem : sysDictionaryItems) { + Map map = new LinkedHashMap<>(); + map.put("项编号", sysDictionaryItem.getItemCode()); + map.put("项名称", sysDictionaryItem.getDictName()); + map.put("父编码", sysDictionaryItem.getParentCode()); + map.put("备注", sysDictionaryItem.getCustom1()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryServiceImpl.java new file mode 100644 index 0000000..58508b3 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryServiceImpl.java @@ -0,0 +1,114 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.mapper.SysDictionaryMapper; +import com.yfd.platform.system.service.ISysDictionaryService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + *

+ * 数据字典表 服务实现类 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@Service +public class SysDictionaryServiceImpl extends ServiceImpl implements ISysDictionaryService { + + @Resource + private SysDictionaryMapper sysDictionaryMapper; + + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; + + /********************************** + * 用途说明: 获取数据字典列表 + * 参数说明 dictType 字典类型 + * 返回值说明: 返回字典列表集合 + ***********************************/ + @Override + public List getDictList(String dictType) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionary::getDictType, dictType).orderByAsc(SysDictionary::getOrderNo); + return sysDictionaryMapper.selectList(queryWrapper); + } + + /********************************** + * 用途说明: 新增字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: 返回增加成功或者失败 + ***********************************/ + @Override + public boolean addDict(SysDictionary sysDictionary) { + //int orderNo = this.count() + 1; + Integer maxNo = + sysDictionaryMapper.selectMaxNo(sysDictionary.getDictType()); + if (maxNo == null) { + maxNo = 0; + } + // 添加顺序号 + sysDictionary.setOrderNo(maxNo + 1); + return this.save(sysDictionary); + } + + /********************************** + * 用途说明: 根据ID删除字典 + * 参数说明 id 字典ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除结果成功或者失败 + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteDictById(String id) { + // 根据字典编码查询字典项中是否关联 + boolean isok=true; + QueryWrapper Wrapper = new QueryWrapper<>(); + Wrapper.eq("dictid", id); + if(sysDictionaryItemsMapper.delete(Wrapper)>0) { + isok=true; + } + return isok&&this.removeById(id); + } + + /********************************** + * 用途说明: 拖动修改字典顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Override + public boolean changeDictOrder(String fromID, String toID) { + SysDictionary fromSysDictionary = + sysDictionaryMapper.selectById(fromID); + SysDictionary toSysDictionary = sysDictionaryMapper.selectById(toID); + // 如果数据字典不存在拖动失败 + if (fromSysDictionary == null || toSysDictionary == null) { + return false; + } + Integer fromOrderNo = fromSysDictionary.getOrderNo(); + Integer toOrderNo = toSysDictionary.getOrderNo(); + // 如果数据字典的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + // 将顺序号放入字典对象中 + fromSysDictionary.setOrderNo(toOrderNo); + toSysDictionary.setOrderNo(fromOrderNo); + // 更改顺序号 + boolean fromBool = this.updateById(fromSysDictionary); + boolean toBool = this.updateById(toSysDictionary); + return fromBool && toBool; + } + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysLogServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysLogServiceImpl.java new file mode 100644 index 0000000..ee671bf --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysLogServiceImpl.java @@ -0,0 +1,219 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.mapper.SysLogMapper; +import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + *

+ * 系统操作日志 服务实现类 + *

+ * + * @author TangWei + * @since 2023-03-08 + */ +@Service +public class SysLogServiceImpl extends ServiceImpl implements ISysLogService { + + @Resource + private SysLogMapper sysLogMapper; + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 pageNum(页码数)、pageSize(页大小,如果是固定页大小可不传)、username(用户名)、(optType) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @Override + public Page getLogList(String username, String optType, + String startDate, + String endDate, Page page) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 没有传username就不按此条件查询 + if (StrUtil.isNotBlank(username)) { + queryWrapper.like(SysLog::getUsername, username); + } + // 没有传optType就不按此条件查询 + if (StrUtil.isNotBlank(optType)) { + queryWrapper.eq(SysLog::getOpttype, optType); + } + DateTime parseStartDate = DateUtil.parse(startDate); + DateTime parseEndDate = DateUtil.parse(endDate); + DateTime dateTime = DateUtil.offsetDay(parseEndDate, 1); + if (parseStartDate != null && parseEndDate != null) { + queryWrapper.ge(SysLog::getLogtime, parseStartDate).lt(SysLog::getLogtime, dateTime); + } + + queryWrapper.orderByDesc(SysLog::getLogtime); + return sysLogMapper.selectPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 导出日志数据 + * 参数说明 sysLogs 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + @Override + public void exportExcel(List sysLogs, + HttpServletResponse response) { + try { + List> list = new LinkedList<>(); + for (SysLog sysLog : sysLogs) { + Map map = new LinkedHashMap<>(); + map.put("操作账号", sysLog.getUsercode()); + map.put("用户姓名", sysLog.getUsername()); + map.put("IP地址", sysLog.getRequestip()); + map.put("浏览器", sysLog.getBrowser()); + map.put("日志类型", sysLog.getOpttype()); + map.put("模块名称", sysLog.getModule()); + map.put("日志描述", sysLog.getDescription()); + Timestamp logTime = sysLog.getLogtime(); + String dateTime = ""; + if (logTime != null) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd " + + "HH:mm:ss"); + dateTime = df.format(logTime); + } + + /*String dateTime = ""; + if (logTime != null) { + dateTime = logTime.format(DateTimeFormatter.ofPattern( + "yyyy-MM-dd HH:mm:ss")); + }*/ + + map.put("操作日期", dateTime); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /********************************** + * 用途说明: 新增日志 + * 参数说明 nickname 用户名 + * 参数说明 username 用户账号 + * 参数说明 browser 浏览器 + * 参数说明 ip 本机Ip地址 + * 参数说明 joinPoint 连接点 + * 参数说明 log 日志信息 + * 返回值说明: void + ***********************************/ + @Override + public void save(String nickname, String username, String browser, + String ip, + ProceedingJoinPoint joinPoint, SysLog log) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + Log aopLog = method.getAnnotation(Log.class); + // 方法路径 + String methodName = + joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()"; + // 描述 + if (log != null) { + log.setDescription(aopLog.value()); + log.setModule(aopLog.module()); + } + assert log != null; + log.setUsercode(username); + log.setRequestip(ip); + log.setMethod(methodName); + log.setUsername(nickname); + log.setParams(getParameter(method, joinPoint.getArgs())); + log.setBrowser(browser); + String operationtype = getOperationtype(signature.getName()); + log.setOpttype(operationtype); + log.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogMapper.insert(log); + } + + /** + * 根据方法和传入的参数获取请求参数 + */ + private String getParameter(Method method, Object[] args) { + List argList = new ArrayList<>(); + Parameter[] parameters = method.getParameters(); + for (int i = 0; i < parameters.length; i++) { + //将RequestBody注解修饰的参数作为请求参数 + AnnotatedType type = parameters[i].getAnnotatedType(); + + RequestBody requestBody = + parameters[i].getAnnotation(RequestBody.class); + if (requestBody != null) { + argList.add(args[i]); + } + + //将RequestParam注解修饰的参数作为请求参数 + RequestParam requestParam = + parameters[i].getAnnotation(RequestParam.class); + if (requestParam != null) { + Map map = new HashMap<>(); + String key = parameters[i].getName(); + if (!StringUtils.isEmpty(requestParam.value())) { + key = requestParam.value(); + } + map.put(key, args[i]); + argList.add(map); + } + } + if (argList.size() == 0) { + return ""; + } + return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : + JSONUtil.toJsonStr(argList); + } + + public static String getOperationtype(String value) { + String type = ""; + if (value.contains("get") || value.contains("select")) { + type = "查询(select)"; + } else if (value.contains("add") || value.contains("insert")) { + type = "添加(insert)"; + } else if (value.contains("update") || value.contains("upd") || value.contains("change") || value.contains("set")) { + type = "修改(update)"; + } else if (value.contains("delete") || value.contains("del")) { + type = "删除(delete)"; + } else if (value.contains("dowload")) { + type = "下载(dowload)"; + } else if (value.contains("import")) { + type = "导入(import)"; + } else if (value.contains("word")) { + type = "word转pdf(wordToPdf)"; + } else { + type = "其他(other)"; + } + return type; + } + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysMenuServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..b53b789 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,674 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysMenu; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.mapper.SysMenuMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysMenuService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.config.FileSpaceProperties; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ResourceUtils; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 菜单及按钮 服务实现类 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@Service +@Transactional +public class SysMenuServiceImpl extends ServiceImpl implements ISysMenuService { + + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private UserServiceImpl currentUser; + + @Resource + private SysRoleMapper sysRoleMapper; + + // 菜单图片路径配置 + @Resource + private FileSpaceProperties fileSpaceProperties; + + /*********************************** + * 用途说明:查询菜单及按钮树状图 + * 参数说明 + * systemcode 系统 + *isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @Override + public List> getMenuButtonTree(String systemcode, + String name, + String isdisplay) { + List> listMap=null; + if(StrUtil.isEmpty(name)){//不带名称查询,返回树结构 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parentid", "0").eq("systemcode", systemcode).orderByAsc("orderno"); + listMap = this.listMaps(queryWrapper); + + for (int i = 0; i < listMap.size(); i++) { + //查询下一子集 + List> childList = child(listMap.get(i).get( + "id").toString(), systemcode, name, null, null); + listMap.get(i).put("children", childList); //添加新列 子集 + } + }else{ //根据菜单名称查询,直接返回类别 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like("name", name).eq("systemcode", systemcode).orderByAsc("name"); + listMap = this.listMaps(queryWrapper); + } + + + + return listMap; + } + + /*********************************** + * 用途说明:获取菜单结构树(不含按钮) + * 参数说明 + * systemcode 系统 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @Override + public List> getMenuTree(String systemcode, + String name, + String isdisplay) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotEmpty(isdisplay)) { + queryWrapper.eq("isdisplay", isdisplay); + } else { + queryWrapper.eq("isdisplay", 1); + } + + //根据系统 ,类型不为2 显示,序号 正序排序 + queryWrapper.eq("parentid", "0").eq("systemcode", systemcode).ne( + "type", "2").orderByAsc("orderno"); + List> listMap = this.listMaps(queryWrapper); + for (int i = 0; i < listMap.size(); i++) { + List> childList = child(listMap.get(i).get( + "id").toString(), systemcode, name, isdisplay, "2");//查询下一子集 + listMap.get(i).put("children", childList); //添加新列 子集 + } + return listMap; + } + + /*********************************** + * 用途说明:查询菜单及按钮树状图 + * 参数说明 + * parentid 上级id + *systemcode 系统 + * isdisplay 是否显示 + * type 按钮 + * 返回值说明: 菜单结构树集合 + ***********************************/ + private List> child(String parentid, + String systemcode, String name, + String isdisplay, String type) { + List> listMap = new ArrayList<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parentid", parentid).eq("systemcode", systemcode); + //根据上级id 系统 查询 + if (StrUtil.isNotEmpty(type)) { + queryWrapper.ne("type", type); + } + + if (StrUtil.isNotEmpty(name)) { //根据菜单名称查询 + queryWrapper.like("name", name); + } + listMap = this.listMaps(queryWrapper.orderByAsc("orderno")); + if (listMap.size() > 0) { //判断是否存在子集 + for (int i = 0; i < listMap.size(); i++) { //遍历表数据 + List> childList = + child(listMap.get(i).get("id").toString(), systemcode + , name, isdisplay, type); //循环获取下一子集 + listMap.get(i).put("children", childList); //添加新列 子集 + } + } + + return listMap; + } + + /*********************************** + * 用途说明:新增菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否添加成功提示 + ***********************************/ + @Override + public boolean addMenu(SysMenu sysMenu) { + String parentId = sysMenu.getParentid(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + //根据上级id 查询到总数 并累加 + long orderno = this.count(queryWrapper.eq("parentid", + parentId)) + 1L; + // 生成排序号 + sysMenu.setOrderno((int) orderno); + // 生成编号 + QueryWrapper queryMaxCode = new QueryWrapper<>(); + queryMaxCode.eq("parentid", + parentId); + // 查询最大的编号 + List maxList = this.listObjs(queryMaxCode.select("max(code) " + + "code").eq("systemcode", sysMenu.getSystemcode())); + SysMenu parentMenu = sysMenuMapper.selectById(parentId); + // 最大编号转换成int类型 + String maxCode = maxList.size() > 0 ? maxList.get(0).toString() : "0"; + int max = ObjectUtil.isEmpty(maxList) ? 0 : + Integer.parseInt(maxCode); + DecimalFormat df; + if ("0".equals(sysMenu.getParentid())) { + df = new DecimalFormat("00"); + } else if (parentMenu.getCode().length() == 2) { + df = new DecimalFormat("0000"); + } else { + df = new DecimalFormat("000000"); + } + //DecimalFormat df = new DecimalFormat("00"); + //int parentCode = Integer.parseInt(parentMenu.getCode()); + String parentCode = ""; + if (parentMenu != null) { + parentCode = parentMenu.getCode(); + } + // 生成的新编号 年月日+4位编号 + String code; + if (max > 0) { + code = df.format(max + 1); + } else { + max = max + 1; + if (StrUtil.isBlank(parentCode)) { + parentCode = "0" + max; + } else { + int i = Integer.parseInt(parentCode); + parentCode = i + "0" + max; + } + + int format = Integer.parseInt(parentCode); + code = df.format(format); + } + + // 判断是否显示字段 是否填写 为空 + if (StrUtil.isEmpty(sysMenu.getIsdisplay())) { + // 默认设置成 1 显示 + sysMenu.setIsdisplay("1"); + } + // 判断是否填写父级id 为空 默认设置成 0 + if (StrUtil.isEmpty(sysMenu.getParentid())) { + sysMenu.setParentid("0"); + } + // 添加编号 + sysMenu.setCode(code); + // 添加排序号 + sysMenu.setOrderno((int) orderno); + // 添加最近修改人 + sysMenu.setLastmodifier(currentUser.getUsername()); + // 添加最近修改时间 + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + return this.save(sysMenu); + } + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * id 上传图标id + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + @Override + public boolean uploadIcon(String id, MultipartFile icon) { + //根据id查询 + SysMenu sysMenu = this.getById(id); + //图片路径 + String iconPath = fileSpaceProperties.getSystem() + "menu" + File.separator; + String iconname = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(icon.getOriginalFilename()); + //上传图标并获取图标名称 (图片改为png格式) + String filename = + FileUtil.upload(icon, iconPath, iconname).getName(); + //更新图标名称 + sysMenu.setIcon(filename); + //添加最近修改人 + sysMenu.setLastmodifier(currentUser.getUsername()); + //添加最近修改时间 + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //更新数据 + boolean isOk = this.updateById(sysMenu); + return isOk; + } + + /*********************************** + * 用途说明:根据id删除单个图标 + * 参数说明 + * id 删除图标id + * icon 图标名称 + * 返回值说明: 是否删除成功 + ***********************************/ + @Override + public boolean deleteIcon(String id) { + //根据id查询 + SysMenu sysMenu = this.getById(id); + //图片路径 + String iconname = + System.getProperty("user.dir") + "\\src\\main" + + "\\resources\\static\\icon" + File.separator + sysMenu.getIcon(); + //更新图标名称 + sysMenu.setIcon(""); + //添加最近修改人 + sysMenu.setLastmodifier(currentUser.getUsername()); + //添加最近修改时间 + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //更新数据 + boolean isOk = this.updateById(sysMenu); + //更新成功 删除图片 + if (isOk == true) { + FileUtil.del(iconname); + } + return isOk; + } + + /*********************************** + * 用途说明:菜单及按钮序号排序 + * 参数说明 + * parentid 上级id + * id + * orderno 更改后序号 + * 返回值说明: 是否更新成功 + ***********************************/ + @Override + public boolean moveOrderno(String parentid, String id, int orderno) { + boolean ok = true; + SysMenu sysMenu = this.getById(id); //根据id查询原顺序号 + if (sysMenu.getOrderno() > orderno) { + ok = sysMenuMapper.upMoveOrderno(parentid, sysMenu.getOrderno(), + orderno); //根据 父级id 小于原序号 大于等于更改序号 + } else { + ok = sysMenuMapper.downMoveOrderno(parentid, sysMenu.getOrderno() + , orderno); //根据 父级id 大于原序号 小于等于更改序号 + } + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + ok = ok && this.update(updateWrapper.eq("id", id).set("orderno", + orderno)); //根据 id修改序号 + return ok; + } + + /*********************************** + * 用途说明:根据id删除菜单或按钮 + * 参数说明 + * id 删除列的id + * 返回值说明: 是否删除成功 + ***********************************/ + @Override + public boolean deleteById(String id) { + //根据id查询 + SysMenu sysMenu = this.getById(id); + //图片路径 + String iconname = + fileSpaceProperties.getSystem() + "menu" + File.separator + sysMenu.getIcon(); + //删除图标 + new File(iconname).delete(); + //根据id删除 + boolean isOk = this.removeById(id); + //删除成功同步更新表数据 + if (isOk) { + //1 创建list集合,用于封装所有删除目录或菜单id值 + List idList = new ArrayList<>(); + this.selectPermissionChildById(id, idList); + if (idList.size() > 0) { + sysMenuMapper.deleteBatchIds(idList); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + //根据上级id 查询 根据 orderno 正序排序 + queryWrapper.eq("parentid", sysMenu.getParentid()).orderByAsc( + "orderno"); + List list = this.list(queryWrapper); + for (int i = 0; i < list.size(); i++) { + SysMenu menu = list.get(i); + //更新序列号 + menu.setOrderno(i + 1); + } + //更新表数据 + this.updateBatchById(list); + } + return isOk; + } + + //2 根据当前菜单id,查询菜单里面子菜单id,封装到list集合 + private void selectPermissionChildById(String id, List idList) { + //查询菜单里面子菜单id + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("parentid", id); + wrapper.select("id"); + List childIdList = baseMapper.selectList(wrapper); + //把childIdList里面菜单id值获取出来,封装idList里面,做递归查询 + childIdList.stream().forEach(item -> { + //封装idList里面 + idList.add(item.getId()); + //递归查询 + this.selectPermissionChildById(item.getId(), idList); + }); + } + + /********************************** + * 用途说明: 调换菜单或按钮的位置 + * 参数说明 upperId 选中的菜单Id + * 参数说明 belowId 切换的菜单Id + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional + public boolean changeOderNoById(String fromId, String toId) { + SysMenu fromSysMenu = this.getById(fromId); + SysMenu toSysMenu = this.getById(toId); + // 如果菜单或按钮不存在拖动失败 + if (fromSysMenu == null || toSysMenu == null) { + return false; + } + Integer fromOrderNo = fromSysMenu.getOrderno(); + Integer toOrderNo = toSysMenu.getOrderno(); + // 如果菜单或按钮的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + fromSysMenu.setOrderno(toOrderNo); + toSysMenu.setOrderno(fromOrderNo); + boolean fromBool = this.updateById(fromSysMenu); + boolean toBool = this.updateById(toSysMenu); + return fromBool && toBool; + } + + /********************************** + * 用途说明: 根据用户id获取菜单树 + * 参数说明 id 用户id + * 返回值说明: 返回菜单树 + ***********************************/ + @Override + public List> getMenuTree(String id) { + // 根据id获取菜单 + //List sysMenus = sysMenuMapper.selectMenuByUserId(id); + List> list; + if (StrUtil.isBlank(id)) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + list = this.listMaps(queryWrapper.eq(SysMenu::getIsdisplay, "1").ne(SysMenu::getType, "2").eq(SysMenu::getSystemcode, "1").orderByAsc(SysMenu::getOrderno)); + } else { + list = sysMenuMapper.selectMenuByUserId(id); + } + // 将菜单转换成树 + List> sysMenus = buildTreeLeft(list); + return sysMenus; + } + + /*********************************** + * 用途说明:权限分配 + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @Override + public List> permissionAssignment(String roleId) { + + String code = sysMenuMapper.getSystemCodeById(roleId); + if (code == null) { + code = "1"; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getSystemcode, code).select(SysMenu::getId, + SysMenu::getParentid, SysMenu::getName).orderByAsc + (SysMenu::getOrderno); + List> listAll = + sysMenuMapper.selectMaps(queryWrapper); + List listRole = + sysMenuMapper.selectMenuByRoleId(roleId); + for (Map map : listAll) { + String id = (String) map.get("id"); + if (listRole.contains(id)) { + map.put("checkinfo", true); + } else { + map.put("checkinfo", false); + } + + } + List> listTree = buildTrees(listAll); + return listTree; + } + + // 另一种方法 + /*public List> permissionAssignment(String roleId) { + + String code = sysMenuMapper.getSystemCodeById(roleId); + if (code == null) { + code = "1"; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getSystemcode, code).select(SysMenu::getId, + SysMenu::getParentid, SysMenu::getName).orderByAsc + (SysMenu::getOrderno); + List> listAll = + sysMenuMapper.selectMaps(queryWrapper); + *//*List listRole = + sysMenuMapper.selectMenuByRoleId(roleId);*//* + SysRole sysRole = sysRoleMapper.selectById(roleId); + String optscope = sysRole.getOptscope(); + // 将当前角色所对应权限id拆分 + String[] split = optscope.split(","); + List listRole = Arrays.asList(split); + for (Map map : listAll) { + String id = (String) map.get("id"); + if (listRole.contains(id)) { + map.put("checkinfo", true); + } else { + map.put("checkinfo", false); + } + + } + List> listTree = buildTrees(listAll); + return listTree; + }*/ + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + @Override + public String uploadIcon(MultipartFile icon) throws FileNotFoundException { + + String path = System.getProperty("user.dir") + "\\src\\main" + + "\\resources\\"; + //图片路径 + String iconPath = path + "static\\icon" + File.separator; + String iconname = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(icon.getOriginalFilename()); + //上传图标并获取图标名称 (图片改为png格式) + String filename = + FileUtil.upload(icon, iconPath, iconname).getName(); + return filename; + } + + /** + * 菜单集合递归生成树状菜单(List) + * + * @param sysMenus 菜单对象 + * @return + */ + /* public List buildTree(List sysMenus) { + List resultMenuList = new ArrayList<>(); + for (SysMenu sysMenu : sysMenus) { + + for (SysMenu menu : sysMenus) { + if (menu.getParentid().equals(sysMenu.getId())) { + sysMenu.getChildren().add(menu); + } + } + if ("0".equals(sysMenu.getParentid())) { + resultMenuList.add(sysMenu); + } + } + return resultMenuList; + }*/ + + /** + * 菜单集合递归生成树状菜单(Map)(暂不使用该方法) + * + * @param sysMenus 菜单对象 + * @return + */ + public List> buildTree(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + for (Map sysMenu : sysMenus) { + + List> childrenList = new ArrayList<>(); + for (Map menu : sysMenus) { + if (menu.get("parentid").equals(sysMenu.get("id"))) { + childrenList.add(menu); + } + } + if ("0".equals(sysMenu.get("parentid"))) { + if (childrenList.size() > 0) { + sysMenu.put("children", childrenList); + } + resultMenuList.add(sysMenu); + } + } + return resultMenuList; + } + + /********************************** + * 用途说明: 左侧菜单树构建 + * 参数说明 sysMenus + * 返回值说明: java.util.List> + ***********************************/ + public List> buildTreeLeft(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + for (Map sysMenu : sysMenus) { + if ("0".equals(sysMenu.get("parentid"))) { + resultMenuList.add(sysMenu); + } + } + for (Map sysMenu : resultMenuList) { + List> menus = iterateMenusLeft(sysMenus, + (String) sysMenu.get("id")); + if (menus.size() > 0) { + sysMenu.put("children", menus); + } + } + return resultMenuList; + } + + /** + * 左侧多级菜单查询方法 + * + * @param menuVoList 不包含最高层次菜单的菜单集合 + * @param pid 父类id + * @return + */ + public List> iterateMenusLeft(List> menuVoList, String pid) { + List> result = new ArrayList<>(); + for (Map menu : menuVoList) { + //获取菜单的id + String menuid = (String) menu.get("id"); + //获取菜单的父id + String parentid = (String) menu.get("parentid"); + if (StrUtil.isNotBlank(parentid)) { + if (parentid.equals(pid)) { + //递归查询当前子菜单的子菜单 + List> iterateMenu = + iterateMenus(menuVoList, menuid); + if (iterateMenu.size() > 0) { + menu.put("children", iterateMenu); + } + result.add(menu); + } + } + } + return result; + } + + /********************************** + * 用途说明: 生成权菜单权限树 + * 参数说明 sysMenus + * 返回值说明: java.util.List> + ***********************************/ + public List> buildTrees(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + for (Map sysMenu : sysMenus) { + if ("0".equals(sysMenu.get("parentid"))) { + resultMenuList.add(sysMenu); + } + } + for (Map sysMenu : resultMenuList) { + List> menus = iterateMenus(sysMenus, + (String) sysMenu.get("id")); + for (Map menu : menus) { + if (!(boolean) menu.get("checkinfo")) { + sysMenu.put("checkinfo", false); + break; + } + } + sysMenu.put("children", menus); + } + return resultMenuList; + } + + /** + * 多级菜单查询方法 + * + * @param menuVoList 不包含最高层次菜单的菜单集合 + * @param pid 父类id + * @return + */ + public List> iterateMenus(List> menuVoList, String pid) { + List> result = new ArrayList<>(); + for (Map menu : menuVoList) { + //获取菜单的id + String menuid = (String) menu.get("id"); + //获取菜单的父id + String parentid = (String) menu.get("parentid"); + if (StrUtil.isNotBlank(parentid)) { + if (parentid.equals(pid)) { + //递归查询当前子菜单的子菜单 + List> iterateMenu = + iterateMenus(menuVoList, menuid); + for (Map map : iterateMenu) { + boolean checkinfo = (boolean) map.get("checkinfo"); + if (!checkinfo) { + menu.put("checkinfo", false); + } + } + menu.put("children", iterateMenu); + result.add(menu); + } + } + } + return result; + } + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysOrganizationServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysOrganizationServiceImpl.java new file mode 100644 index 0000000..01ea014 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysOrganizationServiceImpl.java @@ -0,0 +1,338 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysOrganizationMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysOrganizationService; +import com.yfd.platform.system.service.IUserService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + *

+ * 系统组织框架 服务实现类 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@Service +public class SysOrganizationServiceImpl extends ServiceImpl implements ISysOrganizationService { + + @Resource + private UserServiceImpl currentUser; + + @Resource + private IUserService userService; + + @Resource + private SysRoleMapper sysRoleMapper; + + @Resource + private SysOrganizationMapper sysOrganizationMapper; + + /*********************************** + * 用途说明:获取组织结构树 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + @Override + public List> getOrgTree(String parentid, + String params) { + List> listMap = new ArrayList<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + //根据父级id查询 + queryWrapper.eq("parentid", parentid); + if (StrUtil.isNotEmpty(params)) { + queryWrapper.like("orgname", params); // 根据 部门名称 + } + SysUser userInfo = userService.getUserInfo(); + if (userInfo.getUsertype() != 0) { + List roleByUserId = + sysRoleMapper.getRoleByUserId(userInfo.getId()); + List ids = new ArrayList<>(); + // 循环当前角色 + for (SysRole sysRole : roleByUserId) { + // 获取角色的组织Id + String orgscope = sysRole.getOrgscope(); + if (StrUtil.isBlank(orgscope)) { + continue; + } + // 拆分组织Id + String[] split = orgscope.split(","); + List stringList = Arrays.asList(split); + Set set = new HashSet<>(); + if (stringList.size() > 0) { + List list = + sysOrganizationMapper.selectList(new LambdaQueryWrapper().in(SysOrganization::getId, stringList)); + list.forEach(l -> set.add(l.getParentid())); + } + ids.addAll(stringList); + ids.addAll(set); + } + queryWrapper.in("id", ids); + } + listMap = this.listMaps(queryWrapper.orderByAsc("orgcode")); + for (int i = 0; i < listMap.size(); i++) { + List> childList = child(listMap.get(i).get( + "id").toString());//查询下一子集 + listMap.get(i).put("childList", childList); //添加新列 子集 + } + return listMap; + } + + /*********************************** + * 用途说明:获取组织结构树 + * 参数说明 + *parentid 上级id + * params (根据参数查询 组织名称 负责人 描述) + * 返回值说明: 组织树集合 + ***********************************/ + private List> child(String parentid) { + List> listMap = new ArrayList<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parentid", parentid); //根据上级id 查询 + listMap = this.listMaps(queryWrapper.orderByAsc("orgcode")); + if (listMap.size() > 0) { //判断是否存在子集 + for (int i = 0; i < listMap.size(); i++) { //遍历表数据 + List> childList = + child(listMap.get(i).get("id").toString()); //循环获取下一子集 + listMap.get(i).put("childList", childList); //添加新列 子集 + } + } + return listMap; + } + + /*********************************** + * 用途说明:新增系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否新增成功 + ***********************************/ + @Override + public boolean addOrg(SysOrganization sysOrganization) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + SysOrganization parent = null; + int codeMax = 0; + //查询最大的编号 判断是否存在父级id 有值 根据父级id查询 否则 根据父级id为0 查询 + queryWrapper.select("max(orgcode)"); + if (StrUtil.isNotEmpty(sysOrganization.getParentid())) { + //根据父级id查询父级信息 + parent = this.getById(sysOrganization.getParentid()); + queryWrapper.eq("parentid", sysOrganization.getParentid()); + } else { + //默认 填写父级id为0 + sysOrganization.setParentid("0"); + queryWrapper.eq("parentid", "0"); + } + List max = this.listObjs(queryWrapper); + //判断查询是否存在 存在转换成int类型并给codeMax替换值 + if (max.size() > 0) { + codeMax = + Integer.parseInt(max.get(0).toString().substring(max.get(0).toString().length() - 2)); + } + //2位数字编号 + DecimalFormat df = new DecimalFormat("00"); + //编号 + String code = df.format(codeMax + 1); + //查询到父级不为空 重新赋值 父级编号+新的序号 + if (parent != null) { + code = parent.getOrgcode() + df.format(codeMax + 1); + } + //判断是否是否填写 有效 否则默认为 1 + if (StrUtil.isEmpty(sysOrganization.getIsvaild())) { + sysOrganization.setIsvaild("1"); + } + //填写 编号 + sysOrganization.setOrgcode(code); + //填写 当前用户名称 + sysOrganization.setLastmodifier(currentUser.getUsername()); + //填写 当前日期 + sysOrganization.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + return this.save(sysOrganization); + } + + /*********************************** + * 用途说明:根据企业ID查询组织详情 + * 参数说明 + * id 企业id + * 返回值说明: 系统组织框架对象 + ***********************************/ + @Override + public List getOrganizationById(String id, + String orgName) { + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + SysUser userInfo = userService.getUserInfo(); + if (userInfo.getUsertype() != 0) { + List roleByUserId = + sysRoleMapper.getRoleByUserId(userInfo.getId()); + List ids = new ArrayList<>(); + // 循环当前角色 + for (SysRole sysRole : roleByUserId) { + // 获取角色的组织Id + String orgscope = sysRole.getOrgscope(); + if (StrUtil.isBlank(orgscope)) { + continue; + } + // 拆分组织Id + String[] split = orgscope.split(","); + List stringList = Arrays.asList(split); + ids.addAll(stringList); + } + if (ObjectUtil.isNotEmpty(ids)) { + queryWrapper.in(SysOrganization::getId, ids); + } + + } + if (StrUtil.isNotBlank(orgName)) { + queryWrapper.like(SysOrganization::getOrgname, orgName); + } + queryWrapper.eq(SysOrganization::getParentid, id).orderByDesc(SysOrganization::getOrgcode); + return this.list(queryWrapper); + } + + /*********************************** + * 用途说明:获取组织范围树结构 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + @Override + public List> getOrgScopeTree(String roleId) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysOrganization::getIsvaild, '1'); + queryWrapper.orderByAsc(SysOrganization::getOrgcode); + List> listMaps = this.listMaps(queryWrapper); + // 获取当前角色 + SysRole sysRole = sysRoleMapper.selectById(roleId); + String orgscope = sysRole.getOrgscope(); + List ids = new ArrayList<>(); + if (StrUtil.isNotBlank(orgscope)) { + String[] split = orgscope.split(","); + ids = Arrays.asList(split); + } + + for (Map map : listMaps) { + String id = (String) map.get("id"); + if (ids.contains(id)) { + map.put("checkinfo", true); + } else { + map.put("checkinfo", false); + } + map.put("bool", true); + } + // 生成组织树 + List> listMap = buildTrees(listMaps); + return listMap; + } + + /********************************** + * 用途说明: 修改角色组织范围 + * 参数说明 roleId 角色id + * 参数说明 orgscope 组织id集合 + * 返回值说明: boolean 是否修改成功 + ***********************************/ + @Override + public boolean updateOrgScopeByRoleId(String roleId, String orgscope) { + SysRole sysRole = new SysRole(); + sysRole.setId(roleId); + sysRole.setOrgscope(orgscope); + int i = sysRoleMapper.updateById(sysRole); + if (i > 0) { + return true; + } else { + return false; + } + + } + + /********************************** + * 用途说明: 生成组织范围树 + * 参数说明 sysMenus + * 返回值说明: java.util.List> + ***********************************/ + public List> buildTrees(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + // 获取父节点 + for (Map sysMenu : sysMenus) { + if ("0".equals(sysMenu.get("parentid"))) { + resultMenuList.add(sysMenu); + } + } + // 寻找子节点 + for (Map sysMenu : resultMenuList) { + sysMenu.put("checkinfo", true); + List> children = new ArrayList<>(); + List array = new ArrayList<>(); + for (Map menu : sysMenus) { + String id = (String) sysMenu.get("id"); + String parentid = (String) menu.get("parentid"); + if (id.equals(parentid)) { + // 如果存在一个子节点没有被选中,父节点则不是全选状态 + if (!(boolean) menu.get("checkinfo")) { + sysMenu.put("checkinfo", false); + } else { + // 将处于选中状态的子节点加入到数组中 + array.add((String) menu.get("orgname")); + } + children.add(menu); + } + } + // 所有子节点加入父节点 + sysMenu.put("children", children); + sysMenu.put("array", array); + } + return resultMenuList; + } + + /** + * 组织集合递归生成树状菜单(Map) + * + * @param sysOrgList 组织集合 + * @return + */ + public List> buildTree(List> sysOrgList) { + List> resultOrgList = new ArrayList<>(); + for (Map sysOrg : sysOrgList) { + List> childrenList = new ArrayList<>(); + List array = new ArrayList<>(); + for (Map org : sysOrgList) { + if (org.get("parentid").equals(sysOrg.get("id"))) { + if (!(boolean) org.get("checkinfo")) { + sysOrg.put("checkinfo", false); + } + array.add((String) org.get("orgname")); + childrenList.add(org); + } + } + if ("0".equals(sysOrg.get("parentid"))) { + if (childrenList.size() > 0) { + sysOrg.put("children", childrenList); + } + resultOrgList.add(sysOrg); + } + sysOrg.put("array", array); + } + return resultOrgList; + } +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..63d5c36 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,163 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysRoleService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统角色 服务实现类 + *

+ * + * @author zhengsl + * @since 2021-12-15 + */ +@Service +@Transactional +public class SysRoleServiceImpl extends ServiceImpl implements ISysRoleService { + + @Resource + private SysRoleMapper roleMapper; + + @Resource + private UserServiceImpl currentuser; + + /*********************************** + * 用途说明:新增角色 + * 参数说明 + * sysRole 新增角色信息 + * 返回值说明: 是否新增成功 + ***********************************/ + @Override + public boolean addRole(SysRole sysRole) { + //生成用户编号 + int codeMax = 0; + DecimalFormat df = new DecimalFormat("000");//四位数字编号 + QueryWrapper queryWrapper = new QueryWrapper<>(); + List max = this.listObjs(queryWrapper.select("MAX(rolecode) " + + "rolecode"));// 查询最大的编号 + if (max.size() > 0) { + codeMax = Integer.parseInt(max.get(0).toString());//判断查询是否存在 + } + // 存在转换成int类型并给codeMax替换值 + String code = df.format(codeMax + 1); // 最大编号累加 + + sysRole.setRolecode(code); //添加角色编号 + if (StringUtils.isEmpty(sysRole.getIsvaild())) { + sysRole.setIsvaild("1"); //判断是否填写有效性 默认为 1 是 + } + sysRole.setLastmodifier(currentuser.getUsername()); //添加最近修改者 + sysRole.setLastmodifydate(new Timestamp(System.currentTimeMillis())); //添加最近修改时间 + return this.save(sysRole); + } + + /*********************************** + * 用途说明:删除角色用户(admin除外) + * 参数说明 + * id 系统角色用户对照表id + * 返回值说明: 是否新增成功 + ***********************************/ + @Override + public boolean deleteRoleUsers(String roleid, String urserids) { + boolean ok = true; + //得到单个用户id + String[] temp = urserids.split(","); + for (String userid : temp) { + //根据角色id、用户id删除 (登录账号admin除外) + ok = ok && roleMapper.deleteRoleUsers(roleid, userid); + + } + return ok; + } + + /*********************************** + * 用途说明:根据id删除角色 //待修改 + * 参数说明 + *id 角色id + * 返回值说明: 是否删除成功 + ***********************************/ + @Override + public void deleteById(String id) { + String[] ids = id.split(","); + for (String roleId : ids) { + //根据id删除 角色 + boolean isOk = this.removeById(roleId); + if (!isOk) { + continue; + } + roleMapper.deleteRoleMenus(roleId); + roleMapper.deleteRoleUser(roleId); + } + } + /* 原逻辑 + @Override + public boolean deleteById(String id) { + //根据角色id查询 所关联的用户 + List> isRoleUsersByroleid = + roleMapper.isRoleUsersByroleid(id); + //判断是否关联用户 + if (isRoleUsersByroleid.size() > 0) { + return false; + } + //根据id删除 角色 + boolean isOk = this.removeById(id); + if (isOk) { + return true; + } + return false; + }*/ + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + @Override + public List listRoleUsers(String orgid, String username, + String status, String level, + String rolename, String isvaild) { + return roleMapper.listRoleUsers(orgid, username, status, level, + rolename, isvaild); + } + + /*********************************** + * 用途说明:角色分配权限 + * 参数说明 + * id 角色id + * menuIds 权限id字符串 + * 返回值说明: 是否分配成功 + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setMenuById(String id, String menuIds) { + // 删除角色所对应的权限 + roleMapper.deleteRoleMenus(id); + // 重新赋予权限 + String[] ids = menuIds.split(","); + for (String menuId : ids) { + String uuid = IdUtil.fastSimpleUUID(); + roleMapper.addRoleMenu(uuid, id, menuId); + } + return true; + } + +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..dc28501 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,52 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.yfd.platform.datasource.DataSource; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysMenuMapper; +import com.yfd.platform.system.service.IUserService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + *

+ * 用户服务实现类 继承UserDetailsService 实现接口 + *

+ * + * @author zhengsl + * @since 2021-10-27 + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Resource + private IUserService userService; + + @Resource + private SysMenuMapper sysMenuMapper; + + @Override + @DataSource(name = "master") + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + //根据用户名称查询用户信息 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", username); + SysUser user = userService.getOne(queryWrapper); + if (ObjectUtil.isEmpty(user)) { + throw new RuntimeException("用户账号不存在!"); + } + //Todo 根据用户查询权限信息 添加到LoginUser中 + List permissions = + sysMenuMapper.selectPermsByUserId(user.getId()); + + //封装成UserDetails对象返回 + return new LoginUser(user,permissions); + } +} diff --git a/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java b/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..566d608 --- /dev/null +++ b/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java @@ -0,0 +1,550 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.FileProperties; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysOrganizationMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.FileUtil; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import com.yfd.platform.config.FileSpaceProperties; +import jakarta.annotation.Resource; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotBlank; +import java.io.File; +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 用户服务实现类 + *

+ * + * @author zhengsl + * @since 2021-10-27 + */ +@Service +@RequiredArgsConstructor +public class UserServiceImpl extends ServiceImpl implements IUserService { + + @Resource + private SysUserMapper sysUserMapper; + + @Resource + private SysRoleMapper sysRoleMapper; + + @Resource + private PasswordEncoder passwordEncoder; + /** + * 文件空间配置 + */ + @Resource + private FileSpaceProperties fileSpaceProperties; + + /********************************** + * 用途说明:获取当前用户账号及名称 + * 参数说明 + * 返回值说明: 系统管理员[admin] + ***********************************/ + @Override + public String getUsername() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + String acountname = + loginuser.getUser().getNickname(); + return acountname; + //return "admin"; + } + + @Override + public Map getNameInfo() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + String nickname = loginuser.getUser().getNickname(); + String username = loginuser.getUser().getUsername(); + Map map = new HashMap<>(); + map.put("nickname", nickname); + map.put("username", username); + return map; + } + + @Override + public SysUser getUserInfo() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + return loginuser.getUser(); + } + + @Override + public ResponseResult getLoginUserInfo() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + SysUser user = loginuser.getUser(); + //根据用户ID获取组织 + Map userInfo = + sysUserMapper.getOrganizationByid(user.getId()); + List roles = + sysRoleMapper.selectList(new QueryWrapper().inSql( + "id ", "SELECT roleid FROM sys_role_users ru WHERE ru" + + ".userid = '" + user.getId() + "'")); + List collect = + roles.stream().map(SysRole::getRolename).collect(Collectors.toList()); + ResponseResult responseResult = new ResponseResult(); + responseResult.put("userInfo", userInfo); + responseResult.put("roles", collect); + responseResult.put("permissions", loginuser.getPermissions()); + return responseResult; + } + + /*********************************** + * 用途说明:新增用户 + * 参数说明 + *sysUser 新增用户对象 + * id 创建者id + * roleId 角色id + * 返回值说明: 提示字符串 + ************************************/ + @Override + public Map addUser(SysUser sysUser, String roleids) { + //返回信息 + Map result = new HashMap<>(); + sysUser.setId(IdUtil.fastSimpleUUID()); + //普通用户 + sysUser.setUsertype(1); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + //设置缺省密码 + String cryptPassword = passwordEncoder.encode("123456"); + sysUser.setPassword(cryptPassword); + sysUser.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + sysUser.setLastmodifier(getUsername()); + //账号有效 + sysUser.setStatus(1); + //判断注册的登录账号是否存在 + if (isExistAccount(sysUser.getUsername())) { + //新增用户 + boolean ok = this.save(sysUser); + //新增用户分配权限 + if (StrUtil.isNotEmpty(roleids)) { + String[] roles = roleids.split(","); + for (String roleid : roles) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + ok = ok && sysUserMapper.addUserRoles(id, roleid, + sysUser.getId()); + } + } + //判断新增是否成功 消息提示 + if (ok) { + result.put("status", "sucess"); + result.put("msg", "新增用户成功!"); + + } else { + result.put("status", "error"); + result.put("msg", "新增用户失败!"); + } + } else { + result.put("status", "error"); + result.put("msg", "用户账号已存在,不能重复添加!"); + } + return result; + } + + /*********************************** + * 用途说明:查询系统用户 + * 参数说明 + *page 分页集合参数 + *orgid 所属组织 + *username 用户名称 + * mobile 手机号 + * status 状态 + * 返回值说明: 用户分页集合 + ************************************/ + @Override + public List list(String total, String size, String orgid, + String username, String mobile, String status) { + List list = sysUserMapper.list(total, size, orgid, username, + mobile, status); + for (Map map : list) { + List mapList = + sysUserMapper.getLevel(map.get("id").toString()); + String roleid = ""; + String level = ""; + String rolename = ""; + for (Map map1 : mapList) { + roleid += map1.get("id") + ","; + level += map1.get("level") + ","; + rolename += map1.get("rolename") + ","; + } + if (roleid.endsWith(",")) { + roleid = roleid.substring(0, roleid.length() - 1); + } + if (level.endsWith(",")) { + level = level.substring(0, level.length() - 1); + } + if (rolename.endsWith(",")) { + rolename = rolename.substring(0, rolename.length() - 1); + } + + map.put("roleid", roleid); + map.put("level", level); + map.put("rolename", rolename); + } + return list; //返回分页集合 + } + + /*********************************** + * 用途说明:根据ID修改用户 + * 参数说明 + *sysUser 用户对象 + *roleids 角色id + * 返回值说明: 是否更新成功 + ************************************/ + @Override + public Map updateById(SysUser sysUser, String roleids) { + //返回信息 + Map result = new HashMap<>(); + //获取当前用户 最近修改者替换 + sysUser.setLastmodifier(getUsername()); + //获取当前时间 最近修改日期替换 + sysUser.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //根据修改 + boolean ok = this.updateById(sysUser); + if (ok) { + if (StrUtil.isNotEmpty(roleids)) { + String[] roles = roleids.split(","); + List list = sysUserMapper.getRoleid(sysUser.getId()); + for (String role : roles) { + if (!list.contains(role)) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + ok = ok && sysUserMapper.addUserRoles(id, role, + sysUser.getId()); + } + } + //删除不包含的角色 + sysUserMapper.delInRoleUsersByUserid(sysUser.getId(), roles); + + } else { + //根据用户id 删除该用户角色关联 + ok = ok && sysUserMapper.delRoleUsersByUserid(sysUser.getId()); + } + result.put("status", "sucess"); + result.put("msg", "用户信息修改成功!"); + } else { + result.put("status", "error"); + result.put("msg", "用户信息修改失败!"); + } + + return result; + } + + @Override + public Map getOneById(String id) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + Map map = this.getMap(queryWrapper.eq("id", id)); + List mapList = sysUserMapper.getLevel(id); + String roleid = ""; + String level = ""; + String rolename = ""; + for (Map map1 : mapList) { + roleid += map1.get("id") + ","; + level += map1.get("level") + ","; + rolename += map1.get("rolename") + ","; + } + if (roleid.endsWith(",")) { + roleid = roleid.substring(0, roleid.length() - 1); + } + if (level.endsWith(",")) { + level = level.substring(0, level.length() - 1); + } + if (rolename.endsWith(",")) { + rolename = rolename.substring(0, rolename.length() - 1); + } + + map.put("roleid", roleid); + map.put("level", level); + map.put("rolename", rolename); + return map; + } + + /*********************************** + * 用途说明:用户分配角色 + * 参数说明 + *listId 用户id与角色id + * 返回值说明: 判断是否添加成功 + ************************************/ + @Override + public boolean setUserRoles(String roleid, String userids) { + boolean isOk = true; + //拆分userid 数组 + String[] temp = userids.split(","); + //遍历userid + for (String userid : temp) { + //根据角色id与用户id查询 + List list = sysUserMapper.getRoleUsersByid(roleid, userid); + //判断是否用户已分配此权限 + if (list.size() == 0) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + isOk = isOk && sysUserMapper.addUserRoles(id, roleid, userid); + } + } + return isOk; + } + + /*********************************** + * 用途说明:根据id删除用户 + * 参数说明 + *id 用户id + * 返回值说明: 判断是否删除成功 + ************************************/ + @Override + public boolean deleteById(String id) { + //根据id查询 + SysUser sysUser = this.getById(id); + //账号头像存储地址 + String imgName = + fileSpaceProperties.getSystem() + File.separator + "user" + File.separator + sysUser.getAvatar(); + if ("admin".equals(sysUser.getUsername())) { + return false; + } else { + boolean isOk = this.removeById(id); + //判断是否删除成功 + if (isOk) { + //根据用户id 删除该用户角色关联 + sysUserMapper.delRoleUsersByUserid(id); + //判断是否存在 账号头像 存在删除 + if (StrUtil.isNotEmpty(sysUser.getAvatar())) { + FileUtil.del(imgName); + } + return false; + } else { + return false; + } + } + } + + /*********************************** + * 用途说明:重置用户密码(管理员) + * 参数说明 + *id 重置密码的 用户id + * 返回值说明: 判断是重置成功 + ************************************/ + @Override + public boolean resetPassword(String id) throws Exception { + boolean isOk = false; + //根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id + String level = sysUserMapper.getMaxLevel(id); + //判断是否获取级别 + if (StrUtil.isNotEmpty(level)) { + //判断当前用户级别 管理员及以上权限 + if (Integer.parseInt(level) <= 2) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 修改密码,密码修改时间,最近修改者,最近修改日期 将密码修改为 123456 + String cryptPassword = passwordEncoder.encode("123456"); + updateWrapper.eq("id", id).set("password", cryptPassword).set( + "pwdresettime", + new Timestamp(System.currentTimeMillis())).set( + "lastmodifydate", + new Timestamp(System.currentTimeMillis())).set( + "lastmodifier", getUsername()); + //是否修改成功 + isOk = this.update(updateWrapper); + } + } + return isOk; + } + + /*********************************** + * 用途说明:设置账号状态(管理员) + * 参数说明 + *id 用户id + * status 设置状态 + * 返回值说明: 判断是否设置成功 + ************************************/ + @Override + public boolean setStatus(String id, String status) { + boolean isOk = false; + //根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id + String level = sysUserMapper.getMaxLevel(id); + //判断当前用户级别 管理员及以上权限 + if (Integer.parseInt(level) <= 2) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id修改用户状态,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("status", status).set( + "lastmodifydate", + new Timestamp(System.currentTimeMillis())).set( + "lastmodifier", getUsername()); + //是否修改成功 + isOk = this.update(updateWrapper); + } + return isOk; + } + + /*********************************** + * 用途说明:上传用户头像 + * 参数说明 + * id 用户id + * img 账号头像 + * 返回值说明: 判断是否上传 + ***********************************/ + @Override + public boolean uploadAvatar(String id, MultipartFile img) { + //根据id查询 + SysUser sysUser = this.getById(id); + //账号头像存储地址 + String imgPath = fileSpaceProperties.getSystem() + "user"; + String avatar = sysUser.getAvatar(); + if (StrUtil.isNotBlank(avatar)) { + String imgName = imgPath + File.separator + avatar; + FileUtil.del(imgName); + } + //上传图片 并获取图片名称 (图片格式修改成png) + String imgName = FileUtil.upload(img, imgPath, + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(img.getOriginalFilename())).getName(); + //修改 账户头像 + sysUser.setAvatar(imgName); + //修改 最近修改者 + sysUser.setLastmodifier(getUsername()); + //修改 最近修改日期 + sysUser.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //更新用户表 + boolean isOk = this.updateById(sysUser); + return isOk; + } + + /*********************************** + * 用途说明:新增系统角色用户对照表 对用户分配角色(单个) + * 参数说明 + * id 生成的id + * roleid 角色id + * userid 用户id + * 返回值说明: + ************************************/ + @Override + public boolean addUserRoles(String roleid, String userid) { + boolean isOk = true; + //根据角色id与用户id查询 + List list = sysUserMapper.getRoleUsersByid(roleid, userid); + //判断是否用户已分配此权限 + if (list.size() == 0) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + isOk = sysUserMapper.addUserRoles(id, roleid, userid); + } + return isOk; + } + + /* @Override + public Page queryUsers(String orgid, + String username, Page page) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 分页查询中的条件查询 + if (StrUtil.isNotBlank(username)) { + queryWrapper.like(SysUser::getUsername, username); + } + queryWrapper.eq(SysUser::getOrgid, orgid); + return sysUserMapper.selectPage(page, queryWrapper); + }*/ + + @Override + public Page> queryUsers(String orgid, + String username, + Page page) { + Page> mapPage = sysUserMapper.queryUsers(orgid, + username, page); + List> list = new ArrayList<>(); + List> records = mapPage.getRecords(); + for (Map record : records) { + String id = (String) record.get("id"); + List sysRoles = sysRoleMapper.getRoleByUserId(id); + record.put("roles", sysRoles); + list.add(record); + } + mapPage.setRecords(list); + return mapPage; + } + + /*********************************** + * 用途说明:根据ID批量删除用户 + * 参数说明 + *ids 用户id集合 + * 返回值说明: 判断是否删除成功 + ************************************/ + @Override + public boolean deleteUserByIds(String id) { + String[] splitId = id.split(","); + List ids = Arrays.asList(splitId); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysUser::getId, ids); + List sysUsers = sysUserMapper.selectList(queryWrapper); + List names = + sysUsers.stream().map(SysUser::getUsername).collect(Collectors.toList()); + if (names.contains("admin")) { + return false; + } else { + int result = sysUserMapper.deleteBatchIds(ids); + if (result <= 0) { + return false; + } + // 根据ID删除用户与角色的关联信息 + sysUserMapper.delRoleUsersByUserIds(ids); + List avatars = + sysUsers.stream().map(SysUser::getAvatar).collect(Collectors.toList()); + if (avatars.size() > 0) { + for (String avatar : avatars) { + //账号头像存储地址 + String imgName = + fileSpaceProperties.getSystem() + File.separator + "user" + File.separator + avatar; + FileUtil.del(imgName); + } + } + return true; + } + } + + /*********************************** + * 用途说明:比较登录名称是否有重复 + * 参数说明 + * account 登录名称 + * 返回值说明: 重复返回 false 否则返回 true + ************************************/ + private boolean isExistAccount(String username) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (this.list(queryWrapper.eq("username", username)).size() > 0) { + //判断 查询登录账号 结果集是否为null 重复返回 false 否则返回 tree + return false; + } else { + return true; + } + } +} diff --git a/src/main/java/com/yfd/platform/task/TaskMessage.java b/src/main/java/com/yfd/platform/task/TaskMessage.java new file mode 100644 index 0000000..4a43d15 --- /dev/null +++ b/src/main/java/com/yfd/platform/task/TaskMessage.java @@ -0,0 +1,57 @@ +package com.yfd.platform.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.config.MessageConfig; +import com.yfd.platform.config.WebConfig; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.util.List; + +/** + * @author TangWei + * @Date: 2023/3/22 15:39 + * @Description: + */ +@Component +public class TaskMessage { + + @Resource + private IMessageService messageService; + + /********************************** + * 用途说明: 定时监控消息是否过期 + * 参数说明 + * 返回值说明: void + ***********************************/ + public void examineMessage() { + List list = + messageService.list(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + for (Message message : list) { + Timestamp createtime = message.getCreatetime(); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + long create = createtime.getTime(); + long now = timestamp.getTime(); + Integer validperiod = message.getValidperiod(); + long v = validperiod * 60 * 60 * 1000; + if ((now - create) > v) { + message.setStatus("9"); + message.setReadtime(timestamp); + messageService.updateById(message); + } + + } + } + + /*public void sendMessage() { + String loginToken = webConfig.loginuserCache().get("loginToken"); + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + ServerSendEventServer.sendMessage(loginToken, count + ""); + }*/ +} diff --git a/src/main/java/com/yfd/platform/utils/CallBack.java b/src/main/java/com/yfd/platform/utils/CallBack.java new file mode 100644 index 0000000..5f13d70 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/CallBack.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yfd.platform.utils; + +/** + * @author: liaojinlong + * @date: 2020/6/9 17:02 + * @since: 1.0 + * @see {@link SpringContextHolder} + * 针对某些初始化方法,在SpringContextHolder 初始化前时,
+ * 可提交一个 提交回调任务。
+ * 在SpringContextHolder 初始化后,进行回调使用 + */ + +public interface CallBack { + /** + * 回调执行方法 + */ + void executor(); + + /** + * 本回调任务名称 + * @return / + */ + default String getCallBackName() { + return Thread.currentThread().getId() + ":" + this.getClass().getName(); + } +} + diff --git a/src/main/java/com/yfd/platform/utils/CodeGenerator.java b/src/main/java/com/yfd/platform/utils/CodeGenerator.java new file mode 100644 index 0000000..1c65429 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/CodeGenerator.java @@ -0,0 +1,75 @@ +package com.yfd.platform.utils; + +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中 +public class CodeGenerator { + + public static String scanner(String tip) { + Scanner scanner = new Scanner(System.in); + StringBuilder help = new StringBuilder(); + help.append("请输入" + tip + ":"); + System.out.println(help.toString()); + if (scanner.hasNext()) { + String ipt = scanner.next(); + if (ipt != null && !ipt.trim().isEmpty()) { + return ipt; + } + } + throw new RuntimeException("请输入正确的" + tip + "!"); + } + + public static void main(String[] args) { + String projectPath = System.getProperty("user.dir"); + String module = scanner("模块名称"); + + Map pathInfo = new HashMap<>(); + pathInfo.put(OutputFile.entity, projectPath + "/src/main/java/com/yfd/platform/" + module + "/domain"); + pathInfo.put(OutputFile.mapper, projectPath + "/src/main/java/com/yfd/platform/" + module + "/mapper"); + pathInfo.put(OutputFile.controller, projectPath + "/src/main/java/com/yfd/platform/" + module + "/controller"); + pathInfo.put(OutputFile.serviceImpl, projectPath + "/src/main/java/com/yfd/platform/" + module + "/service/impl"); + pathInfo.put(OutputFile.service, projectPath + "/src/main/java/com/yfd/platform/" + module + "/service"); + pathInfo.put(OutputFile.xml, projectPath + "/src/main/resources/mapper/" + module); + + FastAutoGenerator.create( + "jdbc:mysql://43.143.220.7:3306/frameworkdb2023?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai", + "root", + "zhengg7QkXa { + builder.author("TangWei") + .disableOpenDir() + .outputDir(projectPath + "/src/main/java"); + }) + .packageConfig(builder -> { + builder.parent("com.yfd.platform") + .moduleName(module) + .pathInfo(pathInfo); + }) + .strategyConfig(builder -> { + builder.addInclude(scanner("表名,多个英文逗号分割").split(",")) + .entityBuilder() + .enableLombok() + .naming(NamingStrategy.underline_to_camel) + .columnNaming(NamingStrategy.underline_to_camel) + .controllerBuilder() + .enableRestStyle() + .mapperBuilder() + .formatMapperFileName("%sMapper") + .serviceBuilder() + .formatServiceFileName("%sService") + .formatServiceImplFileName("%sServiceImpl") + .controllerBuilder() + .formatFileName("%sController"); + }) + .templateEngine(new FreemarkerTemplateEngine()) + .execute(); + } + +} diff --git a/src/main/java/com/yfd/platform/utils/EncryptConfigUtil.java b/src/main/java/com/yfd/platform/utils/EncryptConfigUtil.java new file mode 100644 index 0000000..4df5527 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/EncryptConfigUtil.java @@ -0,0 +1,28 @@ +package com.yfd.platform.utils; + +import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; +import org.jasypt.util.text.BasicTextEncryptor; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class EncryptConfigUtil { + + public static void main(String[] args) { + +// String salt = "rca20230101"; +// String password = "123456"; +// +// BasicTextEncryptor textEncryptor = new BasicTextEncryptor(); +// //加密所需的salt +// textEncryptor.setPassword(salt); +// //要加密的数据(数据库的用户名或密码) +// String encrypt = textEncryptor.encrypt(password); +// System.out.println("password:"+encrypt); + + + BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder(); + String cryptPassword=passwordEncoder.encode("dl_2023");//设置缺省密码 + + } + +} \ No newline at end of file diff --git a/src/main/java/com/yfd/platform/utils/EncryptUtils.java b/src/main/java/com/yfd/platform/utils/EncryptUtils.java new file mode 100644 index 0000000..2ae2b26 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/EncryptUtils.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.nio.charset.StandardCharsets; + +/** + * 加密 + * @author + * @date 2018-11-23 + */ + +public class EncryptUtils { + + private static final String STR_PARAM = "Passw0rd"; + + private static Cipher cipher; + + private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8)); + + private static DESKeySpec getDesKeySpec(String source) throws Exception { + if (source == null || source.length() == 0){ + return null; + } + cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + String strKey = "Passw0rd"; + return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 对称加密 + */ + public static String desEncrypt(String source) throws Exception { + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV); + return byte2hex( + cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase(); + } + + /** + * 对称解密 + */ + public static String desDecrypt(String source) throws Exception { + byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8)); + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.DECRYPT_MODE, secretKey, IV); + byte[] retByte = cipher.doFinal(src); + return new String(retByte); + } + + private static String byte2hex(byte[] inStr) { + String stmp; + StringBuilder out = new StringBuilder(inStr.length * 2); + for (byte b : inStr) { + stmp = Integer.toHexString(b & 0xFF); + if (stmp.length() == 1) { + // 如果是0至F的单位字符串,则添加0 + out.append("0").append(stmp); + } else { + out.append(stmp); + } + } + return out.toString(); + } + + private static byte[] hex2byte(byte[] b) { + int size = 2; + if ((b.length % size) != 0){ + throw new IllegalArgumentException("长度不是偶数"); + } + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += size) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } +} diff --git a/src/main/java/com/yfd/platform/utils/ExecutionJob.java b/src/main/java/com/yfd/platform/utils/ExecutionJob.java new file mode 100644 index 0000000..c4a2fcf --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/ExecutionJob.java @@ -0,0 +1,103 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import com.yfd.platform.config.MessageConfig; +import com.yfd.platform.config.thread.ThreadPoolExecutorUtil; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.IQuartzJobService; +import org.quartz.JobExecutionContext; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.sql.Timestamp; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 参考人人开源,https://gitee.com/renrenio/renren-security + * + * @author / + * @date 2019-01-07 + */ +@Async +@SuppressWarnings({"unchecked", "all"}) +public class ExecutionJob extends QuartzJobBean { + + /** + * 该处仅供参考 + */ + private final static ThreadPoolExecutor EXECUTOR = + ThreadPoolExecutorUtil.getPoll(); + + @Resource + private IMessageService messageService; + + @Resource + private MessageConfig messageConfig; + + @Override + public void executeInternal(JobExecutionContext context) { + QuartzJob quartzJob = + (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); + // 获取spring bean + IQuartzJobService quartzJobService = + SpringContextHolder.getBean(IQuartzJobService.class); + String uuid = quartzJob.getId(); + long startTime = System.currentTimeMillis(); + String jobName = quartzJob.getJobName(); + try { + // 执行任务 + System.out.println( + "--------------------------------------------------------------"); + System.out.println("任务开始执行,任务名称:" + jobName); + QuartzRunnable task = new QuartzRunnable(quartzJob.getJobClass(), + quartzJob.getJobMethod(), + quartzJob.getJobParams()); + Future future = EXECUTOR.submit(task); + future.get(); + long times = System.currentTimeMillis() - startTime; + Message message = new Message(); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + message.setType("1"); + message.setTitle(quartzJob.getJobName()); + message.setContent(quartzJob.getDescription()); + message.setSenderName("定时器"); + message.setReceiverCodes(quartzJob.getOrderno().toString()); + message.setReceiverNames(""); + message.setStatus("1"); + message.setValidperiod(24); + messageConfig.addMessage(message); + // 任务状态 + System.out.println("任务执行完毕,任务名称:" + jobName + ", " + + "执行时间:" + times + "毫秒"); + System.out.println( + "--------------------------------------------------------------"); + } catch (Exception e) { + System.out.println("任务执行失败,任务名称:" + jobName); + System.out.println( + "--------------------------------------------------------------"); + quartzJob.setStatus("0"); + //更新状态 + quartzJobService.updateById(quartzJob); + } + } + +} diff --git a/src/main/java/com/yfd/platform/utils/FileUtil.java b/src/main/java/com/yfd/platform/utils/FileUtil.java new file mode 100644 index 0000000..6940aee --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/FileUtil.java @@ -0,0 +1,398 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import com.yfd.platform.exception.BadRequestException; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.tomcat.util.http.fileupload.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLDecoder; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * File工具类,扩展 hutool 工具包 + * + * @author + * @date 2018-12-27 + */ +public class FileUtil extends cn.hutool.core.io.FileUtil { + + private static final Logger log = LoggerFactory.getLogger(FileUtil.class); + + /** + * 系统临时目录 + *
+ * windows 包含路径分割符,但Linux 不包含, + * 在windows \\==\ 前提下, + * 为安全起见 同意拼装 路径分割符, + *
+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * 
+ */ + public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; + /** + * 定义GB的计算常量 + */ + private static final int GB = 1024 * 1024 * 1024; + /** + * 定义MB的计算常量 + */ + private static final int MB = 1024 * 1024; + /** + * 定义KB的计算常量 + */ + private static final int KB = 1024; + + /** + * 格式化小数 + */ + private static final DecimalFormat DF = new DecimalFormat("0.00"); + + public static final String IMAGE = "image"; + public static final String TXT = "document"; + public static final String MUSIC = "music"; + public static final String VIDEO = "video"; + public static final String OTHER = "other"; + + + /** + * MultipartFile转File + */ + public static File toFile(MultipartFile multipartFile) { + // 获取文件名 + String fileName = multipartFile.getOriginalFilename(); + // 获取文件后缀 + String prefix = "." + getExtensionName(fileName); + File file = null; + try { + // 用uuid作为文件名,防止生成的临时文件重复 + file = File.createTempFile(IdUtil.simpleUUID(), prefix); + // MultipartFile to File + multipartFile.transferTo(file); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return file; + } + + /** + * 获取文件扩展名,不带 . + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length()))) { + return filename.substring(0, dot); + } + } + return filename; + } + + /** + * 文件大小转换 + */ + public static String getSize(long size) { + String resultSize; + if (size / GB >= 1) { + //如果当前Byte的值大于等于1GB + resultSize = DF.format(size / (float) GB) + "GB "; + } else if (size / MB >= 1) { + //如果当前Byte的值大于等于1MB + resultSize = DF.format(size / (float) MB) + "MB "; + } else if (size / KB >= 1) { + //如果当前Byte的值大于等于1KB + resultSize = DF.format(size / (float) KB) + "KB "; + } else { + resultSize = size + "B "; + } + return resultSize; + } + + /** + * inputStream 转 File + */ + static File inputStreamToFile(InputStream ins, String name) throws Exception { + File file = new File(SYS_TEM_DIR + name); + if (file.exists()) { + return file; + } + OutputStream os = new FileOutputStream(file); + int bytesRead; + int len = 8192; + byte[] buffer = new byte[len]; + while ((bytesRead = ins.read(buffer, 0, len)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.close(); + ins.close(); + return file; + } + + /** + * 将文件名解析成文件的上传路径 + */ + public static File upload(MultipartFile file, String filePath) { + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); + String name = getFileNameNoEx(file.getOriginalFilename()); + String suffix = getExtensionName(file.getOriginalFilename()); + String nowStr = "-" + format.format(date); + try { + String fileName = name + "." + suffix; + String path = filePath +File.separator + fileName; + // getCanonicalFile 可解析正确各种路径 + File dest = new File(path).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + System.out.println("was not successful."); + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 将文件名解析成文件的上传路径 + * file 上传的文件 + * filePath 存储路径 + * tofilename 保存文件名称 + + */ + public static File upload(MultipartFile file, String filePath,String tofilename) { + try { + String filename = filePath + File.separator + tofilename; + File dest = new File(filename).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + + /** + * 导出excel + */ + public static void downloadExcel(List> list, HttpServletResponse response) throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + String filename = "record"+cn.hutool.core.date.DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"); + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(list, true); + SXSSFSheet sheet = (SXSSFSheet)writer.getSheet(); + //上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法 + sheet.trackAllColumnsForAutoSizing(); + //列宽自适应 + writer.autoSizeColumnAll(); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename="+filename+".xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + public static String getFileType(String type) { + String documents = "txt doc pdf ppt pps xlsx xls docx"; + String music = "mp3 wav wma mpa ram ra aac aif m4a"; + String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; + String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; + if (image.contains(type)) { + return IMAGE; + } else if (documents.contains(type)) { + return TXT; + } else if (music.contains(type)) { + return MUSIC; + } else if (video.contains(type)) { + return VIDEO; + } else { + return OTHER; + } + } + + public static void checkSize(long maxSize, long size) { + // 1M + int len = 1024 * 1024; + if (size > (maxSize * len)) { + throw new BadRequestException("文件超出规定大小"); + } + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(File file1, File file2) { + String img1Md5 = getMd5(file1); + String img2Md5 = getMd5(file2); + return img1Md5.equals(img2Md5); + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(String file1Md5, String file2Md5) { + return file1Md5.equals(file2Md5); + } + + private static byte[] getByte(File file) { + // 得到文件长度 + byte[] b = new byte[(int) file.length()]; + try { + InputStream in = new FileInputStream(file); + try { + System.out.println(in.read(b)); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } catch (FileNotFoundException e) { + log.error(e.getMessage(), e); + return null; + } + return b; + } + + private static String getMd5(byte[] bytes) { + // 16进制字符 + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char[] str = new char[j * 2]; + int k = 0; + // 移位 输出字符串 + for (byte byte0 : md) { + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 下载文件 + * + * @param request / + * @param response / + * @param file / + */ + public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentType("application/octet-stream"); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + IOUtils.copy(fis, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (fis != null) { + try { + fis.close(); + if (deleteOnExit) { + file.deleteOnExit(); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + } + /** + * 预览PDF文件 + * + * @param filepath / + * @param response / + */ + public static void viewPDF(String filepath, HttpServletResponse response) throws IOException { + File file=new File(filepath); + String originFileName=file.getName(); //中文编码 + response.setCharacterEncoding("UTF-8"); + String showName= StrUtil.isNotBlank(originFileName)?originFileName:file.getName(); + showName= URLDecoder.decode(showName,"UTF-8"); + response.setHeader("Content-Disposition","inline;fileName="+new String(showName.getBytes(), "ISO8859-1")+";fileName*=UTF-8''"+ new String(showName.getBytes(), "ISO8859-1")); + FileInputStream fis = new FileInputStream(file); + response.setHeader("content-type", "application/pdf"); + response.setContentType("application/pdf; charset=utf-8"); + IOUtils.copy(fis, response.getOutputStream()); + fis.close(); + } + + public static String getMd5(File file) { + return getMd5(getByte(file)); + } + +} diff --git a/src/main/java/com/yfd/platform/utils/MpGenerator.java b/src/main/java/com/yfd/platform/utils/MpGenerator.java new file mode 100644 index 0000000..e3aec6e --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/MpGenerator.java @@ -0,0 +1,79 @@ +package com.yfd.platform.utils; + +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +public class MpGenerator { + /** + * 读取控制台内容 + */ + public static String scanner(String tip) { + Scanner scanner = new Scanner(System.in); + StringBuilder help = new StringBuilder(); + help.append("请输入" + tip + ":"); + System.out.println(help.toString()); + if (scanner.hasNext()) { + String ipt = scanner.next(); + if (StringUtils.hasText(ipt)) { + return ipt; + } + } + throw new RuntimeException("请输入正确的" + tip + "!"); + } + + public static void main(String[] args) { + String projectPath = System.getProperty("user.dir"); + String url = PropertiesUtils.getPropertyField("spring.datasource.url"); + String username = PropertiesUtils.getPropertyField("spring.datasource.username"); + String password = PropertiesUtils.getPropertyField("spring.datasource.password"); + + String moduleName = scanner("模块名"); + String modulePath = moduleName.replace(".", "/"); + + Map pathInfo = new HashMap<>(); + pathInfo.put(OutputFile.entity, projectPath + "/src/main/java/com/yfd/platform/modules/domain" + modulePath + "/entity"); + pathInfo.put(OutputFile.mapper, projectPath + "/src/main/java/com/yfd/platform/modules/domain/" + modulePath + "/dao"); + pathInfo.put(OutputFile.controller, projectPath + "/src/main/java/com/yfd/platform/modules/domain" + modulePath + "/controller"); + pathInfo.put(OutputFile.service, projectPath + "/src/main/java/com/yfd/platform/modules/domain" + modulePath + "/service"); + pathInfo.put(OutputFile.serviceImpl, projectPath + "/src/main/java/com/yfd/platform/modules/domain" + modulePath + "/service/impl"); + pathInfo.put(OutputFile.xml, projectPath + "/src/main/resources/mapper/" + modulePath); + + FastAutoGenerator.create(url, username, password) + .globalConfig(builder -> { + builder.author("fwh") + .disableOpenDir() + .outputDir(projectPath + "/src/main/java"); + }) + .packageConfig(builder -> { + builder.parent(PropertiesUtils.getPropertyField("project.package.name")) + .moduleName(moduleName) + .pathInfo(pathInfo); + }) + .strategyConfig(builder -> { + builder.addInclude(scanner("表名,多个英文逗号分割").split(",")) + .entityBuilder() + .enableLombok() + .naming(NamingStrategy.underline_to_camel) + .columnNaming(NamingStrategy.underline_to_camel) + .controllerBuilder() + .enableRestStyle() + .mapperBuilder() + .formatMapperFileName("%sDao") + .serviceBuilder() + .formatServiceFileName("%sService") + .formatServiceImplFileName("%sServiceImpl") + .controllerBuilder() + .formatFileName("%sController"); + }) + .templateEngine(new FreemarkerTemplateEngine()) + .execute(); + } +} + diff --git a/src/main/java/com/yfd/platform/utils/PropertiesUtils.java b/src/main/java/com/yfd/platform/utils/PropertiesUtils.java new file mode 100644 index 0000000..93c7cfd --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/PropertiesUtils.java @@ -0,0 +1,29 @@ +package com.yfd.platform.utils; + +import org.springframework.core.io.ClassPathResource; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Properties; +/****************************** + * 用途说明: + * 作者姓名: pcj + * 创建时间: 2022/9/20 14:31 + ******************************/ +public class PropertiesUtils { + public final static String RESOURCE_PATH = "application.properties"; + + public final static Properties properties = new Properties(); + + public static String getPropertyField(String parameter) { + //对应resources目录下的资源路径 + ClassPathResource resource = new ClassPathResource(RESOURCE_PATH); + try { + properties.load(new InputStreamReader(resource.getInputStream(), "gbk")); + } catch (IOException e) { + throw new RuntimeException(e); + } + return properties.getProperty(parameter); + } + +} diff --git a/src/main/java/com/yfd/platform/utils/QuartzManage.java b/src/main/java/com/yfd/platform/utils/QuartzManage.java new file mode 100644 index 0000000..3ff265c --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/QuartzManage.java @@ -0,0 +1,187 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import com.yfd.platform.system.domain.QuartzJob; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.Date; + +import static org.quartz.TriggerBuilder.newTrigger; + +/** + * @author + * @date 2019-01-07 + */ +@Slf4j +@Component +public class QuartzManage { + + private static final String JOB_NAME = "TASK_"; + + @Resource(name = "scheduler") + private Scheduler scheduler; + + public void addJob(QuartzJob quartzJob) { + try { + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). + withIdentity(JOB_NAME + quartzJob.getId()).build(); + + //通过触发器名和cron 表达式创建 Trigger + Trigger cronTrigger = newTrigger() + .withIdentity(JOB_NAME + quartzJob.getId()) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getJobCron())) + .build(); + + cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); + + //重置启动时间 + ((CronTriggerImpl) cronTrigger).setStartTime(new Date()); + + //执行定时任务 + scheduler.scheduleJob(jobDetail, cronTrigger); + + // 暂停任务 + if ("0".equals(quartzJob.getStatus())) { + pauseJob(quartzJob); + } + } catch (Exception e) { + log.error("创建定时任务失败", e); + throw new RuntimeException("创建定时任务失败"); + } + } + + /** + * 更新job cron表达式 + * + * @param quartzJob / + */ + public void updateJobCron(QuartzJob quartzJob) { + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = + (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(quartzJob); + trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + } + CronScheduleBuilder scheduleBuilder = + CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()); + trigger = + trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + //重置启动时间 + ((CronTriggerImpl) trigger).setStartTime(new Date()); + trigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); + + scheduler.rescheduleJob(triggerKey, trigger); + // 暂停任务 + if ("0".equals(quartzJob.getStatus())) { + pauseJob(quartzJob); + } + } catch (Exception e) { + log.error("更新定时任务失败", e); + throw new RuntimeException("更新定时任务失败"); + } + + } + + /** + * 删除一个job + * + * @param quartzJob / + */ + public void deleteJob(QuartzJob quartzJob) { + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + scheduler.deleteJob(jobKey); + } catch (Exception e) { + log.error("删除定时任务失败", e); + throw new RuntimeException("删除定时任务失败"); + } + } + + /** + * 恢复一个job + * + * @param quartzJob / + */ + public void resumeJob(QuartzJob quartzJob) { + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = + (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(quartzJob); + } + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.resumeJob(jobKey); + } catch (Exception e) { + log.error("恢复定时任务失败", e); + throw new RuntimeException("恢复定时任务失败"); + } + } + + /** + * 立即执行job + * + * @param quartzJob / + */ + public void runJobNow(QuartzJob quartzJob) { + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = + (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(quartzJob); + } + JobDataMap dataMap = new JobDataMap(); + dataMap.put(QuartzJob.JOB_KEY, quartzJob); + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.triggerJob(jobKey, dataMap); + } catch (Exception e) { + log.error("定时任务执行失败", e); + throw new RuntimeException("定时任务执行失败"); + } + } + + /** + * 暂停一个job + * + * @param quartzJob / + */ + public void pauseJob(QuartzJob quartzJob) { + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + } catch (Exception e) { + log.error("定时任务暂停失败", e); + throw new RuntimeException("定时任务暂停失败"); + } + } +} diff --git a/src/main/java/com/yfd/platform/utils/QuartzRunnable.java b/src/main/java/com/yfd/platform/utils/QuartzRunnable.java new file mode 100644 index 0000000..b1da541 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/QuartzRunnable.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +/** + * 执行定时任务 + * @author / + */ +@Slf4j +public class QuartzRunnable implements Callable { + + private final Object target; + private final Method method; + private final String params; + + QuartzRunnable(String beanName, String methodName, String params) + throws NoSuchMethodException, SecurityException { + this.target = SpringContextHolder.getBean(beanName); + this.params = params; + + if (StringUtils.isNotBlank(params)) { + this.method = target.getClass().getDeclaredMethod(methodName, String.class); + } else { + this.method = target.getClass().getDeclaredMethod(methodName); + } + } + + @Override + public Object call() throws Exception { + ReflectionUtils.makeAccessible(method); + if (StringUtils.isNotBlank(params)) { + method.invoke(target, params); + } else { + method.invoke(target); + } + return null; + } +} diff --git a/src/main/java/com/yfd/platform/utils/RequestHolder.java b/src/main/java/com/yfd/platform/utils/RequestHolder.java new file mode 100644 index 0000000..3dbfa55 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/RequestHolder.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Objects; + +/** + * 获取 HttpServletRequest + * @author + * @date 2018-11-24 + */ +public class RequestHolder { + + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } +} diff --git a/src/main/java/com/yfd/platform/utils/RsaUtils.java b/src/main/java/com/yfd/platform/utils/RsaUtils.java new file mode 100644 index 0000000..6638ac7 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/RsaUtils.java @@ -0,0 +1,181 @@ +package com.yfd.platform.utils; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * @author https://www.cnblogs.com/nihaorz/p/10690643.html + * @description Rsa 工具类,公钥私钥生成,加解密 + * @date 2020-05-18 + **/ +public class RsaUtils { + + private static final String SRC = "123456"; + + public static void main(String[] args) throws Exception { + System.out.println("\n"); + RsaKeyPair keyPair = generateKeyPair(); + System.out.println("公钥:" + keyPair.getPublicKey()); + System.out.println("私钥:" + keyPair.getPrivateKey()); + System.out.println("\n"); + test1(keyPair); + System.out.println("\n"); + test2(keyPair); + System.out.println("\n"); + } + + /** + * 公钥加密私钥解密 + */ + private static void test1(RsaKeyPair keyPair) throws Exception { + System.out.println("***************** 公钥加密私钥解密开始 *****************"); + String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC); + String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1); + System.out.println("加密前:" + RsaUtils.SRC); + System.out.println("加密后:" + text1); + System.out.println("解密后:" + text2); + if (RsaUtils.SRC.equals(text2)) { + System.out.println("解密字符串和原始字符串一致,解密成功"); + } else { + System.out.println("解密字符串和原始字符串不一致,解密失败"); + } + System.out.println("***************** 公钥加密私钥解密结束 *****************"); + } + + /** + * 私钥加密公钥解密 + * @throws Exception / + */ + private static void test2(RsaKeyPair keyPair) throws Exception { + System.out.println("***************** 私钥加密公钥解密开始 *****************"); + String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC); + String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1); + System.out.println("加密前:" + RsaUtils.SRC); + System.out.println("加密后:" + text1); + System.out.println("解密后:" + text2); + if (RsaUtils.SRC.equals(text2)) { + System.out.println("解密字符串和原始字符串一致,解密成功"); + } else { + System.out.println("解密字符串和原始字符串不一致,解密失败"); + } + System.out.println("***************** 私钥加密公钥解密结束 *****************"); + } + + /** + * 公钥解密 + * + * @param publicKeyText 公钥 + * @param text 待解密的信息 + * @return / + * @throws Exception / + */ + public static String decryptByPublicKey(String publicKeyText, String text) throws Exception { + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 私钥加密 + * + * @param privateKeyText 私钥 + * @param text 待加密的信息 + * @return / + * @throws Exception / + */ + public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(text.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 私钥解密 + * + * @param privateKeyText 私钥 + * @param text 待解密的文本 + * @return / + * @throws Exception / + */ + public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 公钥加密 + * + * @param publicKeyText 公钥 + * @param text 待加密的文本 + * @return / + */ + public static String encryptByPublicKey(String publicKeyText, String text) throws Exception { + X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] result = cipher.doFinal(text.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 构建RSA密钥对 + * + * @return / + * @throws NoSuchAlgorithmException / + */ + public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); + String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); + return new RsaKeyPair(publicKeyString, privateKeyString); + } + + + /** + * RSA密钥对对象 + */ + public static class RsaKeyPair { + + private final String publicKey; + private final String privateKey; + + public RsaKeyPair(String publicKey, String privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public String getPublicKey() { + return publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + } +} diff --git a/src/main/java/com/yfd/platform/utils/SecurityUtils.java b/src/main/java/com/yfd/platform/utils/SecurityUtils.java new file mode 100644 index 0000000..9df23cd --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/SecurityUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.exception.BadRequestException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; + +import java.util.List; + +/** + * 获取当前登录的用户 + * @author + * @date 2019-01-17 + */ +@Slf4j +public class SecurityUtils { + + /** + * 获取当前登录的用户 + * @return UserDetails + */ + public static UserDetails getCurrentUser() { + UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class); + return userDetailsService.loadUserByUsername(getCurrentUsername()); + } + + /** + * 获取系统用户名称 + * + * @return 系统用户名称 + */ + public static String getCurrentUsername() { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期"); + } + if (authentication.getPrincipal() instanceof UserDetails) { + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + return userDetails.getUsername(); + } + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息"); + } + + /** + * 获取系统用户ID + * @return 系统用户ID + */ + public static Long getCurrentUserId() { + UserDetails userDetails = getCurrentUser(); + return new JSONObject(new JSONObject(userDetails).get("user")).get("id", Long.class); + } + + /** + * 获取当前用户的数据权限 + * @return / + */ + public static List getCurrentUserDataScope(){ + UserDetails userDetails = getCurrentUser(); + JSONArray array = JSONUtil.parseArray(new JSONObject(userDetails).get("dataScopes")); + return JSONUtil.toList(array,Long.class); + } + +} diff --git a/src/main/java/com/yfd/platform/utils/SpringContextHolder.java b/src/main/java/com/yfd/platform/utils/SpringContextHolder.java new file mode 100644 index 0000000..b85c770 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/SpringContextHolder.java @@ -0,0 +1,145 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; + +import java.util.ArrayList; +import java.util.List; +/** + * @author Jie + * @date 2019-01-07 + */ +@Slf4j +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + private static final List CALL_BACKS = new ArrayList<>(); + private static boolean addCallback = true; + + /** + * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。 + * 在SpringContextHolder 初始化后,进行回调使用 + * + * @param callBack 回调函数 + */ + public synchronized static void addCallBacks(CallBack callBack) { + if (addCallback) { + SpringContextHolder.CALL_BACKS.add(callBack); + } else { + log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName()); + callBack.executor(); + } + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(Class requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param defaultValue 默认值 + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, T defaultValue, Class requiredType) { + T result = defaultValue; + try { + result = getBean(Environment.class).getProperty(property, requiredType); + } catch (Exception ignored) {} + return result; + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @return / + */ + public static String getProperties(String property) { + return getProperties(property, null, String.class); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, Class requiredType) { + return getProperties(property, null, requiredType); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + if (applicationContext == null) { + throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); + } + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + private static void clearHolder() { + log.debug("清除SpringContextHolder中的ApplicationContext:" + + applicationContext); + applicationContext = null; + } + + @Override + public void destroy() { + SpringContextHolder.clearHolder(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringContextHolder.applicationContext != null) { + log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); + } + SpringContextHolder.applicationContext = applicationContext; + if (addCallback) { + for (CallBack callBack : SpringContextHolder.CALL_BACKS) { + callBack.executor(); + } + CALL_BACKS.clear(); + } + SpringContextHolder.addCallback = false; + } +} diff --git a/src/main/java/com/yfd/platform/utils/StringUtils.java b/src/main/java/com/yfd/platform/utils/StringUtils.java new file mode 100644 index 0000000..b2a9be7 --- /dev/null +++ b/src/main/java/com/yfd/platform/utils/StringUtils.java @@ -0,0 +1,305 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.constant.Constant; +import lombok.SneakyThrows; +import org.lionsoul.ip2region.DataBlock; +import org.lionsoul.ip2region.DbConfig; +import org.lionsoul.ip2region.DbSearcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import eu.bitwalker.useragentutils.Browser; +import eu.bitwalker.useragentutils.UserAgent; +import jakarta.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; + +/** + * @author + * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + private static final Logger log = LoggerFactory.getLogger(StringUtils.class); + private static boolean ipLocal = false; + private static File file ; + private static DbConfig config; + private static final char SEPARATOR = '_'; + private static final String UNKNOWN = "unknown"; + + static { + SpringContextHolder.addCallBacks(() -> { + StringUtils.ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class); + if (ipLocal) { + /* + * 此文件为独享 ,不必关闭 + */ + String path = "ip2region/ip2region.db"; + String name = "ip2region.db"; + try { + config = new DbConfig(); + file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + }); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCamelCase(String s) { + if (s == null) { + return null; + } + + s = s.toLowerCase(); + + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCapitalizeCamelCase(String s) { + if (s == null) { + return null; + } + s = toCamelCase(s); + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + static String toUnderScoreCase(String s) { + if (s == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + boolean nextUpperCase = true; + + if (i < (s.length() - 1)) { + nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); + } + + if ((i > 0) && Character.isUpperCase(c)) { + if (!upperCase || !nextUpperCase) { + sb.append(SEPARATOR); + } + upperCase = true; + } else { + upperCase = false; + } + + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 获取ip地址 + */ + public static String getIp(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + String comma = ","; + String localhost = "127.0.0.1"; + if (ip.contains(comma)) { + ip = ip.split(",")[0]; + } + if (localhost.equals(ip)) { + // 获取本机真正的ip地址 + try { + ip = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + log.error(e.getMessage(), e); + } + } + return ip; + } + + /** + * 根据ip获取详细地址 + */ + @SneakyThrows + public static String getCityInfo(String ip) { + if (ipLocal) { + return getLocalCityInfo(ip); + } else { + return getHttpCityInfo(ip); + } + } + + /** + * 根据ip获取详细地址 + */ + public static String getHttpCityInfo(String ip) { + String host = "202.108.22.5"; + //超时应该在3钞以上 + int timeOut = 3000; + boolean status = false; + try { + status = InetAddress.getByName(host).isReachable(timeOut); + } catch (IOException e) { + e.printStackTrace(); + } + String api =""; + if (status){ + api = HttpUtil.get(String.format(Constant.Url.IP_URL, ip)); + }else { + api = "{\"ip\":\"127.0.0.1\",\"pro\":\"\",\"proCode\":\"999999\",\"city\":\"\",\"cityCode\":\"0\",\"region\":\"\",\"regionCode\":\"0\",\"addr\":\" 局域网\",\"regionNames\":\"\",\"err\":\"noprovince\"}"; + } + JSONObject object = JSONUtil.parseObj(api); + return object.get("addr", String.class); + } + + + /** + * 根据ip获取详细地址 + */ + public static String getLocalCityInfo(String ip) { + try { + DataBlock dataBlock = new DbSearcher(config, file.getPath()) + .binarySearch(ip); + String region = dataBlock.getRegion(); + String address = region.replace("0|", ""); + char symbol = '|'; + if (address.charAt(address.length() - 1) == symbol) { + address = address.substring(0, address.length() - 1); + } + return address.equals(Constant.REGION) ? "内网IP" : address; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return ""; + } + + public static String getBrowser(HttpServletRequest request) { + UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + Browser browser = userAgent.getBrowser(); + return browser.getName(); + } + + /** + * 获得当天是周几 + */ + public static String getWeekDay() { + String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) { + w = 0; + } + return weekDays[w]; + } + + /** + * 获取当前机器的IP + * + * @return / + */ + public static String getLocalIp() { + try { + InetAddress candidateAddress = null; + // 遍历所有的网络接口 + for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) { + NetworkInterface anInterface = interfaces.nextElement(); + // 在所有的接口下再遍历IP + for (Enumeration inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) { + InetAddress inetAddr = inetAddresses.nextElement(); + // 排除loopback类型地址 + if (!inetAddr.isLoopbackAddress()) { + if (inetAddr.isSiteLocalAddress()) { + // 如果是site-local地址,就是它了 + return inetAddr.getHostAddress(); + } else if (candidateAddress == null) { + // site-local类型的地址未被发现,先记录候选地址 + candidateAddress = inetAddr; + } + } + } + } + if (candidateAddress != null) { + return candidateAddress.getHostAddress(); + } + // 如果没有发现 non-loopback地址.只能用最次选的方案 + InetAddress jdkSuppliedAddress = InetAddress.getLocalHost(); + if (jdkSuppliedAddress == null) { + return ""; + } + return jdkSuppliedAddress.getHostAddress(); + } catch (Exception e) { + return ""; + } + } +} diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml new file mode 100644 index 0000000..a53b797 --- /dev/null +++ b/src/main/resources/all-application.yml @@ -0,0 +1,168 @@ + + + +# 此配置文件只是用作展示所有配置项, 不可不直接使用 + + +spring: + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 6 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: + # [可选] 超时时间 + timeout: 10000 + # [可选] 一个pool最多可分配多少个jedis实例 + poolMaxTotal: 1000 + # [可选] 一个pool最多有多少个状态为idle(空闲)的jedis实例 + poolMaxIdle: 500 + # [可选] 最大的等待时间(秒) + poolMaxWait: 5 + # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 + datasource: + # 使用mysql 打开23-28行注释, 删除29-36行 + name: wvp + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: + password: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + # name: eiot +# url: jdbc:sqlite::resource:wvp.sqlite +# username: +# password: +# type: com.alibaba.druid.pool.DruidDataSource +# driver-class-name: org.sqlite.JDBC + max-active: 1 + min-idle: 1 + +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + # [可选] HTTPS配置, 默认不开启 + ssl: + # [可选] 是否开启HTTPS访问 + enabled: false + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 + key-store: classpath:xxx.jks + # [可选] 证书密码 + key-password: password + # [可选] 证书类型, 默认为jks,根据实际修改 + key-store-type: JKS + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP, 必须是网卡上的IP,用于sip下协议栈监听ip,如果监听所有设置为0.0.0.0 + monitor-ip: 0.0.0.0 + # [必须修改] 本机的IP + ip: 192.168.0.100 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 4401020049 + # [可选] + id: 44010200492000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: admin123 + # [可选] 心跳超时时间, 建议设置为心跳周期的三倍 + keepalive-timeout: 255 + # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 + register-time-interval: 60 + # [可选] 云台控制速度 + ptz-speed: 50 + # TODO [可选] 收到心跳后自动上线, 重启服务后会将所有设备置为离线,默认false,等待注册后上线。设置为true则收到心跳设置为上线。 + # keepalliveToOnline: false + +#zlm 默认服务器配置 +media: + # [可选] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: + # [必须修改] zlm服务器的内网IP + ip: 192.168.0.100 + # [可选] 返回流地址时的ip,置空使用 media.ip + stream-ip: + # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip + sdp-ip: + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip + hook-ip: + # [必须修改] zlm服务器的http.port + http-port: 80 + # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 + http-ssl-port: + # [可选] zlm服务器的rtmp.port, 置空使用zlm配置文件配置 + rtmp-port: + # [可选] zlm服务器的rtmp.sslport, 置空使用zlm配置文件配置 + rtmp-ssl-port: + # [可选] zlm服务器的 rtp_proxy.port, 置空使用zlm配置文件配置 + rtp-proxy-port: + # [可选] zlm服务器的 rtsp.port, 置空使用zlm配置文件配置 + rtsp-port: + # [可选] zlm服务器的 rtsp.sslport, 置空使用zlm配置文件配置 + rtsp-ssl-port: + # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 + auto-config: true + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # [可选] zlm服务器的general.streamNoneReaderDelayMS + stream-none-reader-delay-ms: 18000 # 无人观看多久自动关闭流, -1表示永不自动关闭,即 关闭按需拉流 + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, + port-range: 30000,30500 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 0 + +# [可选] 日志配置, 一般不需要改 +logging: + file: + name: logs/wvp.log + max-history: 30 + max-size: 10MB + total-size-cap: 300MB + level: + com.genersoft.iot: debug + com.genersoft.iot.vmp.storager.dao: info + com.genersoft.iot.vmp.gb28181: info +# [根据业务需求配置] +user-settings: + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: false + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播等待超时时间,单位:毫秒 + play-timeout: 3000 + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 + wait-track: false + # 是否开启接口鉴权 + interface-authentication: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: true + # 是否将日志存储进数据库 + logInDatebase: true + +# 在线文档: swagger-ui(生产环境建议关闭) +swagger-ui: + enabled: true + +# 版本信息, 不需修改 +version: + version: "@project.version@" + description: "@project.description@" + artifact-id: "@project.artifactId@" \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..b814931 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,84 @@ +server: + port: 8093 + +spring: + #应用名称 + application: + name: Project-plateform + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://43.138.168.68:3306/frameworkdb2023?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: root + password: ylfw20230626@ + slave: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://43.138.168.68:3306/frameworkdb2023?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: root + password: ylfw20230626@ + + mvc: + pathmatch: + matching-strategy: ant_path_matcher + servlet: + multipart: + max-file-size: 30MB + max-request-size: 100MB + +logging: + file: + name: logs/projectname.log + level: + com.genersoft.iot: debug + com.genersoft.iot.vmp.storager.dao: info + com.genersoft.iot.vmp.gb28181: info + +# 在线文档: swagger-ui(生产环境建议关闭) +swagger-ui: + enabled: true + + +# 登录相关配置 +login: + # 登录缓存 + cache-enable: true + # 是否限制单用户登录 + single-login: false + # 验证码 + login-code: + # 验证码类型配置 查看 LoginProperties 类 + code-type: arithmetic + # 登录图形验证码有效时间/分钟 + expiration: 2 + # 验证码高度 + width: 111 + # 验证码宽度 + heigth: 36 + # 内容长度 + length: 2 + # 字体名称,为空则使用默认字体 + font-name: + # 字体大小 + font-size: 25 + +# IP 本地解析 +ip: + local-parsing: true + + +file-space: #项目文档空间 + files: D:\demoproject\files\ #单独上传的文件附件 + system: D:\demoproject\system\ #单独上传的文件 + +task: + pool: + # 核心线程池大小 + core-pool-size: 10 + # 最大线程数 + max-pool-size: 30 + # 活跃时间 + keep-alive-seconds: 60 + # 队列容量 + queue-capacity: 50 diff --git a/src/main/resources/application-server.yml b/src/main/resources/application-server.yml new file mode 100644 index 0000000..c5a5b91 --- /dev/null +++ b/src/main/resources/application-server.yml @@ -0,0 +1,43 @@ +server: + port: 8090 + +spring: + #应用名称 + application: + name: Project-plateform + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://43.138.168.68:3306/framework2023?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: root + password: zhengg7QkXa + + + + + + + + + + UTF-8 + ${LOG_PATTERN} + + + + + + ${LOG_PATH}/${LOG_FILE}.log + + UTF-8 + ${LOG_PATTERN} + + + ${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}.log + 30 + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..c949c89 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,45 @@ + + + yfAdmin + + + + + + + ${log.pattern} + ${log.charset} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/system/DictionaryMapper.xml b/src/main/resources/mapper/system/DictionaryMapper.xml new file mode 100644 index 0000000..16edd1e --- /dev/null +++ b/src/main/resources/mapper/system/DictionaryMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/MessageMapper.xml b/src/main/resources/mapper/system/MessageMapper.xml new file mode 100644 index 0000000..96cbcfc --- /dev/null +++ b/src/main/resources/mapper/system/MessageMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/Model3dMapper.xml b/src/main/resources/mapper/system/Model3dMapper.xml new file mode 100644 index 0000000..d935f68 --- /dev/null +++ b/src/main/resources/mapper/system/Model3dMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/QuartzJobMapper.xml b/src/main/resources/mapper/system/QuartzJobMapper.xml new file mode 100644 index 0000000..b523b0a --- /dev/null +++ b/src/main/resources/mapper/system/QuartzJobMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysConfigMapper.xml b/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..cf22aa4 --- /dev/null +++ b/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysDictionaryItemsMapper.xml b/src/main/resources/mapper/system/SysDictionaryItemsMapper.xml new file mode 100644 index 0000000..1bf0942 --- /dev/null +++ b/src/main/resources/mapper/system/SysDictionaryItemsMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysDictionaryMapper.xml b/src/main/resources/mapper/system/SysDictionaryMapper.xml new file mode 100644 index 0000000..6963e40 --- /dev/null +++ b/src/main/resources/mapper/system/SysDictionaryMapper.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/src/main/resources/mapper/system/SysLogMapper.xml b/src/main/resources/mapper/system/SysLogMapper.xml new file mode 100644 index 0000000..1046de9 --- /dev/null +++ b/src/main/resources/mapper/system/SysLogMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysMenuMapper.xml b/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..febfab3 --- /dev/null +++ b/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,103 @@ + + + + + + update sys_menu set orderno=orderno+1 where parentid=#{parentid} and orderno < #{Orderno} and orderno >= #{upOrderno} + + + + + update sys_menu SET orderno=orderno-1 where parentid=#{parentid} and orderno > #{Orderno} and orderno <= #{downOrderno} + + + + + + + + + + diff --git a/src/main/resources/mapper/system/SysMessageMapper.xml b/src/main/resources/mapper/system/SysMessageMapper.xml new file mode 100644 index 0000000..cda7c6f --- /dev/null +++ b/src/main/resources/mapper/system/SysMessageMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysOrganizationMapper.xml b/src/main/resources/mapper/system/SysOrganizationMapper.xml new file mode 100644 index 0000000..053c5ae --- /dev/null +++ b/src/main/resources/mapper/system/SysOrganizationMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/main/resources/mapper/system/SysQuartzJobMapper.xml b/src/main/resources/mapper/system/SysQuartzJobMapper.xml new file mode 100644 index 0000000..268cb3d --- /dev/null +++ b/src/main/resources/mapper/system/SysQuartzJobMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysRoleMapper.xml b/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..4b37516 --- /dev/null +++ b/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,116 @@ + + + + + INSERT INTO sys_role_menu + + + id, + + + roleid, + + + menuid + + + + + #{id}, + + + #{roleid}, + + + #{menuid} + + + + + + + + + + + + + + + + + + + + delete from sys_role_users where userid !=(select u.id from sys_user u where u.account="admin") and roleid=#{roleid} and userid=#{urserid} + + + + + DELETE FROM sys_role_menu WHERE roleid= #{id} + + + + + DELETE FROM sys_role_users WHERE roleid= #{id} + + diff --git a/src/main/resources/mapper/system/SysUserMapper.xml b/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..f89b6dc --- /dev/null +++ b/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + insert into sys_role_users value (#{id},#{roleid},#{userid}) + + + + + + + + + + + + + + + + + + delete from sys_role_users where userid=#{userid} + + + + delete from sys_role_users + where + userid=#{userid} + and roleid not in + + #{roleids} + + + + DELETE FROM sys_role_users WHERE userid IN + + #{id} + + + + diff --git a/src/main/resources/quartz.properties b/src/main/resources/quartz.properties new file mode 100644 index 0000000..03d3988 --- /dev/null +++ b/src/main/resources/quartz.properties @@ -0,0 +1,21 @@ +######################################## +# Quartz 默认配置示例(RAMJobStore) +######################################## + +org.quartz.scheduler.instanceName = PlatformScheduler +org.quartz.scheduler.instanceId = AUTO + +# 线程池配置 +org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount = 10 +org.quartz.threadPool.threadPriority = 5 + +# 使用内存存储(如需持久化请改为 JobStoreTX 并配置数据源) +org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore + +# Misfire 阈值 +org.quartz.jobStore.misfireThreshold = 60000 + +# 插件(可选):关闭时清理 +org.quartz.plugin.shutdownHook.class = org.quartz.plugins.management.ShutdownHookPlugin +org.quartz.plugin.shutdownHook.cleanShutdown = true \ No newline at end of file diff --git a/src/main/resources/static/assets/401-485a4475.js b/src/main/resources/static/assets/401-485a4475.js new file mode 100644 index 0000000..390a98a --- /dev/null +++ b/src/main/resources/static/assets/401-485a4475.js @@ -0,0 +1 @@ +import{d as v,aa as x,ab as V,c as C,f as t,w as o,a as i,S as y,i as a,M as I,o as R,j as _,h as e,W as S,J as z,K as B,_ as G}from"./index-5c62e6c4.js";const s=l=>(z("data-v-25007613"),l=l(),B(),l),N={class:"errPage-container"},j=s(()=>e("h1",{class:"text-jumbo text-ginormous"},"Oops!",-1)),M=s(()=>e("a",{href:"https://zh.airbnb.com/",target:"_blank"},"airbnb",-1)),P=s(()=>e("h2",null,"你没有权限去该页面",-1)),U=s(()=>e("h6",null,"如有不满请联系你领导",-1)),E={class:"list-unstyled"},J=s(()=>e("li",null,"或者你可以去:",-1)),K={class:"link-type"},L=s(()=>e("li",{class:"link-type"},[e("a",{href:"https://www.taobao.com/"},"随便看看")],-1)),O=["src"],T=["src"],W={name:"Page401"},$=v({...W,setup(l){const u=x({errGif:new URL("/assets/401-a61ddb94.gif",self.location).href,ewizardClap:"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",dialogVisible:!1}),{errGif:p,ewizardClap:f,dialogVisible:n}=V(u),h=I();function m(){h.back()}return(q,c)=>{const b=a("el-button"),g=a("router-link"),r=a("el-col"),w=a("el-row"),k=a("el-dialog");return R(),C("div",N,[t(b,{icon:"el-icon-arrow-left",class:"pan-back-btn",onClick:m},{default:o(()=>[_(" 返回 ")]),_:1}),t(w,null,{default:o(()=>[t(r,{span:12},{default:o(()=>[j,_(" gif来源"),M,_(" 页面 "),P,U,e("ul",E,[J,e("li",K,[t(g,{to:"/dashboard"},{default:o(()=>[_(" 回首页 ")]),_:1})]),L,e("li",null,[e("a",{href:"#",onClick:c[0]||(c[0]=S(d=>n.value=!0,["prevent"]))},"点我看图")])])]),_:1}),t(r,{span:12},{default:o(()=>[e("img",{src:i(p),width:"313",height:"428",alt:"Girl has dropped her ice cream."},null,8,O)]),_:1})]),_:1}),t(k,{modelValue:i(n),"onUpdate:modelValue":c[1]||(c[1]=d=>y(n)?n.value=d:null),title:"随便看"},{default:o(()=>[e("img",{src:i(f),class:"pan-img"},null,8,T)]),_:1},8,["modelValue"])])}}});const D=G($,[["__scopeId","data-v-25007613"]]);export{D as default}; diff --git a/src/main/resources/static/assets/404-ae343fa7.js b/src/main/resources/static/assets/404-ae343fa7.js new file mode 100644 index 0000000..51a23cb --- /dev/null +++ b/src/main/resources/static/assets/404-ae343fa7.js @@ -0,0 +1 @@ +import{d as o,c as _,h as t,t as i,ag as l,J as n,K as d,j as r,o as h,_ as p}from"./index-5c62e6c4.js";const f="/assets/404-538aa4d7.png",a="/assets/404_cloud-98e7ac66.png",e=s=>(n("data-v-ec8f1f5a"),s=s(),d(),s),u={class:"wscn-http404-container"},m=l('
404404404404
',1),v=e(()=>t("div",{class:"bullshit__oops"},"OOPS!",-1)),g=e(()=>t("div",{class:"bullshit__info"},[r(" All rights reserved "),t("a",{style:{color:"#20a0ff"},href:"https://wallstreetcn.com",target:"_blank"},"wallstreetcn")],-1)),b=e(()=>t("div",{class:"bullshit__info"}," Please check that the URL you entered is correct, or click the button below to return to the homepage. ",-1)),w=e(()=>t("a",{href:"",class:"bullshit__return-home"},"Back to home",-1)),k={name:"Page404"},y=o({...k,setup(s){function c(){return"The webmaster said that you can not enter this page..."}return(S,x)=>(h(),_("div",u,[t("div",{class:"wscn-http404"},[m,t("div",{class:"bullshit"},[v,g,t("div",{class:"bullshit__headline"},i(c)),b,w])])]))}});const I=p(y,[["__scopeId","data-v-ec8f1f5a"]]);export{I as default}; diff --git a/src/main/resources/static/assets/BarChart-a4765ae3.js b/src/main/resources/static/assets/BarChart-a4765ae3.js new file mode 100644 index 0000000..95b45f0 --- /dev/null +++ b/src/main/resources/static/assets/BarChart-a4765ae3.js @@ -0,0 +1 @@ +import{r as c,i as f,L as a}from"./resize-9f0962b6.js";import{d as m,I as y,aq as u,ar as x,k as h,a4 as p,o as g,c as b,X as S,p as v}from"./index-5c62e6c4.js";const w=["id"],L=m({__name:"BarChart",props:{id:{type:String,default:"barChart"},className:{type:String,default:""},width:{type:String,default:"200px",required:!0},height:{type:String,default:"200px",required:!0}},setup(e){const o=e,{mounted:i,chart:r,beforeDestroy:n,activated:l,deactivated:s}=c();function d(){const t=f(document.getElementById(o.id));t.setOption({title:{show:!0,text:"业绩总览",x:"center",padding:15,textStyle:{fontSize:18,fontStyle:"normal",fontWeight:"bold",color:"#337ecc"}},grid:{left:"2%",right:"2%",bottom:"10%",containLabel:!0},tooltip:{trigger:"axis",axisPointer:{type:"cross",crossStyle:{color:"#999"}}},legend:{x:"center",y:"bottom",data:["收入","毛利润","收入增长率","利润增长率"]},xAxis:[{type:"category",data:["浙江","北京","上海","广东","深圳"],axisPointer:{type:"shadow"}}],yAxis:[{type:"value",min:0,max:1e4,interval:2e3,axisLabel:{formatter:"{value} "}},{type:"value",min:0,max:100,interval:20,axisLabel:{formatter:"{value}%"}}],series:[{name:"收入",type:"bar",data:[7e3,7100,7200,7300,7400],barWidth:20,itemStyle:{color:new a(0,0,0,1,[{offset:0,color:"#83bff6"},{offset:.5,color:"#188df0"},{offset:1,color:"#188df0"}])}},{name:"毛利润",type:"bar",data:[8e3,8200,8400,8600,8800],barWidth:20,itemStyle:{color:new a(0,0,0,1,[{offset:0,color:"#25d73c"},{offset:.5,color:"#1bc23d"},{offset:1,color:"#179e61"}])}},{name:"收入增长率",type:"line",yAxisIndex:1,data:[60,65,70,75,80],itemStyle:{color:"#67C23A"}},{name:"利润增长率",type:"line",yAxisIndex:1,data:[70,75,80,85,90],itemStyle:{color:"#409EFF"}}]}),r.value=t}return y(()=>{n()}),u(()=>{l()}),x(()=>{s()}),h(()=>{i(),p(()=>{d()})}),(t,C)=>(g(),b("div",{id:e.id,class:S(e.className),style:v({height:e.height,width:e.width})},null,14,w))}});export{L as default}; diff --git a/src/main/resources/static/assets/FunnelChart-8e41d306.js b/src/main/resources/static/assets/FunnelChart-8e41d306.js new file mode 100644 index 0000000..b4f80e1 --- /dev/null +++ b/src/main/resources/static/assets/FunnelChart-8e41d306.js @@ -0,0 +1 @@ +import{r as s,i as u}from"./resize-9f0962b6.js";import{d as c,I as m,aq as h,ar as f,k as p,a4 as y,o as g,c as S,X as b,p as v}from"./index-5c62e6c4.js";const w=["id"],q=c({__name:"FunnelChart",props:{id:{type:String,default:"funnelChart"},className:{type:String,default:""},width:{type:String,default:"200px",required:!0},height:{type:String,default:"200px",required:!0}},setup(e){const n=e,{mounted:a,chart:i,beforeDestroy:o,activated:l,deactivated:r}=s();function d(){const t=u(document.getElementById(n.id));t.setOption({title:{show:!0,text:"订单线索转化漏斗图",x:"center",padding:15,textStyle:{fontSize:18,fontStyle:"normal",fontWeight:"bold",color:"#337ecc"}},grid:{left:"2%",right:"2%",bottom:"10%",containLabel:!0},legend:{x:"center",y:"bottom",data:["Show","Click","Visit","Inquiry","Order"]},series:[{name:"Funnel",type:"funnel",left:"20%",top:60,bottom:60,width:"60%",sort:"descending",gap:2,label:{show:!0,position:"inside"},labelLine:{length:10,lineStyle:{width:1,type:"solid"}},itemStyle:{borderColor:"#fff",borderWidth:1},emphasis:{label:{fontSize:20}},data:[{value:60,name:"Visit"},{value:40,name:"Inquiry"},{value:20,name:"Order"},{value:80,name:"Click"},{value:100,name:"Show"}]}]}),i.value=t}return m(()=>{o()}),h(()=>{l()}),f(()=>{r()}),p(()=>{a(),y(()=>{d()})}),(t,x)=>(g(),S("div",{id:e.id,class:b(e.className),style:v({height:e.height,width:e.width})},null,14,w))}});export{q as default}; diff --git a/src/main/resources/static/assets/PieChart-f0d9d351.js b/src/main/resources/static/assets/PieChart-f0d9d351.js new file mode 100644 index 0000000..8f6ea0a --- /dev/null +++ b/src/main/resources/static/assets/PieChart-f0d9d351.js @@ -0,0 +1 @@ +import{r as c,i as u}from"./resize-9f0962b6.js";import{d as m,I as h,aq as f,ar as p,k as g,a4 as y,o as C,c as v,X as x,p as S}from"./index-5c62e6c4.js";const b=["id"],B=m({__name:"PieChart",props:{id:{type:String,default:"pieChart"},className:{type:String,default:""},width:{type:String,default:"200px",required:!0},height:{type:String,default:"200px",required:!0}},setup(e){const i=e,{mounted:n,chart:o,beforeDestroy:r,activated:s,deactivated:d}=c();function l(){const t=u(document.getElementById(i.id));t.setOption({title:{show:!0,text:"产品分类总览",x:"center",padding:15,textStyle:{fontSize:18,fontStyle:"normal",fontWeight:"bold",color:"#337ecc"}},grid:{left:"2%",right:"2%",bottom:"10%",containLabel:!0},legend:{top:"bottom"},series:[{name:"Nightingale Chart",type:"pie",radius:[50,130],center:["50%","50%"],roseType:"area",itemStyle:{borderRadius:1,color:function(a){return["#409EFF","#67C23A","#E6A23C","#F56C6C"][a.dataIndex]}},data:[{value:26,name:"家用电器"},{value:27,name:"户外运动"},{value:24,name:"汽车用品"},{value:23,name:"手机数码"}]}]}),o.value=t}return h(()=>{r()}),f(()=>{s()}),p(()=>{d()}),g(()=>{n(),y(()=>{l()})}),(t,a)=>(C(),v("div",{id:e.id,class:x(e.className),style:S({height:e.height,width:e.width})},null,14,b))}});export{B as default}; diff --git a/src/main/resources/static/assets/RadarChart-94b1112a.js b/src/main/resources/static/assets/RadarChart-94b1112a.js new file mode 100644 index 0000000..3a44423 --- /dev/null +++ b/src/main/resources/static/assets/RadarChart-94b1112a.js @@ -0,0 +1 @@ +import{r as l,i as m}from"./resize-9f0962b6.js";import{d as u,I as h,aq as f,ar as g,k as p,a4 as y,o as v,c as x,X as C,p as S}from"./index-5c62e6c4.js";const b=["id"],z=u({__name:"RadarChart",props:{id:{type:String,default:"radarChart"},className:{type:String,default:""},width:{type:String,default:"200px",required:!0},height:{type:String,default:"200px",required:!0}},setup(e){const n=e,{mounted:r,chart:i,beforeDestroy:o,activated:d,deactivated:s}=l();function c(){const t=m(document.getElementById(n.id));t.setOption({title:{show:!0,text:"订单状态统计",x:"center",padding:15,textStyle:{fontSize:18,fontStyle:"normal",fontWeight:"bold",color:"#337ecc"}},grid:{left:"2%",right:"2%",bottom:"10%",containLabel:!0},legend:{x:"center",y:"bottom",data:["预定数量","下单数量","发货数量"]},radar:{radius:"60%",indicator:[{name:"家用电器"},{name:"服装箱包"},{name:"运动户外"},{name:"手机数码"},{name:"汽车用品"},{name:"家具厨具"}]},series:[{name:"Budget vs spending",type:"radar",itemStyle:{borderRadius:6,color:function(a){return["#409EFF","#67C23A","#E6A23C","#F56C6C"][a.dataIndex]}},data:[{value:[400,400,400,400,400,400],name:"预定数量"},{value:[300,300,300,300,300,300],name:"下单数量"},{value:[200,200,200,200,200,200],name:"发货数量"}]}]}),i.value=t}return h(()=>{o()}),f(()=>{d()}),g(()=>{s()}),p(()=>{r(),y(()=>{c()})}),(t,a)=>(v(),x("div",{id:e.id,class:C(e.className),style:S({height:e.height,width:e.width})},null,14,b))}});export{z as default}; diff --git a/src/main/resources/static/assets/editor-ec3491e5.js b/src/main/resources/static/assets/editor-ec3491e5.js new file mode 100644 index 0000000..78d9f56 --- /dev/null +++ b/src/main/resources/static/assets/editor-ec3491e5.js @@ -0,0 +1,186 @@ +import{d as N1,r as Eg,am as lP,k as u$,a3 as s$,a9 as l$,an as c$,o as I1,c as L1,aa as cP,ab as fP,I as f$,f as Dg,a as br,S as d$,ae as yx,w as p$}from"./index-5c62e6c4.js";import{u as h$}from"./index-f88fe59a.js";var se=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function g$(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function us(t){var e={exports:{}};return t(e,e.exports),e.exports}var yi,$0,Fh=function(t){return t&&t.Math==Math&&t},kt=Fh(typeof globalThis=="object"&&globalThis)||Fh(typeof window=="object"&&window)||Fh(typeof self=="object"&&self)||Fh(typeof se=="object"&&se)||function(){return this}()||Function("return this")(),r5=Function.prototype,bx=r5.apply,v$=r5.bind,wx=r5.call,dP=typeof Reflect=="object"&&Reflect.apply||(v$?wx.bind(bx):function(){return wx.apply(bx,arguments)}),pP=Function.prototype,w4=pP.bind,E4=pP.call,m$=w4&&w4.bind(E4),ge=w4?function(t){return t&&m$(E4,t)}:function(t){return t&&function(){return E4.apply(t,arguments)}},sn=function(t){return typeof t=="function"},Gn=function(t){try{return!!t()}catch{return!0}},Hn=!Gn(function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!=7}),kc=Function.prototype.call,zn=kc.bind?kc.bind(kc):function(){return kc.apply(kc,arguments)},Ex={}.propertyIsEnumerable,Dx=Object.getOwnPropertyDescriptor,y$=Dx&&!Ex.call({1:2},1)?function(t){var e=Dx(this,t);return!!e&&e.enumerable}:Ex,o5={f:y$},Xr=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},b$=ge({}.toString),w$=ge("".slice),Du=function(t){return w$(b$(t),8,-1)},qm=kt.Object,E$=ge("".split),hP=Gn(function(){return!qm("z").propertyIsEnumerable(0)})?function(t){return Du(t)=="String"?E$(t,""):qm(t)}:qm,D$=kt.TypeError,i5=function(t){if(t==null)throw D$("Can't call method on "+t);return t},jo=function(t){return hP(i5(t))},tr=function(t){return typeof t=="object"?t!==null:sn(t)},Qn={},Cx=function(t){return sn(t)?t:void 0},oc=function(t,e){return arguments.length<2?Cx(Qn[t])||Cx(kt[t]):Qn[t]&&Qn[t][e]||kt[t]&&kt[t][e]},Td=ge({}.isPrototypeOf),Km=oc("navigator","userAgent")||"",xx=kt.process,Sx=kt.Deno,Ax=xx&&xx.versions||Sx&&Sx.version,Ox=Ax&&Ax.v8;Ox&&($0=(yi=Ox.split("."))[0]>0&&yi[0]<4?1:+(yi[0]+yi[1])),!$0&&Km&&(!(yi=Km.match(/Edge\/(\d+)/))||yi[1]>=74)&&(yi=Km.match(/Chrome\/(\d+)/))&&($0=+yi[1]);var _h,Cg=$0,ho=!!Object.getOwnPropertySymbols&&!Gn(function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&Cg&&Cg<41}),a5=ho&&!Symbol.sham&&typeof Symbol.iterator=="symbol",C$=kt.Object,hl=a5?function(t){return typeof t=="symbol"}:function(t){var e=oc("Symbol");return sn(e)&&Td(e.prototype,C$(t))},x$=kt.String,D4=function(t){try{return x$(t)}catch{return"Object"}},S$=kt.TypeError,u5=function(t){if(sn(t))return t;throw S$(D4(t)+" is not a function")},xg=function(t,e){var n=t[e];return n==null?void 0:u5(n)},A$=kt.TypeError,O$=Object.defineProperty,ea=kt["__core-js_shared__"]||function(t,e){try{O$(kt,t,{value:e,configurable:!0,writable:!0})}catch{kt[t]=e}return e}("__core-js_shared__",{}),ss=us(function(t){(t.exports=function(e,n){return ea[e]||(ea[e]=n!==void 0?n:{})})("versions",[]).push({version:"3.19.3",mode:"pure",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})}),k$=kt.Object,Fp=function(t){return k$(i5(t))},B$=ge({}.hasOwnProperty),Vt=Object.hasOwn||function(t,e){return B$(Fp(t),e)},F$=0,_$=Math.random(),T$=ge(1 .toString),Sg=function(t){return"Symbol("+(t===void 0?"":t)+")_"+T$(++F$+_$,36)},Bc=ss("wks"),Cu=kt.Symbol,kx=Cu&&Cu.for,P$=a5?Cu:Cu&&Cu.withoutSetter||Sg,Bn=function(t){if(!Vt(Bc,t)||!ho&&typeof Bc[t]!="string"){var e="Symbol."+t;ho&&Vt(Cu,t)?Bc[t]=Cu[t]:Bc[t]=a5&&kx?kx(e):P$(e)}return Bc[t]},j$=kt.TypeError,N$=Bn("toPrimitive"),I$=function(t,e){if(!tr(t)||hl(t))return t;var n,r=xg(t,N$);if(r){if(e===void 0&&(e="default"),n=zn(r,t,e),!tr(n)||hl(n))return n;throw j$("Can't convert object to primitive value")}return e===void 0&&(e="number"),function(o,i){var a,u;if(i==="string"&&sn(a=o.toString)&&!tr(u=zn(a,o))||sn(a=o.valueOf)&&!tr(u=zn(a,o))||i!=="string"&&sn(a=o.toString)&&!tr(u=zn(a,o)))return u;throw A$("Can't convert object to primitive value")}(t,e)},ic=function(t){var e=I$(t,"string");return hl(e)?e:e+""},C4=kt.document,L$=tr(C4)&&tr(C4.createElement),gP=function(t){return L$?C4.createElement(t):{}},vP=!Hn&&!Gn(function(){return Object.defineProperty(gP("div"),"a",{get:function(){return 7}}).a!=7}),Bx=Object.getOwnPropertyDescriptor,R$=Hn?Bx:function(t,e){if(t=jo(t),e=ic(e),vP)try{return Bx(t,e)}catch{}if(Vt(t,e))return Xr(!zn(o5.f,t,e),t[e])},R1={f:R$},M$=/#|\.prototype\./,_p=function(t,e){var n=$$[z$(t)];return n==V$||n!=H$&&(sn(e)?Gn(e):!!e)},z$=_p.normalize=function(t){return String(t).replace(M$,".").toLowerCase()},$$=_p.data={},H$=_p.NATIVE="N",V$=_p.POLYFILL="P",U$=_p,Fx=ge(ge.bind),s5=function(t,e){return u5(t),e===void 0?t:Fx?Fx(t,e):function(){return t.apply(e,arguments)}},W$=kt.String,G$=kt.TypeError,ar=function(t){if(tr(t))return t;throw G$(W$(t)+" is not an object")},q$=kt.TypeError,_x=Object.defineProperty,K$=Hn?_x:function(t,e,n){if(ar(t),e=ic(e),ar(n),vP)try{return _x(t,e,n)}catch{}if("get"in n||"set"in n)throw q$("Accessors not supported");return"value"in n&&(t[e]=n.value),t},Na={f:K$},Tn=Hn?function(t,e,n){return Na.f(t,e,Xr(1,n))}:function(t,e,n){return t[e]=n,t},Y$=R1.f,X$=function(t){var e=function(n,r,o){if(this instanceof e){switch(arguments.length){case 0:return new t;case 1:return new t(n);case 2:return new t(n,r)}return new t(n,r,o)}return dP(t,this,arguments)};return e.prototype=t.prototype,e},Ko=function(t,e){var n,r,o,i,a,u,s,l,c=t.target,f=t.global,p=t.stat,d=t.proto,m=f?kt:p?kt[c]:(kt[c]||{}).prototype,g=f?Qn:Qn[c]||Tn(Qn,c,{})[c],v=g.prototype;for(o in e)n=!U$(f?o:c+(p?".":"#")+o,t.forced)&&m&&Vt(m,o),a=g[o],n&&(u=t.noTargetGet?(l=Y$(m,o))&&l.value:m[o]),i=n&&u?u:e[o],n&&typeof a==typeof i||(s=t.bind&&n?s5(i,kt):t.wrap&&n?X$(i):d&&sn(i)?ge(i):i,(t.sham||i&&i.sham||a&&a.sham)&&Tn(s,"sham",!0),Tn(g,o,s),d&&(Vt(Qn,r=c+"Prototype")||Tn(Qn,r,{}),Tn(Qn[r],o,i),t.real&&v&&!v[o]&&Tn(v,o,i)))},Tx=ss("keys"),M1=function(t){return Tx[t]||(Tx[t]=Sg(t))},Z$=!Gn(function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype}),Px=M1("IE_PROTO"),x4=kt.Object,J$=x4.prototype,Ag=Z$?x4.getPrototypeOf:function(t){var e=Fp(t);if(Vt(e,Px))return e[Px];var n=e.constructor;return sn(n)&&e instanceof n?n.prototype:e instanceof x4?J$:null},Q$=kt.String,tH=kt.TypeError,Og=Object.setPrototypeOf||("__proto__"in{}?function(){var t,e=!1,n={};try{(t=ge(Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set))(n,[]),e=n instanceof Array}catch{}return function(r,o){return ar(r),function(i){if(typeof i=="object"||sn(i))return i;throw tH("Can't set "+Q$(i)+" as a prototype")}(o),e?t(r,o):r.__proto__=o,r}}():void 0),eH=Math.ceil,nH=Math.floor,l5=function(t){var e=+t;return e!=e||e===0?0:(e>0?nH:eH)(e)},rH=Math.max,oH=Math.min,S4=function(t,e){var n=l5(t);return n<0?rH(n+e,0):oH(n,e)},iH=Math.min,Tp=function(t){return(e=t.length)>0?iH(l5(e),9007199254740991):0;var e},jx=function(t){return function(e,n,r){var o,i=jo(e),a=Tp(i),u=S4(r,a);if(t&&n!=n){for(;a>u;)if((o=i[u++])!=o)return!0}else for(;a>u;u++)if((t||u in i)&&i[u]===n)return t||u||0;return!t&&-1}},aH={includes:jx(!0),indexOf:jx(!1)},Pp={},uH=aH.indexOf,Nx=ge([].push),mP=function(t,e){var n,r=jo(t),o=0,i=[];for(n in r)!Vt(Pp,n)&&Vt(r,n)&&Nx(i,n);for(;e.length>o;)Vt(r,n=e[o++])&&(~uH(i,n)||Nx(i,n));return i},kg=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],sH=kg.concat("length","prototype"),lH=Object.getOwnPropertyNames||function(t){return mP(t,sH)},c5={f:lH},H0={f:Object.getOwnPropertySymbols},cH=ge([].concat),fH=oc("Reflect","ownKeys")||function(t){var e=c5.f(ar(t)),n=H0.f;return n?cH(e,n(t)):e},f5=Object.keys||function(t){return mP(t,kg)},dH=Hn?Object.defineProperties:function(t,e){ar(t);for(var n,r=jo(e),o=f5(e),i=o.length,a=0;i>a;)Na.f(t,n=o[a++],r[n]);return t},pH=oc("document","documentElement"),yP=M1("IE_PROTO"),Ym=function(){},bP=function(t){return"